2020-03-05 01:48:50 +03:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
# Purpose: Prepare an Application-Services release
|
|
|
|
# Dependencies: yaml
|
|
|
|
# Usage: ./automation/prepare-release.py minor
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
from datetime import datetime
|
|
|
|
import subprocess
|
|
|
|
import re
|
|
|
|
import webbrowser
|
|
|
|
import yaml
|
|
|
|
|
2020-03-06 20:57:10 +03:00
|
|
|
from shared import step_msg, fatal_err, run_cmd_checked, ensure_working_tree_clean
|
|
|
|
|
2020-03-05 01:48:50 +03:00
|
|
|
parser = argparse.ArgumentParser(description="Prepares an application-services release "
|
|
|
|
"(increment versions, write changelog, send PR to GitHub).")
|
|
|
|
parser.add_argument("release_type",
|
|
|
|
choices=["major", "minor", "patch"],
|
|
|
|
help="The release type to be done. See https://semver.org/ for guidance.")
|
|
|
|
parser.add_argument("--base-branch",
|
2020-06-26 02:21:01 +03:00
|
|
|
default="main",
|
|
|
|
help="The branch to make a release from. Default is main.")
|
2020-03-05 01:48:50 +03:00
|
|
|
parser.add_argument("--remote",
|
|
|
|
default="origin",
|
|
|
|
help="The remote name that corresponds to the Application Services main repository.")
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
base_branch = args.base_branch
|
|
|
|
remote = args.remote
|
|
|
|
release_type = args.release_type
|
|
|
|
|
|
|
|
# Constants
|
|
|
|
BUILDCONFIG_FILE = ".buildconfig-android.yml"
|
|
|
|
BUILDCONFIG_VERSION_FIELD = "libraryVersion"
|
|
|
|
UNRELEASED_CHANGES_FILE = "CHANGES_UNRELEASED.md"
|
|
|
|
CHANGELOG_FILE = "CHANGELOG.md"
|
|
|
|
|
2020-04-14 09:44:25 +03:00
|
|
|
# 1. Create a new branch based on the branch we want to release from.
|
|
|
|
|
|
|
|
ensure_working_tree_clean()
|
|
|
|
|
2021-07-06 20:52:05 +03:00
|
|
|
step_msg(f"Calculating remote owner {remote}")
|
|
|
|
result = run_cmd_checked(["git", "remote", "get-url", remote], stdout=subprocess.PIPE)
|
|
|
|
repo_url = result.stdout.decode('utf8').strip()
|
|
|
|
repo_owner_patterns = [
|
|
|
|
r"git@github.com:(.*?)/application-services.git",
|
|
|
|
r'https://github.com/(.*?)/application-services.git',
|
|
|
|
]
|
|
|
|
for pattern in repo_owner_patterns:
|
|
|
|
match = re.match(pattern, repo_url)
|
|
|
|
if match:
|
|
|
|
repo_owner = match.group(1)
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
fatal_err(f"Error parsing remote URL: {repo_url}")
|
|
|
|
print(f"Parsed github owner {repo_owner} for {repo_url}")
|
|
|
|
|
2020-04-14 09:44:25 +03:00
|
|
|
step_msg(f"Updating remote {remote}")
|
|
|
|
run_cmd_checked(["git", "remote", "update", remote])
|
|
|
|
|
|
|
|
temp_branch = f"prepare-release-from-{base_branch}"
|
|
|
|
step_msg(f"Checking out candidate release branch from {base_branch}")
|
|
|
|
run_cmd_checked(["git", "checkout", "-b", temp_branch, "--no-track", f"{remote}/{base_branch}"])
|
|
|
|
|
|
|
|
# 2. Calculate new version number based on what's in the base branch.
|
2020-03-05 01:48:50 +03:00
|
|
|
|
|
|
|
with open(BUILDCONFIG_FILE, "r") as stream:
|
|
|
|
buildConfig = yaml.safe_load(stream)
|
|
|
|
|
|
|
|
cur_version = buildConfig[BUILDCONFIG_VERSION_FIELD]
|
|
|
|
cur_version_full = f"v{cur_version}"
|
|
|
|
[major, minor, patch] = map(lambda n: int(n), cur_version.split(".", 3))
|
|
|
|
|
|
|
|
if release_type == "major":
|
|
|
|
major += 1
|
2020-03-10 22:57:03 +03:00
|
|
|
minor = 0
|
|
|
|
patch = 0
|
2020-03-05 01:48:50 +03:00
|
|
|
elif release_type == "minor":
|
|
|
|
minor += 1
|
2020-03-10 22:57:03 +03:00
|
|
|
patch = 0
|
2020-03-05 01:48:50 +03:00
|
|
|
elif release_type == "patch":
|
|
|
|
patch += 1
|
|
|
|
|
|
|
|
next_version = f"{major}.{minor}.{patch}"
|
|
|
|
next_version_full = f"v{next_version}"
|
|
|
|
|
|
|
|
release_branch = f"cut-{next_version_full}"
|
2020-04-14 09:44:25 +03:00
|
|
|
step_msg(f"Preparing release branch {release_branch} for {next_version_full}")
|
|
|
|
run_cmd_checked(["git", "branch", "-M", temp_branch, release_branch])
|
2020-03-05 01:48:50 +03:00
|
|
|
|
|
|
|
# 3. Bump YML version
|
|
|
|
|
|
|
|
step_msg(f"Bumping version in {BUILDCONFIG_FILE}")
|
|
|
|
buildConfig[BUILDCONFIG_VERSION_FIELD] = next_version
|
|
|
|
|
|
|
|
with open(BUILDCONFIG_FILE, "w") as stream:
|
|
|
|
yaml.dump(buildConfig, stream, sort_keys=False)
|
|
|
|
|
|
|
|
# 4. Process changelog files
|
|
|
|
|
|
|
|
with open(UNRELEASED_CHANGES_FILE, "r") as stream:
|
|
|
|
unreleased_changes = stream.read()
|
|
|
|
|
2021-05-25 19:08:22 +03:00
|
|
|
# Copy the text after the end of the header comment section in the unreleased changes file.
|
|
|
|
to_find = re.escape("-->")
|
|
|
|
|
|
|
|
changes = re.split(f"^{to_find}$", unreleased_changes, flags=re.MULTILINE)[1].strip()
|
2020-03-05 01:48:50 +03:00
|
|
|
|
|
|
|
with open(CHANGELOG_FILE, "r") as stream:
|
|
|
|
changelog = stream.read()
|
|
|
|
|
|
|
|
today_date = datetime.today().strftime("%Y-%m-%d")
|
|
|
|
|
|
|
|
new_changelog = f"""# {next_version_full} (_{today_date}_)
|
|
|
|
|
|
|
|
[Full Changelog](https://github.com/mozilla/application-services/compare/{cur_version_full}...{next_version_full})
|
|
|
|
|
|
|
|
{changes}
|
|
|
|
|
|
|
|
{changelog}"""
|
|
|
|
|
|
|
|
new_changes_unreleased = f"""**See [the release process docs](docs/howtos/cut-a-new-release.md) for the steps to take when cutting a new release.**
|
|
|
|
|
|
|
|
# Unreleased Changes
|
|
|
|
|
2020-06-26 02:21:01 +03:00
|
|
|
[Full Changelog](https://github.com/mozilla/application-services/compare/{next_version_full}...main)
|
2021-05-25 19:08:22 +03:00
|
|
|
|
|
|
|
<!-- WARNING: New entries should be added below this comment to ensure the `./automation/prepare-release.py` script works as expected.
|
|
|
|
|
|
|
|
Use the template below to make assigning a version number during the release cutting process easier.
|
|
|
|
|
|
|
|
## [Component Name]
|
|
|
|
|
|
|
|
### ⚠️ Breaking Changes ⚠️
|
|
|
|
- Description of the change with a link to the pull request ([#0000](https://github.com/mozilla/application-services/pull/0000))
|
2023-01-04 23:32:39 +03:00
|
|
|
### 🦊 What's Changed 🦊
|
2021-05-25 19:08:22 +03:00
|
|
|
- Description of the change with a link to the pull request ([#0000](https://github.com/mozilla/application-services/pull/0000))
|
2023-01-04 23:32:39 +03:00
|
|
|
### ✨ What's New ✨
|
2021-05-25 19:08:22 +03:00
|
|
|
- Description of the change with a link to the pull request ([#0000](https://github.com/mozilla/application-services/pull/0000))
|
|
|
|
|
|
|
|
-->
|
2020-03-05 01:48:50 +03:00
|
|
|
"""
|
|
|
|
|
|
|
|
with open(CHANGELOG_FILE, "w") as stream:
|
|
|
|
stream.write(new_changelog)
|
|
|
|
|
|
|
|
with open(UNRELEASED_CHANGES_FILE, "w") as stream:
|
|
|
|
stream.write(new_changes_unreleased)
|
|
|
|
|
|
|
|
# 5. Create a commit and send a PR
|
|
|
|
|
|
|
|
step_msg(f"Creating a commit with the changes")
|
2020-03-06 20:57:10 +03:00
|
|
|
run_cmd_checked(["git", "add", "-A"]) # We can use -A since we checked the working dir is clean.
|
|
|
|
run_cmd_checked(["git", "commit", "-m", f"Cut release {next_version_full}"])
|
2020-03-05 01:48:50 +03:00
|
|
|
|
2020-03-06 20:57:10 +03:00
|
|
|
response = input("Great! Would you like to push and open a pull-request? ([Y]/N)").lower()
|
2020-03-05 01:48:50 +03:00
|
|
|
if response != "y" and response != "" and response != "yes":
|
|
|
|
exit(0)
|
2020-03-06 20:57:10 +03:00
|
|
|
run_cmd_checked(["git", "push", remote, release_branch])
|
2021-07-06 20:52:05 +03:00
|
|
|
if repo_owner == 'mozilla':
|
|
|
|
webbrowser.open_new_tab(f"https://github.com/mozilla/application-services/compare/{base_branch}...{release_branch}")
|
|
|
|
else:
|
|
|
|
webbrowser.open_new_tab(f"https://github.com/mozilla/application-services/compare/{base_branch}...{repo_owner}:{release_branch}")
|