gitian-builder/bin/gverify
Wladimir J. van der Laan 449bc5b243 Extend gverify comparison
- Allow comparing to a sepcific 'golden' manifest with `--compare-to`
- By default pick the first manifest to compare to, instead of always
  comparing against the previous one, which is confusing
- Show line-by-line difference if `-v` given
2014-06-11 09:37:03 +02:00

159 lines
4.3 KiB
Ruby
Executable File

#!/usr/bin/ruby
require 'optparse'
require 'yaml'
require 'fileutils'
require 'pathname'
@options = {}
def system!(cmd)
system(cmd) or raise "failed to run #{cmd}"
end
def sanitize(str, where)
raise "unsanitary string in #{where}" if (str =~ /[^\w.-]/)
str
end
def sanitize_path(str, where)
raise "unsanitary string in #{where}" if (str =~ /[^@\w\/. -]/)
str
end
def info(str)
puts str if @options[:verbose]
end
################################
OptionParser.new do |opts|
opts.banner = "Usage: build [options] <build-description>.yml"
opts.on("-v", "--verbose", "be more verbose") do |v|
@options[:verbose] = v
end
opts.on("-r REL", "--release REL", "release name") do |v|
@options[:release] = v
end
opts.on("-d DEST", "--destination DEST", "directory to place signature in") do |v|
@options[:destination] = v
end
opts.on("-c SIGNER", "--compare-to SIGNER", "compare other manifests to SIGNER's, if not given pick first") do |v|
@options[:compareto] = v
end
end.parse!
base_dir = Pathname.new(__FILE__).expand_path.dirname.parent
build_desc_file = ARGV.shift or raise "must supply YAML build description file"
build_desc = YAML.load_file(build_desc_file)
in_sums = []
result_dir = 'result'
package_name = build_desc["name"] or raise "must supply name"
package_name = sanitize(package_name, "package name")
destination = @options[:destination] || File.join(base_dir, "sigs", package_name)
release = @options[:release] || "current"
release = sanitize(release, "release")
verbose = @options[:verbose]
release_path = File.join(destination, release)
File.exists?(release_path) or raise "#{release_path} does not exist"
result_file = "#{package_name}-build.assert"
sig_file = "#{result_file}.sig"
current_manifest = nil
if @options[:compareto]
# Load a specific 'golden' manifest to compare to
compareto = @options[:compareto]
result_path = sanitize_path(File.join(release_path, compareto, result_file), "result path")
result = YAML.load_file(result_path)
current_manifest = result['out_manifest']
end
did_fail = false
Dir.foreach(release_path) do |signer_dir|
next if signer_dir == "." or signer_dir == ".."
signer_path = sanitize_path(File.join(release_path, signer_dir), "signer path")
next if !File.directory?(signer_path)
result_path = sanitize_path(File.join(signer_path, result_file), "result path")
sig_path = sanitize_path(File.join(signer_path, sig_file), "result path")
if !File.exist?(result_path)
puts "missing result at #{result_path}"
next
end
if !File.exist?(sig_path)
puts "missing signature at #{sig_path}"
next
end
result = YAML.load_file(result_path)
system("gpg --keyserver pgp.mit.edu --recv-keys `gpg --quiet --batch --verify \"#{File.join(signer_path, 'signature.pgp')}\" \"#{result_path}\" 2>&1 | head -n1 | grep \"key ID\" | awk '{ print $15 }'` > /dev/null 2>&1")
out = `gpg --quiet --batch --verify \"#{sig_path}\" \"#{result_path}\" 2>&1`
if $? != 0
out.each_line do |line|
if line =~ /^gpg: Signature made/
info(line)
else
puts line
end
end
puts "#{signer_dir}: BAD SIGNATURE"
did_fail = true
elsif current_manifest and (result['out_manifest'] != current_manifest or result['release'] != release or result['name'] != package_name)
out.each_line do |line|
if line =~ /^gpg: Signature made/
info(line)
elsif line =~ /^gpg: Good signature/
info(line)
elsif line =~ /^gpg: aka/
info(line)
else
puts line
end
end
puts "#{signer_dir}: MISMATCH"
if verbose
lines1 = current_manifest.each_line
lines2 = result['out_manifest'].each_line
lines1.zip(lines2) do |line|
if line[0] != line[1]
puts "- "+line[0]
puts "+ "+line[1]
end
end
end
did_fail = true
else
out.each_line do |line|
if line =~ /^gpg: Signature made/
info(line)
elsif line =~ /^gpg: Good signature/
info(line)
elsif line =~ /^gpg: aka/
info(line)
else
puts line
end
end
puts "#{signer_dir}: OK"
end
if !current_manifest
# take first manifest as 'current' to compare against
current_manifest = result['out_manifest']
end
end
exit 1 if did_fail