Remove homebrew-fork Checksum class and refactor

* perform actual checksum calculation within download.rb
* use an accessor named sha256 instead of sums for checksum data
* migrate checksum-specific exceptions out of homebrew-fork
This commit is contained in:
Roland Walker 2014-12-23 11:08:45 -05:00
parent cb427e0da3
commit 05690689bc
13 changed files with 70 additions and 134 deletions

View File

@ -13,7 +13,6 @@ class Cask::Audit
def run!(download = false)
_check_required_stanzas
_check_no_string_version_latest
_check_checksums
_check_sha256_no_check_if_latest
_check_sourceforge_download_url_format
_check_download(download) if download
@ -27,7 +26,7 @@ class Cask::Audit
def _check_required_stanzas
odebug "Auditing required stanzas"
%i{version url homepage}.each do |sym|
%i{version sha256 url homepage}.each do |sym|
add_error "a #{sym} stanza is required" unless cask.send(sym)
end
add_error 'a license value is required (:unknown is OK)' unless cask.license
@ -37,12 +36,6 @@ class Cask::Audit
add_error 'at least one activatable artifact stanza is required' unless installable_artifacts.size > 0
end
def _check_checksums
odebug "Auditing checksums"
return if cask.sums == :no_check
add_error 'a sha256 stanza is required' unless cask.sums.is_a?(Array) && cask.sums.length > 0
end
def _check_no_string_version_latest
odebug "Verifying version :latest does not appear as a string ('latest')"
if (cask.version == 'latest')
@ -52,7 +45,7 @@ class Cask::Audit
def _check_sha256_no_check_if_latest
odebug "Verifying sha256 :no_check with version :latest"
if cask.version == :latest and cask.sums.is_a?(Array)
if cask.version == :latest and cask.sha256 != :no_check
add_error "you should use sha256 :no_check when version is :latest"
end
end

View File

@ -117,7 +117,7 @@ class Cask::CLI
Cask.init
command = lookup_command(command_string)
run_command(command, *rest)
rescue CaskError, ChecksumMismatchError => e
rescue CaskError, CaskSha256MismatchError => e
onoe e
$stderr.puts e.backtrace if Cask.debug
exit 1

View File

@ -6,7 +6,7 @@ class Cask::CLI::InternalStanza < Cask::CLI::InternalUseBase
#
# If no tokens are given, then data for all Casks is returned.
#
# The pseudo-stanzas "artifacts" and "sums" are available.
# The pseudo-stanza "artifacts" is available.
#
# On failure, a blank line is returned on the standard output.
#
@ -70,7 +70,6 @@ class Cask::CLI::InternalStanza < Cask::CLI::InternalUseBase
def self.print_stanzas(stanza, format=nil, table=nil, quiet=nil, *cask_tokens)
count = 0
stanza = :sums if stanza == :sha256
stanza = :full_name if stanza == :name
if ARTIFACTS.include?(stanza)
artifact_name = stanza
@ -118,9 +117,6 @@ class Cask::CLI::InternalStanza < Cask::CLI::InternalUseBase
else
if artifact_name or value.is_a?(Symbol)
puts value.inspect
elsif stanza == :sums
# hack. why does "to_s" equal "inspect" for checksum objects?
puts value
else
puts value.to_s
end

View File

@ -27,25 +27,31 @@ class Cask::Download
HOMEBREW_CACHE_CASKS.join(downloaded_path.basename)
rescue StandardError => e
end
_check_sums(downloaded_path, cask.sums) unless cask.sums === :no_check
_compare_sha256(downloaded_path, cask)
downloaded_path
end
private
def _check_sums(path, sums)
has_sum = false
sums.each do |sum|
unless sum.empty?
computed = Checksum.new(sum.hash_type, Digest.const_get(sum.hash_type.to_s.upcase).file(path).hexdigest)
if sum == computed
odebug "Checksums match"
else
ohai 'Note: running "brew update" may fix checksum errors'
raise ChecksumMismatchError.new(path, sum, computed)
end
has_sum = true
end
def _calc_sha256(path)
require 'digest/sha2'
Digest::SHA2.file(path).hexdigest
end
def _compare_sha256(path, cask)
begin
expected = cask.sha256
rescue RuntimeError => e
end
return if expected == :no_check
computed = _calc_sha256(path)
if expected.nil? or expected.empty?
raise CaskSha256MissingError.new("sha256 required: sha256 '#{computed}'")
end
if expected == computed
odebug "SHA256 checksums match"
else
ohai 'Note: running "brew update" may fix sha256 checksum errors'
raise CaskSha256MismatchError.new(path, expected, computed)
end
raise ChecksumMissingError.new("Checksum required: sha256 '#{Digest::SHA256.file(path).hexdigest}'") unless has_sum
end
end

View File

@ -1,4 +1,3 @@
require 'checksum'
require 'set'
module Cask::DSL; end
@ -44,7 +43,7 @@ module Cask::DSL
def tags; self.class.tags; end
def sums; self.class.sums || []; end
def sha256; self.class.sha256; end
def artifacts; self.class.artifacts; end
@ -149,6 +148,21 @@ module Cask::DSL
@version ||= arg
end
SYMBOLIC_SHA256S = Set.new [
:no_check,
]
def sha256(arg=nil)
if arg.nil?
@sha256
elsif @sha256
raise CaskInvalidError.new(self.token, "'sha256' stanza may only appear once")
elsif !arg.is_a?(String) and !SYMBOLIC_SHA256S.include?(arg)
raise CaskInvalidError.new(self.token, "invalid 'sha256' value: '#{arg.inspect}'")
end
@sha256 ||= arg
end
def tags(*args)
return @tags if args.empty?
if @tags and !args.empty?
@ -296,21 +310,6 @@ module Cask::DSL
end
end
attr_reader :sums
def hash_name(hash_type)
hash_type.to_s == 'sha2' ? 'sha256' : hash_type.to_s
end
def sha256(sha2=nil)
if sha2 == :no_check
@sums = sha2
else
@sums ||= []
@sums << Checksum.new(:sha2, sha2) unless sha2.nil?
end
end
def method_missing(method, *args)
Cask::Utils.method_missing_message(method, self.token)
return nil

