123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #!/usr/bin/ruby
-
- require 'optparse'
- require 'yaml'
- require 'fileutils'
- require 'pathname'
-
- bold = ["\033[0m", "\033[1m"]
-
- @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
- @options[:markup] = true
- opts.on("-m", "--[no-]markup", "markup the output using ANSI escape codes") do |m|
- @options[:markup] = m
- 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]
- bold = ['', ''] unless @options[:markup]
-
- 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 "#{bold[1]}#{signer_dir}: BAD SIGNATURE#{bold[0]}"
- puts
- 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 "#{bold[1]}#{signer_dir}: MISMATCH#{bold[0]}"
- puts
- 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 "#{bold[1]}#{signer_dir}: OK#{bold[0]}"
- puts
- end
- if !current_manifest
- # take first manifest as 'current' to compare against
- current_manifest = result['out_manifest']
- end
- end
-
- exit 1 if did_fail
|