diff options
Diffstat (limited to 'dom/media/webrtc/third_party_build/cherry_pick_commit.py')
-rw-r--r-- | dom/media/webrtc/third_party_build/cherry_pick_commit.py | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/dom/media/webrtc/third_party_build/cherry_pick_commit.py b/dom/media/webrtc/third_party_build/cherry_pick_commit.py new file mode 100644 index 0000000000..5aa6c9e6a6 --- /dev/null +++ b/dom/media/webrtc/third_party_build/cherry_pick_commit.py @@ -0,0 +1,420 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import atexit +import os +import re +import shutil +import sys + +from filter_git_changes import filter_git_changes +from restore_patch_stack import restore_patch_stack +from run_operations import ( + get_last_line, + run_git, + run_hg, + run_shell, + update_resume_state, +) +from vendor_and_commit import vendor_and_commit + +# This script cherry-picks an upstream commit with the appropriate +# commit message, and adds the no-op commit tracking file for the when +# we vendor the upstream commit later. + +error_help = None +script_name = os.path.basename(__file__) + + +def early_exit_handler(): + print(f"*** ERROR *** {script_name} did not complete successfully") + if error_help is not None: + print(error_help) + + +def write_commit_message_file( + commit_message_filename, + github_path, + github_sha, + bug_number, + reviewers, +): + print(f"commit_message_filename: {commit_message_filename}") + print(f"github_path: {github_path}") + print(f"github_sha: {github_sha}") + print(f"bug_number: {bug_number}") + + cmd = "git show --format=%H --no-patch {}".format(github_sha) + stdout_lines = run_git(cmd, github_path) + github_long_sha = stdout_lines[0] + print(f"github_long_sha: {github_long_sha}") + + cmd = "git show --format=%s%n%n%b --no-patch {}".format(github_sha) + github_commit_msg_lines = run_git(cmd, github_path) + + with open(commit_message_filename, "w") as ofile: + ofile.write( + "Bug {} - Cherry-pick upstream libwebrtc commit {} r?{}".format( + bug_number, github_sha, reviewers + ) + ) + ofile.write("\n") + ofile.write("\n") + ofile.write( + "Upstream commit: https://webrtc.googlesource.com/src/+/{}".format( + github_long_sha + ) + ) + ofile.write("\n") + for line in github_commit_msg_lines: + ofile.write(" {}".format(line)) + ofile.write("\n") + + +def cherry_pick_commit( + commit_message_filename, + github_path, + github_sha, +): + print(f"commit_message_filename: {commit_message_filename}") + print(f"github_path: {github_path}") + print(f"github_sha: {github_sha}") + + cmd = "git cherry-pick --no-commit {}".format(github_sha) + run_git(cmd, github_path) + + cmd = "git commit --file {}".format(os.path.abspath(commit_message_filename)) + run_git(cmd, github_path) + + +def write_noop_tracking_file( + github_sha, + bug_number, +): + noop_basename = "{}.no-op-cherry-pick-msg".format(github_sha) + noop_filename = os.path.join(args.state_path, noop_basename) + print(f"noop_filename: {noop_filename}") + with open(noop_filename, "w") as ofile: + ofile.write("We cherry-picked this in bug {}".format(bug_number)) + ofile.write("\n") + shutil.copy(noop_filename, args.patch_path) + cmd = "hg add {}".format(os.path.join(args.patch_path, noop_basename)) + run_hg(cmd) + cmd = "hg amend {}".format(os.path.join(args.patch_path, noop_basename)) + run_hg(cmd) + + +if __name__ == "__main__": + default_target_dir = "third_party/libwebrtc" + default_state_dir = ".moz-fast-forward" + default_log_dir = ".moz-fast-forward/logs" + default_tmp_dir = ".moz-fast-forward/tmp" + default_script_dir = "dom/media/webrtc/third_party_build" + default_patch_dir = "third_party/libwebrtc/moz-patch-stack" + default_repo_dir = ".moz-fast-forward/moz-libwebrtc" + default_tar_name = "moz-libwebrtc.tar.gz" + + parser = argparse.ArgumentParser( + description="Cherry-pick upstream libwebrtc commit" + ) + parser.add_argument( + "--target-path", + default=default_target_dir, + help="target path for vendoring (defaults to {})".format(default_target_dir), + ) + parser.add_argument( + "--state-path", + default=default_state_dir, + help="path to state directory (defaults to {})".format(default_state_dir), + ) + parser.add_argument( + "--log-path", + default=default_log_dir, + help="path to log directory (defaults to {})".format(default_log_dir), + ) + parser.add_argument( + "--tmp-path", + default=default_tmp_dir, + help="path to tmp directory (defaults to {})".format(default_tmp_dir), + ) + parser.add_argument( + "--script-path", + default=default_script_dir, + help="path to script directory (defaults to {})".format(default_script_dir), + ) + parser.add_argument( + "--repo-path", + default=default_repo_dir, + help="path to moz-libwebrtc repo (defaults to {})".format(default_repo_dir), + ) + parser.add_argument( + "--tar-name", + default=default_tar_name, + help="name of tar file (defaults to {})".format(default_tar_name), + ) + parser.add_argument( + "--commit-sha", + required=True, + help="sha of commit to examine", + ) + parser.add_argument( + "--branch", + default="mozpatches", + help="moz-libwebrtc branch (defaults to mozpatches)", + ) + parser.add_argument( + "--commit-bug-number", + type=int, + required=True, + help="integer Bugzilla number (example: 1800920)", + ) + parser.add_argument( + "--patch-path", + default=default_patch_dir, + help="path to save patches (defaults to {})".format(default_patch_dir), + ) + parser.add_argument( + "--reviewers", + required=True, + help='reviewers for cherry-picked patch (like "ng,mjf")', + ) + parser.add_argument( + "--abort", + action="store_true", + default=False, + help="abort an interrupted cherry-pick", + ) + parser.add_argument( + "--continue", + dest="cont", # because args.continue causes syntax errors + action="store_true", + default=False, + help="continue an interrupted cherry-pick", + ) + args = parser.parse_args() + + # register the exit handler after the arg parser completes so '--help' doesn't exit with + # an error. + atexit.register(early_exit_handler) + + # other scripts assume the short-sha is used for various comparisons, so + # make sure the provided sha is in the short form. + cmd = f"git show --format=%h --no-patch {args.commit_sha}" + args.commit_sha = run_git(cmd, args.repo_path)[0] + + commit_message_filename = os.path.join(args.tmp_path, "cherry-pick-commit_msg.txt") + + resume_state_filename = os.path.join(args.state_path, "cherry_pick_commit.resume") + resume_state = "" + if os.path.exists(resume_state_filename): + resume_state = get_last_line(resume_state_filename).strip() + print(f"resume_state: '{resume_state}'") + + # don't allow abort/continue flags if not in resume state + error_help = "--abort or --continue flags are not allowed when not in resume state" + if len(resume_state) == 0 and (args.abort or args.cont): + sys.exit(1) + error_help = None + + # detect missing abort/continue flags if in resume state + error_help = "cherry-pick in progress, use --abort or --continue" + if len(resume_state) != 0 and not args.abort and not args.cont: + sys.exit(1) + error_help = None + + # handle aborting cherry-pick + if args.abort: + # If the resume_state is not resume2 or resume3 that means we may + # have committed something to mercurial. First we need to check + # for our cherry-pick commit message, and if found, remove + # that commit. + if not (resume_state == "resume2" or resume_state == "resume3"): + run_hg("hg revert --all") + run_hg("hg purge {}".format(args.target_path)) + # check for committed mercurial patch and backout + stdout_lines = run_hg("hg log --template {desc|firstline}\n -r .") + # check for "Cherry-pick upstream libwebrtc commit" + print("stdout_lines before filter: {}".format(stdout_lines)) + stdout_lines = [ + line + for line in stdout_lines + if re.findall("Cherry-pick upstream libwebrtc commit", line) + ] + print("looking for commit: {}".format(stdout_lines)) + if len(stdout_lines) > 0: + cmd = "hg prune ." + print("calling '{}'".format(cmd)) + run_hg(cmd) + print("restoring patch stack") + restore_patch_stack( + args.repo_path, + args.branch, + os.path.abspath(args.patch_path), + args.state_path, + args.tar_name, + "https", # unused in this case + ) + # reset the resume file + print("reset resume file") + update_resume_state("", resume_state_filename) + print("after resetting resume file") + atexit.unregister(early_exit_handler) + sys.exit(0) + + # make sure the mercurial repo is clean before beginning + error_help = ( + "There are modified or untracked files in the mercurial repo.\n" + f"Please start with a clean repo before running {script_name}" + ) + stdout_lines = run_hg("hg status") + if len(stdout_lines) != 0: + sys.exit(1) + + # make sure the github repo exists + error_help = ( + f"No moz-libwebrtc github repo found at {args.repo_path}\n" + f"Please run restore_patch_stack.py before running {script_name}" + ) + if not os.path.exists(args.repo_path): + sys.exit(1) + error_help = None + + if len(resume_state) == 0: + update_resume_state("resume2", resume_state_filename) + print("-------") + print(f"------- write commit message file {commit_message_filename}") + print("-------") + write_commit_message_file( + commit_message_filename, + args.repo_path, + args.commit_sha, + args.commit_bug_number, + args.reviewers, + ) + + if len(resume_state) == 0 or resume_state == "resume2": + resume_state = "" + update_resume_state("resume3", resume_state_filename) + print("-------") + print(f"------- cherry-pick {args.commit_sha} into {args.repo_path}") + print("-------") + full_commit_message_filename = os.path.abspath(commit_message_filename) + error_help = ( + f"The cherry-pick operation of {args.commit_sha} has failed.\n" + "To fix this issue, you will need to jump to the github\n" + f"repo at {args.repo_path} .\n" + "Please resolve all the cherry-pick conflicts, and commit the changes\n" + "using:\n" + f" git commit --file {full_commit_message_filename}\n" + "\n" + "When the github cherry-pick is complete, resume running this\n" + f"script ({script_name})" + ) + cherry_pick_commit( + commit_message_filename, + args.repo_path, + args.commit_sha, + ) + error_help = None + + if len(resume_state) == 0 or resume_state == "resume3": + resume_state = "" + update_resume_state("resume4", resume_state_filename) + print("-------") + print(f"------- vendor from {args.repo_path}") + print("-------") + error_help = ( + f"Vendoring the newly cherry-picked git commit ({args.commit_sha}) has failed.\n" + "The mercurial repo is in an unknown state. This failure is\n" + "rare and thus makes it difficult to provide definitive guidance.\n" + "In essence, the current failing command is:\n" + f"./mach python {args.script_path}/vendor_and_commit.py \\\n" + f" --script-path {args.script_path} \\\n" + f" --repo-path {args.repo_path} \\\n" + f" --branch {args.branch} \\\n" + f" --commit-sha {args.commit_sha} \\\n" + f" --target-path {args.target_path} \\\n" + f" --state-path {args.state_path} \\\n" + f" --log-path {args.log_path} \\\n" + f" --commit-msg-path {commit_message_filename}\n" + "\n" + "Additional guidance may be in the terminal output above. Resolve\n" + "issues encountered by vendor_and_commit.py followed by re-running\n" + "vendor_and_commit.py to resume/complete its processing. After\n" + "vendor_and_commit.py completes successfully, resume running\n" + f"this script ({script_name})" + ) + vendor_and_commit( + args.script_path, + args.repo_path, + args.branch, + args.commit_sha, + args.target_path, # os.path.abspath(args.target_path), + args.state_path, + args.log_path, + commit_message_filename, + ) + error_help = None + + if len(resume_state) == 0 or resume_state == "resume4": + resume_state = "" + update_resume_state("resume5", resume_state_filename) + error_help = ( + "Reverting change to 'third_party/libwebrtc/README.mozilla'\n" + "has failed. The cherry-pick commit should not modify\n" + "'third_party/libwebrtc/README.mozilla'. If necessary\n" + "manually revert changes to 'third_party/libwebrtc/README.mozilla'\n" + f"and re-run {script_name}\n" + "to complete the cherry-pick processing." + ) + # The normal vendoring process updates README.mozilla with info + # on what commit was vendored and the command line used to do + # the vendoring. Since we're only reusing the vendoring script + # here, we don't want to update the README.mozilla file. + cmd = "hg revert -r tip^ third_party/libwebrtc/README.mozilla" + run_hg(cmd) + cmd = "hg amend" + run_hg(cmd) + error_help = None + + if len(resume_state) == 0 or resume_state == "resume5": + resume_state = "" + update_resume_state("resume6", resume_state_filename) + # get the files changed from the newly vendored cherry-pick + # commit in mercurial + cmd = "hg status --change tip --exclude '**/README.*'" + stdout_lines = run_shell(cmd) # run_shell to allow file wildcard + print("Mercurial changes:\n{}".format(stdout_lines)) + hg_file_change_cnt = len(stdout_lines) + + # get the files changed from the original cherry-picked patch in + # our github repo (moz-libwebrtc) + git_paths_changed = filter_git_changes(args.repo_path, args.commit_sha, None) + print("github changes:\n{}".format(git_paths_changed)) + git_file_change_cnt = len(git_paths_changed) + + error_help = ( + f"Vendoring the cherry-pick of commit {args.commit_sha} has failed due to mismatched\n" + f"changed file counts between mercurial ({hg_file_change_cnt}) and git ({git_file_change_cnt}).\n" + "This may be because the mozilla patch-stack was not verified after\n" + "running restore_patch_stack.py. After reconciling the changes in\n" + f"the newly committed mercurial patch, please re-run {script_name} to complete\n" + "the cherry-pick processing." + ) + if hg_file_change_cnt != git_file_change_cnt: + sys.exit(1) + error_help = None + + if len(resume_state) == 0 or resume_state == "resume6": + resume_state = "" + update_resume_state("", resume_state_filename) + print("-------") + print("------- write the noop tracking file") + print("-------") + write_noop_tracking_file(args.commit_sha, args.commit_bug_number) + + # unregister the exit handler so the normal exit doesn't falsely + # report as an error. + atexit.unregister(early_exit_handler) |