View File

@ -103,3 +103,25 @@ class CaskInvalidError < CaskError
"Cask '#{token}' definition is invalid" + (submsg.length > 0 ? ": #{submsg}" : '')
end
end
class CaskSha256MissingError < ArgumentError
end
class CaskSha256MismatchError < RuntimeError
attr_reader :path, :expected, :actual
def initialize(path, expected, actual)
@path = path
@expected = expected
@actual = actual
end
def to_s
<<-EOS.undent
sha256 mismatch
Expected: #{expected}
Actual: #{actual}
File: #{path}
To retry an incomplete download, remove the file above.
EOS
end
end

View File

@ -1,3 +1,4 @@
# coding: utf-8
# see Homebrew Library/Homebrew/utils.rb
require 'yaml'
@ -101,7 +102,7 @@ module Cask::Utils
:version,
:license,
:tags,
:sums,
:sha256,
:artifacts,
:caveats,
:depends_on,

View File

@ -1,19 +0,0 @@
class Checksum
attr_reader :hash_type, :hexdigest
alias_method :to_s, :hexdigest
TYPES = [:sha1, :sha256]
def initialize(hash_type, hexdigest)
@hash_type = hash_type
@hexdigest = hexdigest
end
def empty?
hexdigest.empty?
end
def ==(other)
hash_type == other.hash_type && hexdigest == other.hexdigest
end
end

View File

@ -8,24 +8,3 @@ end
# raised in CurlDownloadStrategy.fetch
class CurlDownloadStrategyError < RuntimeError; end
# raised by Pathname#verify_checksum when "expected" is nil or empty
class ChecksumMissingError < ArgumentError; end
# raised by Pathname#verify_checksum when verification fails
class ChecksumMismatchError < RuntimeError
attr_reader :expected, :hash_type
def initialize fn, expected, actual
@expected = expected
@hash_type = expected.hash_type.to_s.upcase
super <<-EOS.undent
#{@hash_type} mismatch
Expected: #{expected}
Actual: #{actual}
Archive: #{fn}
To retry an incomplete download, remove the file above.
EOS
end
end

View File

@ -57,28 +57,6 @@ class Pathname
%r[^#!\s*\S+] === open('r') { |f| f.read(1024) }
end
def incremental_hash(klass)
digest = klass.new
if digest.respond_to?(:file)
digest.file(self)
else
buf = ""
open("rb") { |f| digest << buf while f.read(1024, buf) }
end
digest.hexdigest
end
def sha256
require 'digest/sha2'
incremental_hash(Digest::SHA2)
end
def verify_checksum expected
raise ChecksumMissingError if expected.nil? or expected.empty?
actual = Checksum.new(expected.hash_type, send(expected.hash_type).downcase)
raise ChecksumMismatchError.new(self, expected, actual) unless expected == actual
end
# FIXME eliminate the places where we rely on this method
alias_method :to_str, :to_s unless method_defined?(:to_str)

View File

@ -1,5 +1,4 @@
require 'download_strategy'
require 'checksum'
require 'version'
# Resource is the fundamental representation of an external resource. The
@ -7,8 +6,8 @@ require 'version'
# of this class.
class Resource
attr_reader :checksum, :mirrors, :specs, :using
attr_writer :url, :checksum, :version
attr_reader :mirrors, :specs, :using
attr_writer :url, :version
attr_accessor :download_strategy
# Formula name must be set after the DSL, as we have no access to the
@ -21,7 +20,6 @@ class Resource
@version = nil
@mirrors = []
@specs = {}
@checksum = nil
@using = nil
instance_eval(&block) if block_given?
end
@ -87,21 +85,6 @@ class Resource
cached_download
end
def verify_download_integrity fn
if fn.respond_to?(:file?) && fn.file?
ohai "Verifying #{fn.basename} checksum" if ARGV.verbose?
fn.verify_checksum(checksum)
end
rescue ChecksumMissingError
opoo "Cannot verify integrity of #{fn.basename}"
puts "A checksum was not provided for this resource"
puts "For your reference the SHA1 is: #{fn.sha1}"
end
Checksum::TYPES.each do |type|
define_method(type) { |val| @checksum = Checksum.new(type, val) }
end
def url val=nil, specs={}
return @url if val.nil?
@url = val

View File

@ -114,9 +114,7 @@ describe Cask::DSL do
sha256 'imasha2'
end
instance = ChecksumCask.new
instance.sums.must_equal [
Checksum.new(:sha2, 'imasha2')
]
instance.sha256.must_equal 'imasha2'
end
end

View File

@ -162,7 +162,7 @@ describe Cask::Installer do
shutup do
Cask::Installer.new(bad_checksum).install
end
}.must_raise(ChecksumMismatchError)
}.must_raise(CaskSha256MismatchError)
end
it "blows up on a missing checksum" do
@ -171,7 +171,7 @@ describe Cask::Installer do
shutup do
Cask::Installer.new(missing_checksum).install
end
}.must_raise(ChecksumMissingError)
}.must_raise(CaskSha256MissingError)
end
it "installs fine if sha256 :no_check is used" do