Source code
Revision control
Copy as Markdown
Other Tools
# 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
import argparse
import atexit
import os
import subprocess
import sys
from run_operations import (
ErrorHelp,
RepoType,
detect_repo_type,
get_last_line,
git_status,
run_git,
run_hg,
run_shell,
update_resume_state,
)
from vendor_and_commit import vendor_and_commit
script_name = os.path.basename(__file__)
error_help = ErrorHelp()
error_help.set_prefix(f"*** ERROR *** {script_name} did not complete successfully")
repo_type = detect_repo_type()
def early_exit_handler():
error_help.print_help()
def run_git_log_output(cmd, working_dir, log_filename):
res = subprocess.run(
cmd.split(" "),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
cwd=working_dir,
check=False,
)
with open(log_filename, "w") as ofile:
ofile.write(res.stdout)
return res
def run_shell_no_strip(cmd):
res = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
check=False,
)
if res.returncode != 0:
print(
f"Hit return code {res.returncode} running '{cmd}'. Aborting.",
file=sys.stderr,
)
print(res.stderr)
sys.exit(1)
output_lines = [] if len(res.stdout) == 0 else res.stdout.split("\n")
return output_lines
def find_base_commit(
libwebrtc_repo_path,
target_branch_head,
):
# read the last line of README.mozilla.last-vendor to retrieve our current base
# commit in moz-libwebrtc
base_commit_sha = get_last_line("third_party/libwebrtc/README.mozilla.last-vendor")
print(f"prelim base_commit_sha: {base_commit_sha}")
# if we've advanced into a chrome release branch, we need to adjust the
# base_commit_sha to the last common commit so we can now advance up
# the trunk commits.
stdout_lines = run_git(
f"git merge-base {base_commit_sha} {target_branch_head}", libwebrtc_repo_path
)
if len(stdout_lines) != 1:
error_help.set_help("Unable to find merge-base in find_base_commit")
sys.exit(1)
base_commit_sha = stdout_lines[0]
# now make it a short hash
stdout_lines = run_git(
f"git rev-parse --short {base_commit_sha}", libwebrtc_repo_path
)
if len(stdout_lines) != 1:
error_help.set_help("Unable to find merge-base in find_base_commit")
sys.exit(1)
base_commit_sha = stdout_lines[0]
print(f"adjusted base_commit_sha: {base_commit_sha}")
return base_commit_sha
def find_next_commit(
libwebrtc_repo_path,
target_branch_head,
):
base_commit_sha = find_base_commit(libwebrtc_repo_path, target_branch_head)
stdout_lines = run_git(
f"git log --oneline --reverse --format=%h --ancestry-path {base_commit_sha}^..{target_branch_head}",
libwebrtc_repo_path,
)
line_cnt = len(stdout_lines)
if line_cnt == 0:
error_help.set_help(
"No information was returned from 'git log' in find_next_commit"
)
sys.exit(1)
return stdout_lines[1] if len(stdout_lines) > 1 else stdout_lines[0]
def rebase_mozlibwebrtc_stack(
libwebrtc_repo_path,
log_path,
libwebrtc_branch,
next_libwebrtc_sha,
):
print("-------")
print(f"------- Rebase {libwebrtc_branch} to {next_libwebrtc_sha}")
print("-------")
run_git(f"git checkout -q {libwebrtc_branch}", libwebrtc_repo_path)
error_help.set_help(
f"The rebase operation onto {next_libwebrtc_sha} has failed. Please\n"
"resolve all the rebase conflicts. To fix this issue, you will need to\n"
f"jump to the github repo at {libwebrtc_repo_path} .\n"
"When the github rebase is complete, re-run the script to resume the\n"
"fast-forward process."
)
git_log_filename = os.path.join(log_path, "log-rebase-moz-libwebrtc.txt")
res = run_git_log_output(
f"git rebase {next_libwebrtc_sha}", libwebrtc_repo_path, git_log_filename
)
if res.returncode != 0:
sys.exit(1)
error_help.set_help(None)
def git_get_long_sha(
libwebrtc_repo_path,
commit_sha,
):
stdout_lines = run_git(
f"git show --format=%H --no-patch {commit_sha}", libwebrtc_repo_path
)
if len(stdout_lines) == 0:
error_help.set_help(
"No information was returned from 'git show' in git_get_long_sha"
)
sys.exit(1)
return stdout_lines[0]
def write_commit_message_file(
libwebrtc_repo_path,
state_path,
tmp_path,
next_libwebrtc_sha,
bug_number,
commit_msg_filename,
):
print("-------")
print(f"------- Write commit message file ({commit_msg_filename})")
print("-------")
no_op_msg_filename = os.path.join(
state_path, f"{next_libwebrtc_sha}.no-op-cherry-pick-msg"
)
long_sha = git_get_long_sha(libwebrtc_repo_path, next_libwebrtc_sha)
no_op_msg_contents = None
if os.path.exists(no_op_msg_filename):
no_op_msg_contents = "\n".join(run_shell(f"cat {no_op_msg_filename}"))
original_upstream_commit_msg = "\n".join(
run_shell_no_strip(
f'cd {libwebrtc_repo_path} && git show --name-only {next_libwebrtc_sha} | grep "^ "'
)
)
with open(commit_msg_filename, "w") as ofile:
ofile.write(f"Bug {bug_number} - Vendor libwebrtc from {next_libwebrtc_sha}\n")
ofile.write("\n")
if no_op_msg_contents:
ofile.write(no_op_msg_contents)
ofile.write("\n")
ofile.write("\n")
ofile.write(
)
ofile.write(original_upstream_commit_msg)
if __name__ == "__main__":
# first, check which repo we're in, git or hg
if repo_type is None or not isinstance(repo_type, RepoType):
print("Unable to detect repo (git or hg)")
sys.exit(1)
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_repo_dir = ".moz-fast-forward/moz-libwebrtc"
default_tar_name = "moz-libwebrtc.tar.gz"
parser = argparse.ArgumentParser(
description="Move forward one upstream commit and vendor the results"
)
parser.add_argument(
"--target-path",
required=True,
help=f"target path for vendoring (typically {default_target_dir})",
)
parser.add_argument(
"--state-path",
required=True,
help=f"path to state directory (typically {default_state_dir})",
)
parser.add_argument(
"--log-path",
required=True,
help=f"path to log directory (typically {default_log_dir})",
)
parser.add_argument(
"--tmp-path",
required=True,
help=f"path to tmp directory (typically {default_tmp_dir})",
)
parser.add_argument(
"--script-path",
required=True,
help=f"path to script directory (typically {default_script_dir})",
)
parser.add_argument(
"--repo-path",
required=True,
help=f"path to moz-libwebrtc repo (typically {default_repo_dir})",
)
parser.add_argument(
"--branch",
required=True,
help="moz-libwebrtc branch (typically mozpatches)",
)
parser.add_argument(
"--commit-bug-number",
type=int,
required=True,
help="integer Bugzilla number (example: 1800920)",
)
parser.add_argument(
"--target-branch-head",
required=True,
help="target branch head for fast-forward, should match MOZ_TARGET_UPSTREAM_BRANCH_HEAD in config_env",
)
args = parser.parse_args()
# ensure the log and tmp directories exist
os.makedirs(args.log_path, exist_ok=True)
os.makedirs(args.tmp_path, exist_ok=True)
# check the resume file
resume_state_filename = os.path.join(args.state_path, "fast_forward.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}'")
skip_to = "run"
if len(resume_state) == 0:
# Check for modified files and abort if present.
if repo_type == RepoType.GIT:
stdout_lines = git_status(".", args.target_path)
else:
stdout_lines = run_shell(
f'hg status --exclude "{args.target_path}/**.orig" {args.target_path}'
)
if len(stdout_lines) != 0:
print("There are modified files in the checkout. Cowardly aborting!")
print("\n".join(stdout_lines))
sys.exit(1)
# Completely clean the checkout before proceeding
if repo_type == RepoType.GIT:
run_shell("git restore --staged :/ && git restore :/ && git clean -fd")
else:
run_shell("hg update -C -r . && hg purge")
else:
skip_to = resume_state
if repo_type == RepoType.GIT:
run_git(f"git restore {args.target_path}/README.mozilla.last-vendor", ".")
else:
run_hg(f"hg revert -C {args.target_path}/README.mozilla.last-vendor")
# register the exit handler after the arg parser completes so '--help' doesn't exit with
# an error.
atexit.register(early_exit_handler)
next_commit_sha = find_next_commit(args.repo_path, args.target_branch_head)
print(f"next_commit_sha: {next_commit_sha}")
print(f" resume_state: {resume_state}")
print(f" skip_to: {skip_to}")
commit_msg_filename = os.path.join(args.tmp_path, "commit_msg.txt")
if skip_to == "run":
update_resume_state("resume2", resume_state_filename)
rebase_mozlibwebrtc_stack(
args.repo_path,
args.log_path,
args.branch,
next_commit_sha,
)
if skip_to == "resume2":
skip_to = "run"
if skip_to == "run":
update_resume_state("resume3", resume_state_filename)
write_commit_message_file(
args.repo_path,
args.state_path,
args.tmp_path,
next_commit_sha,
args.commit_bug_number,
commit_msg_filename,
)
if skip_to == "resume3":
skip_to = "run"
if skip_to == "run":
vendor_and_commit(
args.script_path,
args.repo_path,
args.branch,
next_commit_sha,
args.target_path,
args.state_path,
args.log_path,
commit_msg_filename,
)
update_resume_state("", resume_state_filename)
no_op_msg_filename = os.path.join(
args.state_path, f"{next_commit_sha}.no-op-cherry-pick-msg"
)
if os.path.exists(no_op_msg_filename):
os.remove(no_op_msg_filename)
# unregister the exit handler so the normal exit doesn't falsely
# report as an error.
atexit.unregister(early_exit_handler)