diff --git a/lib/cask/audit.rb b/lib/cask/audit.rb index f961c7e5e..2f5f96146 100644 --- a/lib/cask/audit.rb +++ b/lib/cask/audit.rb @@ -12,11 +12,6 @@ class Cask::Audit def run! _check_required_fields return if errors? - _get_data_from_request - return if errors? - _check_response_status - return if errors? - _check_content_length end def add_error(message) @@ -65,62 +60,4 @@ class Cask::Audit add_error "homepage is required" unless cask.homepage end - http_responses = [ - 'HTTP/1.0 200 OK', - 'HTTP/1.1 200 OK' - ] - - OK_RESPONSES = { - 'http' => http_responses, - 'https' => http_responses, - 'ftp' => [ 'OK' ] - } - - def _check_response_status - ok = OK_RESPONSES[cask.url.scheme] - unless ok.include?(@response_status) - add_error "unexpected http response, expecting #{ok.map(&:inspect).join(' or ')}, got #{@response_status.inspect}" - end - end - - def _check_content_length - remote_content_length = @headers['Content-Length'] - if cask.content_length.nil? - add_warning "specify content_length so we can check against URL, currently: content_length '#{remote_content_length}'" - else - unless cask.content_length == remote_content_length - add_warning "unexpected content_length for #{cask}; specified #{cask.content_length.inspect}, but got #{remote_content_length.inspect}" - end - end - end - - def _get_data_from_request - response = @fetcher.head(cask.url) - - if response.empty? - add_error "timeout while requesting #{cask.url}" - return - end - - response_lines = response.split("\n").map(&:chomp) - - case cask.url.scheme - when 'http', 'https' then - @response_status = response_lines.grep(/^HTTP/).last - http_headers = response_lines[(response_lines.index(@response_status)+1)..-1] - http_headers.each { |line| - header_name, header_value = line.split(': ') - @headers[header_name] = header_value - } - when 'ftp' then - @response_status = 'OK' - response_lines.each { |line| - header_name, header_value = line.split(': ') - @headers[header_name] = header_value - } - else - add_error "unknown scheme for #{cask.url}" - end - end - end diff --git a/lib/cask/cli/checklinks.rb b/lib/cask/cli/checklinks.rb new file mode 100644 index 000000000..ef72936af --- /dev/null +++ b/lib/cask/cli/checklinks.rb @@ -0,0 +1,14 @@ +class Cask::CLI::Checklinks + def self.run(*args) + casks_to_check = args.empty? ? Cask.all : args.map { |arg| Cask.load(arg) } + casks_to_check.each do |cask| + checker = Cask::LinkChecker.new(cask) + checker.run + puts checker.summary + end + end + + def self.help + "checks for bad cask links" + end +end diff --git a/lib/cask/link_checker.rb b/lib/cask/link_checker.rb new file mode 100644 index 000000000..99e5da658 --- /dev/null +++ b/lib/cask/link_checker.rb @@ -0,0 +1,104 @@ +class Cask::LinkChecker + attr_accessor :cask, :errors, :response_status, :headers + + def initialize(cask, fetcher=Cask::Fetcher) + @cask = cask + @errors = [] + @warnings = [] + @headers = {} + @fetcher = fetcher + end + + def run + _get_data_from_request + return if errors? + _check_response_status + end + + def add_error(message) + @errors << message + end + + def add_warning(message) + @warnings << message + end + + def errors? + !@errors.empty? + end + + def warnings? + !@warnings.empty? + end + + def result + if errors? + "#{Tty.red}failed#{Tty.reset}" + elsif warnings? + "#{Tty.yellow}warning#{Tty.reset}" + else + "#{Tty.green}passed#{Tty.reset}" + end + end + + def summary + summary = ["audit for #{cask}: #{result}"] + + @errors.each do |error| + summary << " #{Tty.red}-#{Tty.reset} #{error}" + end + + @warnings.each do |warning| + summary << " #{Tty.yellow}-#{Tty.reset} #{warning}" + end + + summary.join("\n") + end + + HTTP_RESPONSES = [ + 'HTTP/1.0 200 OK', + 'HTTP/1.1 200 OK' + ] + + OK_RESPONSES = { + 'http' => HTTP_RESPONSES, + 'https' => HTTP_RESPONSES, + 'ftp' => [ 'OK' ] + } + + def _check_response_status + ok = OK_RESPONSES[cask.url.scheme] + unless ok.include?(@response_status) + add_error "unexpected http response, expecting #{ok.map(&:inspect).join(' or ')}, got #{@response_status.inspect}" + end + end + + def _get_data_from_request + response = @fetcher.head(cask.url) + + if response.empty? + add_error "timeout while requesting #{cask.url}" + return + end + + response_lines = response.split("\n").map(&:chomp) + + case cask.url.scheme + when 'http', 'https' then + @response_status = response_lines.grep(/^HTTP/).last + http_headers = response_lines[(response_lines.index(@response_status)+1)..-1] + http_headers.each { |line| + header_name, header_value = line.split(': ') + @headers[header_name] = header_value + } + when 'ftp' then + @response_status = 'OK' + response_lines.each { |line| + header_name, header_value = line.split(': ') + @headers[header_name] = header_value + } + else + add_error "unknown scheme for #{cask.url}" + end + end +end diff --git a/test/cask/audit_test.rb b/test/cask/audit_test.rb index d9d44b889..a476837d6 100644 --- a/test/cask/audit_test.rb +++ b/test/cask/audit_test.rb @@ -57,34 +57,5 @@ describe Cask::Audit do end end - describe "request processing" do - it "adds an error if response is empty" do - cask = TestHelper.test_cask - TestHelper.fake_response_for(cask.url, "") - audit = Cask::Audit.new(cask, TestHelper.fake_fetcher) - audit.run! - audit.errors.must_include "timeout while requesting #{cask.url}" - end - - it "properly populates the response code and headers from an http response" do - TestHelper.fake_response_for(TestHelper.test_cask.url, <<-RESPONSE.gsub(/^ /, '')) - HTTP/1.1 200 OK - Content-Type: application/x-apple-diskimage - ETag: "b4208f3e84967be4b078ecaa03fba941" - Content-Length: 23726161 - Last-Modified: Sun, 12 Aug 2012 21:17:21 GMT - RESPONSE - - audit = Cask::Audit.new(TestHelper.test_cask, TestHelper.fake_fetcher) - audit.run! - audit.response_status.must_equal 'HTTP/1.1 200 OK' - audit.headers.must_equal({ - 'Content-Type' => 'application/x-apple-diskimage', - 'ETag' => '"b4208f3e84967be4b078ecaa03fba941"', - 'Content-Length' => '23726161', - 'Last-Modified' => 'Sun, 12 Aug 2012 21:17:21 GMT' - }) - end - end end end diff --git a/test/cask/link_checker_spec.rb b/test/cask/link_checker_spec.rb new file mode 100644 index 000000000..701e716ad --- /dev/null +++ b/test/cask/link_checker_spec.rb @@ -0,0 +1,33 @@ +require 'test_helper' + +describe Cask::Installer do + describe "request processing" do + it "adds an error if response is empty" do + cask = TestHelper.test_cask + TestHelper.fake_response_for(cask.url, "") + checker = Cask::LinkChecker.new(cask, TestHelper.fake_fetcher) + checker.run + checker.errors.must_include "timeout while requesting #{cask.url}" + end + + it "properly populates the response code and headers from an http response" do + TestHelper.fake_response_for(TestHelper.test_cask.url, <<-RESPONSE.gsub(/^ */, '')) + HTTP/1.1 200 OK + Content-Type: application/x-apple-diskimage + ETag: "b4208f3e84967be4b078ecaa03fba941" + Content-Length: 23726161 + Last-Modified: Sun, 12 Aug 2012 21:17:21 GMT + RESPONSE + + checker = Cask::LinkChecker.new(TestHelper.test_cask, TestHelper.fake_fetcher) + checker.run + checker.response_status.must_equal 'HTTP/1.1 200 OK' + checker.headers.must_equal({ + 'Content-Type' => 'application/x-apple-diskimage', + 'ETag' => '"b4208f3e84967be4b078ecaa03fba941"', + 'Content-Length' => '23726161', + 'Last-Modified' => 'Sun, 12 Aug 2012 21:17:21 GMT' + }) + end + end +end