def sign_tarball(tarball_path): """ Prompt the user to GPG-sign the tarball using their Apache GPG key. """ if not confirm_prompt("Would you like to GPG-sign the tarball now?"): return email = get_my_email() if not email.endswith("@apache.org"): print( Colors.YELLOW, "Your email address for the repository is not an @apache.org address." ) print( "Release signatures should typically be signed by committers with @apache.org GPG keys." ) print(end=Colors.RESET) if not confirm_prompt("Continue?"): return try: subprocess.check_call( ["gpg", "--detach-sign", "--armor", "-u", email, tarball_path]) except subprocess.CalledProcessError: print(Colors.RED + "GPG signing failed. Artifact will not be signed." + Colors.RESET) return print(Colors.GREEN + "Generated signature:\t" + Colors.RESET, tarball_path + ".asc")
def check_repo_not_dirty(): """Check that the git repository isn't dirty.""" dirty_repo = subprocess.call("git diff --quiet && git diff --cached --quiet", shell=True) != 0 if not dirty_repo: return print "The repository does not appear to be clean." print Colors.RED + "The source release will not include your local changes." + Colors.RESET if not confirm_prompt("Continue?"): sys.exit(1)
def sign_tarball(tarball_path): """ Prompt the user to GPG-sign the tarball using their Apache GPG key. """ if not confirm_prompt("Would you like to GPG-sign the tarball now?"): return email = get_my_email() if not email.endswith("@apache.org"): print(Colors.YELLOW, "Your email address for the repository is not an @apache.org address.") print("Release signatures should typically be signed by committers with @apache.org GPG keys.") print(end=Colors.RESET) if not confirm_prompt("Continue?"): return try: subprocess.check_call(["gpg", "--detach-sign", "--armor", "-u", email, tarball_path]) except subprocess.CalledProcessError: print(Colors.RED + "GPG signing failed. Artifact will not be signed." + Colors.RESET) return print(Colors.GREEN + "Generated signature:\t" + Colors.RESET, tarball_path + ".asc")
def check_repo_not_dirty(): """Check that the git repository isn't dirty.""" dirty_repo = subprocess.call("git diff --quiet && git diff --cached --quiet", shell=True) != 0 if not dirty_repo: return print("The repository does not appear to be clean.") print(Colors.RED + "The source release will not include your local changes." + Colors.RESET) if not confirm_prompt("Continue?"): sys.exit(1)
def do_update(branch, gerrit_sha, apache_sha): """ Displays and performs a proposed update of the Apache repository for branch 'branch' from 'apache_sha' to 'gerrit_sha'. """ # First, verify that the update is fast-forward. If it's not, then something # must have gotten committed to Apache outside of gerrit, and we'd need some # manual intervention. if not is_fast_forward(apache_sha, gerrit_sha): print >> sys.stderr, "Cannot update branch '%s' from gerrit:" % branch print >> sys.stderr, "Apache revision %s is not an ancestor of gerrit revision %s" % ( apache_sha[:8], gerrit_sha[:8]) print >> sys.stderr, "Something must have been committed to Apache and bypassed gerrit." print >> sys.stderr, "Manual intervention is required." sys.exit(1) # List the commits that are going to be pushed to the ASF, so that the committer # can verify and "sign off". commits = rev_list("%s..%s" % (apache_sha, gerrit_sha)) commits.reverse() # Display from oldest to newest. print "-" * 60 print Colors.GREEN + ("%d commit(s) need to be pushed from Gerrit to ASF:" % len(commits)) + Colors.RESET push_sha = None for sha in commits: oneline = describe_commit(sha) print " ", oneline committer = get_committer_email(sha) if committer != get_my_email(): print Colors.RED + " !!! Committed by someone else (%s) !!!" % committer, Colors.RESET if not confirm_prompt( Colors.RED + " !!! Are you sure you want to push on behalf of another committer?" + Colors.RESET): # Even if they don't want to push this commit, we could still push any # earlier commits that the user _did_ author. if push_sha is not None: print "... will still update to prior commit %s..." % push_sha break push_sha = sha if push_sha is None: print "Nothing to push" return # Everything has been confirmed. Do the actual push cmd = ['git', 'push', 'apache'] if OPTIONS.dry_run: cmd.append('--dry-run') cmd.append('%s:refs/heads/%s' % (push_sha, branch)) print Colors.GREEN + "Running: " + Colors.RESET + " ".join(cmd) subprocess.check_call(cmd) print Colors.GREEN + "Successfully updated %s to %s" % ( branch, gerrit_sha) + Colors.RESET print
def do_update(branch, gerrit_sha, apache_sha): """ Displays and performs a proposed update of the Apache repository for branch 'branch' from 'apache_sha' to 'gerrit_sha'. """ # First, verify that the update is fast-forward. If it's not, then something # must have gotten committed to Apache outside of gerrit, and we'd need some # manual intervention. if not is_fast_forward(apache_sha, gerrit_sha): print >>sys.stderr, "Cannot update branch '%s' from gerrit:" % branch print >>sys.stderr, "Apache revision %s is not an ancestor of gerrit revision %s" % ( apache_sha[:8], gerrit_sha[:8]) print >>sys.stderr, "Something must have been committed to Apache and bypassed gerrit." print >>sys.stderr, "Manual intervention is required." sys.exit(1) # List the commits that are going to be pushed to the ASF, so that the committer # can verify and "sign off". commits = rev_list("%s..%s" % (apache_sha, gerrit_sha)) commits.reverse() # Display from oldest to newest. print "-" * 60 print Colors.GREEN + ("%d commit(s) need to be pushed from Gerrit to ASF:" % len(commits)) + Colors.RESET push_sha = None for sha in commits: oneline = describe_commit(sha) print " ", oneline committer = get_committer_email(sha) if committer != get_my_email(): print Colors.RED + " !!! Committed by someone else (%s) !!!" % committer, Colors.RESET if not confirm_prompt( Colors.RED + " !!! Are you sure you want to push on behalf of another committer?" + Colors.RESET): # Even if they don't want to push this commit, we could still push any # earlier commits that the user _did_ author. if push_sha is not None: print "... will still update to prior commit %s..." % push_sha break push_sha = sha if push_sha is None: print "Nothing to push" return # Everything has been confirmed. Do the actual push cmd = ['git', 'push', 'apache'] if OPTIONS.dry_run: cmd.append('--dry-run') cmd.append('%s:refs/heads/%s' % (push_sha, branch)) print Colors.GREEN + "Running: " + Colors.RESET + " ".join(cmd) subprocess.check_call(cmd) print Colors.GREEN + "Successfully updated %s to %s" % (branch, gerrit_sha) + Colors.RESET print
def check_no_local_commits(): """ Check that there are no local commits which haven't been pushed to the upstream repo via Jenkins. """ upstream_commit = get_upstream_commit() cur_commit = check_output(["git", "rev-parse", "HEAD"]).strip().decode('utf-8') if upstream_commit == cur_commit: return print("The repository appears to have local commits:") subprocess.check_call(["git", "log", "--oneline", "%s..HEAD" % upstream_commit]) print(Colors.RED + "This should not be an official release!" + Colors.RESET) if not confirm_prompt("Continue?"): sys.exit(1)
def run_rat(tarball_path): """ Run Apache RAT on the source tarball. Raises an exception on failure. """ if not confirm_prompt( "Would you like to run Apache RAT (Release Audit Tool) now?"): return # TODO: Cache and call the jar from the maven repo? rat_url = "https://search.maven.org/remotecontent?filepath=org/apache/rat/apache-rat/0.13/apache-rat-0.13.jar" tmpdir_path = tempfile.mkdtemp() rat_report_result = '' try: rat_jar_dest = "%s/%s" % (tmpdir_path, os.path.basename(rat_url)) print("> Downloading RAT jar from " + rat_url) urllib.urlretrieve(rat_url, rat_jar_dest) print("> Running RAT...") xml = subprocess.check_output( ["java", "-jar", rat_jar_dest, "-x", tarball_path]) rat_report_dest = "%s/%s" % (tmpdir_path, "rat_report.xml") with open(rat_report_dest, "wb") as f: f.write(xml) print("> Parsing RAT report...") rat_report_result = subprocess.check_output( [ "./build-support/release/check-rat-report.py", "./build-support/release/rat_exclude_files.txt", rat_report_dest ], stderr=subprocess.STDOUT).decode('utf-8') print(Colors.GREEN + "RAT: LICENSES APPROVED" + Colors.RESET) except subprocess.CalledProcessError as e: print(Colors.RED + "RAT: LICENSES NOT APPROVED" + Colors.RESET) print(e.output.decode('utf-8')) raise e finally: shutil.rmtree(tmpdir_path)
def run_rat(tarball_path): """ Run Apache RAT on the source tarball. Raises an exception on failure. """ if not confirm_prompt("Would you like to run Apache RAT (Release Audit Tool) now?"): return # TODO: Cache and call the jar from the maven repo? rat_url = "http://central.maven.org/maven2/org/apache/rat/apache-rat/0.11/apache-rat-0.11.jar" tmpdir_path = tempfile.mkdtemp() rat_report_result = "" try: rat_jar_dest = "%s/%s" % (tmpdir_path, os.path.basename(rat_url)) print "> Downloading RAT jar from " + rat_url urllib.urlretrieve(rat_url, rat_jar_dest) print "> Running RAT..." xml = subprocess.check_output(["java", "-jar", rat_jar_dest, "-x", tarball_path]) rat_report_dest = "%s/%s" % (tmpdir_path, "rat_report.xml") with open(rat_report_dest, "w") as f: f.write(xml) print "> Parsing RAT report..." rat_report_result = subprocess.check_output( [ "./build-support/release/check-rat-report.py", "./build-support/release/rat_exclude_files.txt", rat_report_dest, ], stderr=subprocess.STDOUT, ) print Colors.GREEN + "RAT: LICENSES APPROVED" + Colors.RESET except subprocess.CalledProcessError as e: print Colors.RED + "RAT: LICENSES NOT APPROVED" + Colors.RESET print e.output raise e finally: shutil.rmtree(tmpdir_path)