2019-07-21 22:41:20 +00:00
#!/usr/bin/env python3
import argparse
import os
import subprocess
import glob
2019-07-23 08:18:29 +00:00
import sys
2019-07-21 22:41:20 +00:00
GIT = os . getenv ( ' GIT ' , ' git ' )
GPG = os . getenv ( ' GPG ' , ' gpg ' )
def verify ( ) :
global args , workdir
2019-07-23 08:18:29 +00:00
if args . import_keys :
os . chdir ( ' gitian-pubkeys ' )
2019-11-19 23:32:50 +00:00
print ( ' Importing gpg pubkeys... ' )
keys = [ f for f in glob . glob ( ' *.asc ' , recursive = False ) ]
2019-07-23 08:18:29 +00:00
for key in keys :
subprocess . check_call ( [ GPG , ' --import ' , key ] )
os . chdir ( ' ../ ' )
if args . refresh_keys :
print ( ' Refreshing pubkeys... ' )
subprocess . check_call ( [ GPG , ' --refresh ' ] )
2019-11-19 02:50:03 +00:00
print ( ' Verifying signatures: ' )
is_verification_error = False
ver_pattern = args . version if args . version else ' v0* '
for sig_file in sorted ( glob . glob ( ver_pattern + ' -*/*/*.sig ' , recursive = False ) ) :
print ( ' - ' + ' { message: < {fill} } ' . format ( message = sig_file , fill = ' 72 ' ) , end = ' ' )
result = subprocess . run ( [ GPG , ' --verify ' , sig_file ] , capture_output = True , encoding = ' utf-8 ' )
if result . returncode != 0 :
is_verification_error = True
print ( ' \n ' )
sys . stderr . write ( ' ERROR: \n ' + result . stderr + ' - ' * 80 + ' \n ' )
else :
print ( ' [OK] ' )
if is_verification_error :
sys . stderr . write ( ' ERROR: One or more signatures failed verification. \n ' )
exit ( 1 )
2019-11-19 17:07:29 +00:00
print ( ' All signatures verified correctly. \n ' )
print ( ' Beginning checksum comparison... \n ' )
2019-11-19 23:32:50 +00:00
# Check that the contents between the assertion signers match.
# This is meant for quick verification, not for validation of their contents.
2019-11-19 17:07:29 +00:00
# TODO: prevent false positives related to filenames / whitespace / formatting.
builds = glob . glob ( ver_pattern + ' * ' )
for build in builds :
first_file = glob . glob ( build + ' /*/*.assert ' , recursive = False ) [ 0 ]
2019-11-19 23:32:50 +00:00
f = open ( first_file , ' r ' )
2019-11-19 17:07:29 +00:00
first_file_contents = f . readlines ( )
f . close ( )
for assert_file in glob . glob ( build + ' /*/*.assert ' , recursive = False ) :
2019-11-19 23:32:50 +00:00
f = open ( assert_file , ' r ' )
2019-11-19 17:07:29 +00:00
assert_file_contents = f . readlines ( )
f . close ( )
for i in range ( len ( assert_file_contents ) ) :
2019-11-19 23:32:50 +00:00
# Compare each line in the assertion file until base_manifests:
if assert_file_contents [ i ] == ' - base_manifests: !!omap \n ' :
2019-11-19 17:07:29 +00:00
break
2019-11-19 23:32:50 +00:00
# The OSX SDK may change from time to time:
if ' sdk ' in assert_file_contents [ i ] :
2019-11-19 17:07:29 +00:00
continue
if assert_file_contents [ i ] != first_file_contents [ i ] :
2019-11-19 23:32:50 +00:00
sys . stderr . write ( ' ERROR: Found conflicting contents on line: ' , i )
sys . stderr . write ( assert_file + ' : \n ' + assert_file_contents [ i ] )
sys . stderr . write ( first_file + ' : \n ' + first_file_contents [ i ] )
2019-11-19 17:07:29 +00:00
exit ( 1 )
print ( ' No discrepancies found in assertion files. ' )
print ( ' All checks passed. ' )
2019-07-21 22:41:20 +00:00
os . chdir ( workdir )
def main ( ) :
2019-11-19 23:32:50 +00:00
host_repo = ' git@github.com/monero-project/gitian.sigs '
2019-07-21 22:41:20 +00:00
global args , workdir
2019-11-19 02:50:03 +00:00
parser = argparse . ArgumentParser ( usage = ' %(prog)s [options] ' , description = ' Use this script to verify the signatures of existing gitian assert files and / or assert files in a specific pull request. ' )
2019-11-19 23:32:50 +00:00
parser . add_argument ( ' -p ' , ' --pull_id ' , dest = ' pull_id ' , help = ' GitHub Pull request id to check ' )
parser . add_argument ( ' -r ' , ' --remote ' , dest = ' remote ' , default = ' upstream ' , help = ' The git remote repository ' )
2019-07-23 08:18:29 +00:00
parser . add_argument ( ' -t ' , ' --target-branch ' , dest = ' target_branch ' , default = ' master ' , help = ' Remote repository merge into branch ' )
2019-07-21 22:41:20 +00:00
parser . add_argument ( ' -m ' , ' --merge ' , action = ' store_true ' , dest = ' merge ' , help = ' Merge the given pull request id ' )
2019-11-19 23:32:50 +00:00
parser . add_argument ( ' -k ' , ' --refresh-keys ' , action = ' store_true ' , dest = ' refresh_keys ' , help = ' Refresh all public keys that are currently in the gpg keyring. ' )
parser . add_argument ( ' -i ' , ' --import-keys ' , action = ' store_true ' , dest = ' import_keys ' , help = ' Import all public keys in the gitian-pubkeys directory to the gpg keyring. ' )
2019-07-21 22:41:20 +00:00
parser . add_argument ( ' -o ' , ' --no-verify ' , action = ' store_true ' , dest = ' no_verify ' , help = ' Do not run any signature verification ' )
2019-11-19 02:50:03 +00:00
parser . add_argument ( ' -v ' , ' --version ' , dest = ' version ' , help = ' Version number of sigs to be verified (defaults to all versions if not specified). ' )
2019-07-21 22:41:20 +00:00
args = parser . parse_args ( )
2019-07-23 08:18:29 +00:00
2019-07-21 22:41:20 +00:00
workdir = os . getcwd ( )
if args . pull_id != None :
# Get branch from remote pull request and compare
2019-11-19 23:32:50 +00:00
head_branch = args . pull_id + ' _head '
2019-07-21 22:41:20 +00:00
subprocess . check_call ( [ GIT , ' fetch ' , args . remote ] )
2019-11-19 23:32:50 +00:00
subprocess . check_call ( [ GIT , ' checkout ' , args . remote + ' / ' + args . target_branch ] )
subprocess . check_call ( [ GIT , ' fetch ' , ' -q ' , args . remote , ' pull/ ' + args . pull_id + ' /head: ' + head_branch ] )
2019-07-21 22:41:20 +00:00
subprocess . check_call ( [ GIT , ' checkout ' , ' -f ' , head_branch ] )
if args . merge :
# Hard reset the target branch to the remote's state and merge the pull request's head branch into it
subprocess . check_call ( [ GIT , ' checkout ' , args . target_branch ] )
subprocess . check_call ( [ GIT , ' reset ' , ' --hard ' , args . remote + ' / ' + args . target_branch ] )
print ( ' Merging and signing pull request # ' + args . pull_id + ' , if you are using a smartcard, confirm the signature now. ' )
2019-11-19 23:32:50 +00:00
subprocess . check_call ( [ GIT , ' merge ' , ' -q ' , ' --commit ' , ' --no-edit ' , ' -m ' , ' Merge pull request # ' + args . pull_id + ' into ' + args . target_branch , ' --no-ff ' , ' --gpg-sign ' , head_branch ] )
2019-07-21 22:41:20 +00:00
if not args . no_verify :
verify ( )
subprocess . check_call ( [ GIT , ' checkout ' , ' master ' ] )
subprocess . check_call ( [ GIT , ' branch ' , ' -D ' , head_branch ] )
else :
verify ( )
if __name__ == ' __main__ ' :
main ( )