# 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 https://mozilla.org/MPL/2.0/.
# Common code used by the automation python scripts.
import os
import subprocess
import sys
from pathlib
import Path
def step_msg(msg):
print(f
"> \033[34m{msg}\033[0m")
def fatal_err(msg):
print(f
"\033[31mError: {msg}\033[0m")
sys.exit(
1)
def run_cmd_checked(*args, **kwargs):
"""Run a command, throwing an exception if it exits with non-zero status."""
kwargs[
"check"] =
True
return subprocess.run(*args, **kwargs)
# noqa: PLW1510
def check_output(*args, **kwargs):
"""Run a command, throwing an exception if it exits with non-zero status."""
return subprocess.check_output(*args, **kwargs, encoding=
"utf8")
def ensure_working_tree_clean():
"""Error out if there are un-committed or staged files in the working tree."""
if run_cmd_checked([
"git",
"status",
"--porcelain"], capture_output=
True).stdout:
fatal_err(
"The working tree has un-committed or staged files.")
def find_app_services_root():
"""Find the absolute path of the Application services repository root."""
cur_dir = Path(__file__).parent
while not Path(cur_dir,
"LICENSE").exists():
cur_dir = cur_dir.parent
return cur_dir.absolute()
def get_moz_remote():
"""
Get the name of the remote
for the official mozilla application-services repo
"""
for line
in check_output([
"git",
"remote",
"-v"]).splitlines():
split = line.split()
if (
len(split) ==
3
and split[
1] ==
"git@github.com:mozilla/application-services.git"
and split[
2] ==
"(push)"
):
return split[
0]
fatal_err(
"Can't find remote origin for git@github.com:mozilla/application-services.git"
)
def set_gradle_substitution_path(project_dir, name, value):
"""Set a substitution path property in a gradle `local.properties` file.
Given the path to a gradle project directory, this helper will set the named
property to the given path value
in that directory
's `local.properties` file.
If the named property already exists
with the correct value then it will
silently succeed;
if the named property already exists
with a different value
then it will noisily fail.
"""
project_dir = Path(project_dir).resolve()
properties_file = project_dir /
"local.properties"
step_msg(f
"Configuring local publication in project at {properties_file}")
name_eq = name +
"="
abs_value = Path(value).resolve()
# Check if the named property already exists.
if properties_file.exists():
with properties_file.open()
as f:
for ln
in f:
# Not exactly a thorough parser, but should be good enough...
if ln.startswith(name_eq):
cur_value = ln[len(name_eq) :].strip()
if Path(project_dir, cur_value).resolve() != abs_value:
fatal_err(
f
"Conflicting property {name}={cur_value} (not {abs_value})"
)
return
# The file does not contain the required property, append it.
# Note that the project probably expects a path relative to the project root.
ancestor = Path(os.path.commonpath([project_dir, abs_value]))
relpath = Path(
".")
for _
in project_dir.parts[len(ancestor.parts) :]:
relpath /=
".."
for nm
in abs_value.parts[len(ancestor.parts) :]:
relpath /= nm
step_msg(f
"Setting relative path from {project_dir} to {abs_value} as {relpath}")
with properties_file.open(
"a")
as f:
f.write(f
"{name}={relpath}\n")
class RefNames:
"""
Contains the branch
and tag names we use
for automation.
Attributes:
main -- where new development happens
release_branch -- contains the code
for a given major release
release_pr_branch -- Used
for PRs against release_branch
for a new version
start_release_pr_branch -- Used
for PRs against main to start a new major release
"""
def __init__(self, major_version_number, minor_version_number):
major_version_number = int(major_version_number)
minor_version_number = int(minor_version_number)
self.main =
"main"
self.release = f
"release-v{major_version_number}"
self.release_pr = f
"cut-v{major_version_number}.{minor_version_number}"
self.start_release_pr = f
"start-release-v{major_version_number+1}"
self.version_tag = f
"v{major_version_number}.{minor_version_number}"
if minor_version_number ==
0:
self.previous_version_tag = f
"v{major_version_number-1}.0"
else:
self.previous_version_tag = (
f
"v{major_version_number}.{minor_version_number-1}"
)