Browse Source

brew cask audit

for a given cask:
 - checks required fields
 - checks URL responds successfully
 - checks content_length specified
phinze 12 years ago
parent
commit
2f99d29693
  1. 128
      lib/cask/audit.rb
  2. 7
      lib/cask/auditor.rb
  3. 12
      lib/cask/cli/audit.rb
  4. 6
      lib/cask/dsl.rb
  5. 78
      test/cask/audit_test.rb

128
lib/cask/audit.rb

@ -0,0 +1,128 @@
class Cask::Audit
attr_reader :cask, :errors, :warnings, :headers, :response_status
def initialize(cask)
@cask = cask
@errors = []
@warnings = []
@headers = {}
end
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)
@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
def _check_required_fields
add_error "url is required" unless cask.url
add_error "version is required" unless cask.version
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 = _curl(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
def _curl(url)
`curl --max-time 5 --silent --location --head '#{url}'`
end
end

7
lib/cask/auditor.rb

@ -0,0 +1,7 @@
class Cask::Auditor
def self.audit(cask)
audit = Cask::Audit.new(cask)
audit.run!
puts audit.summary
end
end

12
lib/cask/cli/audit.rb

@ -0,0 +1,12 @@
class Cask::CLI::Audit
def self.run(*args)
casks_to_audit = args.empty? ? Cask.all : args.map { |arg| Cask.load(arg) }
casks_to_audit.each do |cask|
Cask::Auditor.audit(cask)
end
end
def self.help
"verifies installability of casks"
end
end

6
lib/cask/dsl.rb

@ -3,6 +3,8 @@ module Cask::DSL
base.extend(ClassMethods)
end
def content_length; self.class.content_length; end
def homepage; self.class.homepage; end
def url; self.class.url; end
@ -10,6 +12,10 @@ module Cask::DSL
def version; self.class.version; end
module ClassMethods
def content_length(content_length=nil)
@content_length ||= content_length
end
def homepage(homepage=nil)
@homepage ||= homepage
end

78
test/cask/audit_test.rb

@ -0,0 +1,78 @@
require 'test_helper'
describe Cask::Audit do
describe "result" do
it "is 'failed' if there are have been any errors added" do
audit = Cask::Audit.new(mock())
audit.add_error 'bad'
audit.add_warning 'eh'
audit.result.must_match /failed/
end
it "is 'warning' if there are no errors, but there are warnings" do
audit = Cask::Audit.new(mock())
audit.add_warning 'eh'
audit.result.must_match /warning/
end
it "is 'passed' if there are no errors or warning" do
audit = Cask::Audit.new(mock())
audit.result.must_match /passed/
end
end
describe "run!" do
describe "required fields" do
it "adds an error if url is missing" do
audit = Cask::Audit.new(stub(:url => nil, :version => 'something', :homepage => 'something'))
audit.run!
audit.errors.must_include 'url is required'
end
it "adds an error if version is missing" do
audit = Cask::Audit.new(stub(:url => 'something', :version => nil, :homepage => 'something'))
audit.run!
audit.errors.must_include 'version is required'
end
it "adds an error if homepage is missing" do
audit = Cask::Audit.new(stub(:url => 'something', :version => 'something', :homepage => nil))
audit.run!
audit.errors.must_include 'homepage is required'
end
end
describe "request processing" do
it "adds an error if response is empty" do
audit = Cask::Audit.new(stub(:url => 'something', :version => 'something', :homepage => 'something'))
audit.stubs(:_curl).returns('')
audit.run!
audit.errors.must_include 'timeout while requesting something'
end
it "properly populates the response code and headers from an http response" do
audit = Cask::Audit.new(stub(
:url => URI('http://something/file.zip'),
:version => 'something',
:homepage => 'something',
:content_length => '123'
))
audit.stubs(:_curl).returns(<<-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.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
Loading…
Cancel
Save