summaryrefslogtreecommitdiffstats
path: root/mobile/android/mach_commands.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
commitda4c7e7ed675c3bf405668739c3012d140856109 (patch)
treecdd868dba063fecba609a1d819de271f0d51b23e /mobile/android/mach_commands.py
parentAdding upstream version 125.0.3. (diff)
downloadfirefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz
firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/mach_commands.py')
-rw-r--r--mobile/android/mach_commands.py300
1 files changed, 297 insertions, 3 deletions
diff --git a/mobile/android/mach_commands.py b/mobile/android/mach_commands.py
index 68271bd0c0..fac483902c 100644
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -5,6 +5,8 @@
import argparse
import logging
import os
+import re
+import subprocess
import sys
import tarfile
@@ -12,6 +14,7 @@ import mozpack.path as mozpath
from mach.decorators import Command, CommandArgument, SubCommand
from mozbuild.base import MachCommandConditions as conditions
from mozbuild.shellutil import split as shell_split
+from mozfile import which
# Mach's conditions facility doesn't support subcommands. Print a
# deprecation message ourselves instead.
@@ -189,9 +192,13 @@ def create_maven_archive(topobjdir):
gradle_folder = os.path.join(topobjdir, "gradle")
maven_folder = os.path.join(gradle_folder, "maven")
- with tarfile.open(
- os.path.join(gradle_folder, "target.maven.tar.xz"), "w|xz"
- ) as tar:
+ # Create the archive, with no compression: The archive contents are large
+ # files which cannot be significantly compressed; attempting to compress
+ # the archive is usually expensive in time and results in minimal
+ # reduction in size.
+ # Even though the archive is not compressed, use the .xz file extension
+ # so that the taskcluster worker also skips compression.
+ with tarfile.open(os.path.join(gradle_folder, "target.maven.tar.xz"), "w") as tar:
for abs_path in get_maven_archive_paths(maven_folder):
tar.add(
abs_path,
@@ -694,3 +701,290 @@ def emulator(
"Unable to retrieve Android emulator return code.",
)
return 0
+
+
+@SubCommand(
+ "android",
+ "uplift",
+ description="Uplift patch to https://github.com/mozilla-mobile/firefox-android.",
+)
+@CommandArgument(
+ "--revset",
+ "-r",
+ default=None,
+ help="Revision or revisions to uplift. Supported values are the same as what your "
+ "VCS provides. Defaults to the current HEAD revision.",
+)
+@CommandArgument(
+ "firefox_android_clone_dir",
+ help="The directory where your local clone of "
+ "https://github.com/mozilla-mobile/firefox-android repo is.",
+)
+def uplift(
+ command_context,
+ revset,
+ firefox_android_clone_dir,
+):
+ revset = _get_default_revset_if_needed(command_context, revset)
+ major_version = _get_major_version(command_context, revset)
+ uplift_version = major_version - 1
+ bug_number = _get_bug_number(command_context, revset)
+ new_branch_name = (
+ f"{uplift_version}-bug{bug_number}" if bug_number else f"{uplift_version}-nobug"
+ )
+ command_context.log(
+ logging.INFO,
+ "uplift",
+ {
+ "new_branch_name": new_branch_name,
+ "firefox_android_clone_dir": firefox_android_clone_dir,
+ },
+ "Creating branch {new_branch_name} in {firefox_android_clone_dir}...",
+ )
+
+ try:
+ _checkout_new_branch_updated_to_the_latest_remote(
+ command_context, firefox_android_clone_dir, uplift_version, new_branch_name
+ )
+ _export_and_apply_revset(command_context, revset, firefox_android_clone_dir)
+ except subprocess.CalledProcessError:
+ return 1
+
+ command_context.log(
+ logging.INFO,
+ "uplift",
+ {"revset": revset, "firefox_android_clone_dir": firefox_android_clone_dir},
+ "Revision(s) {revset} now applied to {firefox_android_clone_dir}. Please go to "
+ "this directory, inspect the commit(s), and push.",
+ )
+ return 0
+
+
+def _get_default_revset_if_needed(command_context, revset):
+ if revset is not None:
+ return revset
+ if conditions.is_hg(command_context):
+ return "."
+ if conditions.is_git(command_context):
+ return "HEAD"
+ raise NotImplementedError()
+
+
+def _get_major_version(command_context, revset):
+ milestone_txt = _get_milestone_txt(command_context, revset)
+ version = _extract_version_from_milestone_txt(milestone_txt)
+ return _extract_major_version(version)
+
+
+def _get_bug_number(command_context, revision):
+ revision_message = _extract_revision_message(command_context, revision)
+ return _extract_bug_number(revision_message)
+
+
+def _get_milestone_txt(command_context, revset):
+ if conditions.is_hg(command_context):
+ args = [
+ str(which("hg")),
+ "cat",
+ "-r",
+ f"first({revset})",
+ "config/milestone.txt",
+ ]
+ elif conditions.is_git(command_context):
+ revision = revset.split("..")[-1]
+ args = [
+ str(which("git")),
+ "show",
+ f"{revision}:config/milestone.txt",
+ ]
+ else:
+ raise NotImplementedError()
+
+ return subprocess.check_output(args, text=True)
+
+
+def _extract_version_from_milestone_txt(milestone_txt):
+ return milestone_txt.splitlines()[-1]
+
+
+def _extract_major_version(version):
+ return int(version.split(".")[0])
+
+
+def _extract_revision_message(command_context, revision):
+ if conditions.is_hg(command_context):
+ args = [
+ str(which("hg")),
+ "log",
+ "--rev",
+ f"first({revision})",
+ "--template",
+ "{desc}",
+ ]
+ elif conditions.is_git(command_context):
+ args = [
+ str(which("git")),
+ "log",
+ "--format=%s",
+ "-n",
+ "1",
+ revision,
+ ]
+ else:
+ raise NotImplementedError()
+
+ return subprocess.check_output(args, text=True)
+
+
+# Source: https://hg.mozilla.org/hgcustom/version-control-tools/file/cef43d3d676e9f9e9668a50a5d90c012e4025e5b/pylib/mozautomation/mozautomation/commitparser.py#l12
+_BUG_TEMPLATE = re.compile(
+ r"""# bug followed by any sequence of numbers, or
+ # a standalone sequence of numbers
+ (
+ (?:
+ bug |
+ b= |
+ # a sequence of 5+ numbers preceded by whitespace
+ (?=\b\#?\d{5,}) |
+ # numbers at the very beginning
+ ^(?=\d)
+ )
+ (?:\s*\#?)(\d+)(?=\b)
+ )""",
+ re.I | re.X,
+)
+
+
+def _extract_bug_number(revision_message):
+ try:
+ return _BUG_TEMPLATE.match(revision_message).group(2)
+ except AttributeError:
+ return ""
+
+
+_FIREFOX_ANDROID_URL = "https://github.com/mozilla-mobile/firefox-android"
+
+
+def _checkout_new_branch_updated_to_the_latest_remote(
+ command_context, firefox_android_clone_dir, uplift_version, new_branch_name
+):
+ args = [
+ str(which("git")),
+ "fetch",
+ _FIREFOX_ANDROID_URL,
+ f"+releases_v{uplift_version}:{new_branch_name}",
+ ]
+
+ try:
+ subprocess.check_call(args, cwd=firefox_android_clone_dir)
+ except subprocess.CalledProcessError:
+ command_context.log(
+ logging.CRITICAL,
+ "uplift",
+ {
+ "firefox_android_clone_dir": firefox_android_clone_dir,
+ "new_branch_name": new_branch_name,
+ },
+ "Could not fetch branch {new_branch_name}. This may be a network issue. If "
+ "not, please go to {firefox_android_clone_dir}, inspect the branch and "
+ "delete it (with `git branch -D {new_branch_name}`) if you don't have any "
+ "use for it anymore",
+ )
+ raise
+
+ args = [
+ str(which("git")),
+ "checkout",
+ new_branch_name,
+ ]
+ subprocess.check_call(args, cwd=firefox_android_clone_dir)
+
+
+_MERCURIAL_REVISION_TO_GIT_COMMIT_TEMPLATE = """
+From 1234567890abcdef1234567890abcdef12345678 Sat Jan 1 00:00:00 2000
+From: {user}
+Date: {date|rfc822date}
+Subject: {desc}
+
+"""
+
+
+def _export_and_apply_revset(command_context, revset, firefox_android_clone_dir):
+ export_command, import_command = _get_export_import_commands(
+ command_context, revset
+ )
+
+ export_process = subprocess.Popen(export_command, stdout=subprocess.PIPE)
+ try:
+ subprocess.check_call(
+ import_command, stdin=export_process.stdout, cwd=firefox_android_clone_dir
+ )
+ except subprocess.CalledProcessError:
+ command_context.log(
+ logging.CRITICAL,
+ "uplift",
+ {"firefox_android_clone_dir": firefox_android_clone_dir},
+ "Could not run `git am`. Please go to {firefox_android_clone_dir} and fix "
+ "the conflicts. Then run `git am --continue`.",
+ )
+ raise
+ export_process.wait()
+
+
+def _get_export_import_commands(command_context, revset):
+ if conditions.is_hg(command_context):
+ export_command = [
+ str(which("hg")),
+ "log",
+ "--rev",
+ revset,
+ "--patch",
+ "--template",
+ _MERCURIAL_REVISION_TO_GIT_COMMIT_TEMPLATE,
+ "--include",
+ "mobile/android",
+ ]
+ import_command = [str(which("git")), "am", "-p3"]
+ elif conditions.is_git(command_context):
+ export_command = [
+ str(which("git")),
+ "format-patch",
+ "--relative=mobile/android",
+ "--stdout",
+ ]
+ # From the git man page:
+ # > If you want to format only <commit> itself, you can do this with
+ # > git format-patch -1 <commit>."
+ #
+ # https://git-scm.com/docs/git-format-patch#_description
+ if _is_single_revision(command_context, revset):
+ export_command.append("-1")
+
+ export_command.extend(
+ [
+ revset,
+ "--",
+ ]
+ )
+ import_command = [str(which("git")), "am"]
+ else:
+ raise NotImplementedError()
+
+ return export_command, import_command
+
+
+def _is_single_revision(command_context, revset):
+ if conditions.is_git(command_context):
+ command = [
+ str(which("git")),
+ "show",
+ "--no-patch",
+ "--format='%H'",
+ revset,
+ "--",
+ ]
+ else:
+ raise NotImplementedError()
+
+ revisions = subprocess.check_output(command, text=True)
+ return len(revisions.splitlines()) == 1