#!/usr/bin/env python3 # 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 os import sys import shutil import re import tempfile from optparse import OptionParser from subprocess import check_call from subprocess import check_output
def inplace_replace(replacements=[], filename=""): for r in replacements: ifnot isinstance(r, Replacement): raise TypeError("Expecting a list of Replacement objects")
with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file: with open(filename) as in_file: for line in in_file: for r in replacements:
line = r.replace(line)
tmp_file.write(line)
tmp_file.flush()
def set_root_ca_version(args):
ensure_arguments_count(args, 2, "major_version minor_version")
major = args[0].strip()
minor = args[1].strip()
version = major + "." + minor
if old_major < new_major or (old_major == new_major and old_minor < new_minor):
print("You're increasing the minor (or major) version:")
print("- erasing ABI comparison expectations")
new_branch = "NSS_" + str(old_major) + "_" + str(old_minor) + "_BRANCH"
print( "- setting reference branch to the branch of the previous version: "
+ new_branch
) with open(abi_base_version_file, "w") as abi_base:
abi_base.write("%s\n" % new_branch) for report_file in abi_report_files: with open(report_file, "w") as report_file_handle:
report_file_handle.truncate()
major, minor, patch = parse_version_string(version_string) if patch isnotNone:
exit_with_failure( "make_release_branch expects a minor version (e.g., '3.117'), not a patch version."
)
version = f"{major}.{minor}"
branch_name = f"NSS_{major}_{minor}_BRANCH"
tag_name = f"NSS_{major}_{minor}_BETA1"
# Step 1: Update local repo
print("Step 1: Updating local repository...")
check_call_noisy(["hg", "pull"])
check_call_noisy(["hg", "checkout", "default"])
print_separator()
print("Step 2: Checking working directory is clean")
hg_status = check_output(["hg", "status"]).decode("utf-8").strip() if hg_status:
print()
print("ERROR: Working directory is not clean")
print(hg_status)
print()
exit_with_failure( "Please commit or revert changes then run this command again. You can reset your working directory with 'hg update -C' and 'hg purge if you want to discard all local changes."
)
branches = check_output(["hg", "branches"]).decode("utf-8").strip() if branch_name in branches:
exit_with_failure(f"Branch {branch_name} already exists.")
print_separator()
# Step 2: Verify version numbers are correct
print("Step 2: Verifying version numbers are correct...")
set_version_to_minor_release([major, minor])
print("=" * 70)
set_beta_status()
print("=" * 70) # Check if there are any uncommitted changes
hg_status = check_output(["hg", "status"]).decode("utf-8").strip() if hg_status:
print()
print("ERROR: Version numbers are not correctly set")
print()
print()
exit_with_failure( "Please check the correct version to freeze, or update the version numbers then run this command again."
)
print("Version numbers verified - no changes needed.")
print_separator()
# Step 6: Prompt user and push if confirmed
response = input("Push this branch and tag to the NSS repository? [yN]: ") if"y"in response.lower():
print("Pushing branch and tag...")
check_call_noisy(["hg", "push", "--new-branch", remote])
print_separator()
print("SUCCESS: Branch and tag have been pushed!")
print_separator()
print()
print("NEXT STEPS:")
print(
f"1. Wait for the changes to sync to Github: https://github.com/mozilla/nss/tree/{branch_name}"
)
print("2. In your mozilla-unified repository, run:")
print(f" ./mach nss-uplift {tag_name}")
print() else:
print("Branch and tag have NOT been pushed to the repository.")
print("The local branch and tag remain in your working directory.")
print_separator()
def parse_version_string(version_string): """Parse a version string like '3.117' or '3.117.1' and return (major, minor, patch)
For versions like '3.117', patch will be None.
Returns: tuple of (major, minor, patch) where patch can be None """
parts = version_string.split(".") if len(parts) < 2:
exit_with_failure(
f"Invalid version string '{version_string}'. Expected format: 'major.minor' or 'major.minor.patch'"
)
major = parts[0].strip()
minor = parts[1].strip()
patch = parts[2].strip() if len(parts) >= 3elseNone
# Validate that they're numbers try:
int(major)
int(minor) if patch isnotNone:
int(patch) except ValueError:
exit_with_failure(
f"Invalid version string '{version_string}'. Version components must be numbers."
)
return major, minor, patch
def version_string_to_RTM_tag(version_string):
parts = version_string.split(".") return"NSS_" + "_".join(parts) + "_RTM"
version = args[0].strip()
this_tag = args[1].strip() # Typically going to be .
version_underscore = version_string_to_underscore(version)
prev_tag = version_string_to_RTM_tag(args[2].strip())
# Get the NSPR version
nspr_version = (
check_output(
["hg", "cat", "-r", this_tag, "automation/release/nspr-version.txt"]
)
.decode("utf-8")
.split("\n")[0]
.strip()
)
# Get the current date from datetime import datetime
# Get the list of bugs from hg log # Get log entries between previous tag and current HEAD
command = [ "hg", "log", "-r",
f"{prev_tag}:{this_tag}", "--template", "{desc|firstline}\\n",
]
log_output = check_output(command).decode("utf-8")
# Extract bug numbers and descriptions
bug_lines = [] for line in reversed(log_output.split("\n")): if"Bug"in line or"bug"in line:
line = line.strip()
line = line.split("r=")[0].strip()
# Match patterns like "Bug 1234567 Something" and convert to "Bug 1234567 - Something"
line = re.sub(
r"(Bug\s+\d+)\s+([^-])", r"\1 - \2", line, flags=re.IGNORECASE
)
# Add a full stop at the end if there isn't one if line:
line = line.rstrip(",")
if line andnot line.endswith("."):
line = line + "."
if line and line notin bug_lines:
bug_lines.append(line)
changes_text = "\n".join([f" - {line}"for line in bug_lines])
# Create the release notes content
rst_content = f""".. _mozilla_projects_nss_nss_{version_underscore}_release_notes:
# Read all release note files from doc/rst/releases/
release_dir = "doc/rst/releases" ifnot os.path.exists(release_dir):
exit_with_failure(f"Release notes directory not found: {release_dir}")
# Get all nss_*.rst files (excluding index.rst)
release_files = [] for filename in os.listdir(release_dir): if (
filename.startswith("nss_") and filename.endswith(".rst") and filename != "index.rst"
):
release_files.append(filename)
# Sort release files in reverse order (newest first) # Extract version numbers for proper sorting def version_key(filename): # Extract version parts from filename like nss_3_116.rst
parts = filename.replace("nss_", "").replace(".rst", "").split("_") # Convert to integers for proper numerical sorting return [int(p) for p in parts]
release_files.sort(key=version_key, reverse=True)
# Build the toctree content
toctree_lines = "\n".join([f" {f}"for f in release_files])
# Create the index.rst content
index_content = f""".. _mozilla_projects_nss_releases:
Release Notes
=============
.. toctree::
:maxdepth: 0
:glob:
:hidden:
{toctree_lines}
.. note::
**NSS {latest_version}** is the latest version of NSS.
Complete release notes are available here: :ref:`mozilla_projects_nss_nss_{latest_underscore}_release_notes`
**NSS {esr_version} (ESR)** is the latest ESR version of NSS.
Complete release notes are available here: :ref:`mozilla_projects_nss_nss_{esr_underscore}_release_notes`
"""
index_file = os.path.join(release_dir, "index.rst") with open(index_file, "w") as f:
f.write(index_content)
# Step 1: Update local repo
print("Step 1: Updating local repository...")
check_call_noisy(["hg", "pull"])
print_separator()
# Step 2: Checking working directory is clean
print("Step 2: Checking working directory is clean...")
hg_status = check_output(["hg", "status"]).decode("utf-8").strip() if hg_status:
print()
print("ERROR: Working directory is not clean")
print(hg_status)
print()
exit_with_failure( "Please commit or revert changes then run this command again. You can reset your working directory with 'hg update -C' and 'hg purge if you want to discard all local changes."
)
print_separator()
# Step 3: Make sure we're on the appropriate branch
print(f"Step 3: Checking out branch {branch_name}...") try:
check_call_noisy(["hg", "checkout", branch_name]) except Exception as e:
exit_with_failure(f"Failed to checkout branch {branch_name}. Does it exist?")
print_separator()
# Step 4: Check for any existing commits or tags
print("Step 4: Checking for existing release commits or tags...")
# Check if RTM tag already exists
tags_output = check_output(["hg", "tags"]).decode("utf-8") if rtm_tag in tags_output:
exit_with_failure(
f"Tag {rtm_tag} already exists. Has this release already been made?"
)
# Check for recent commits with the same commit messages we're about to make
version_commit_message = f"Set version numbers to {version} final"
release_notes_commit_message = f"Release notes for NSS {version}"
if version_commit_message in recent_log:
exit_with_failure(
f"Found recent commit with message '{version_commit_message}'. Has this release already been started?"
)
if release_notes_commit_message in recent_log:
exit_with_failure(
f"Found recent commit with message '{release_notes_commit_message}'. Has this release already been started?"
)
print("No existing release commits or tags found.")
print_separator()
# Step 5: Update the NSS version numbers (remove beta)
print("Step 5: Removing beta status from version numbers...") if patch:
set_version_to_patch_release([major, minor, patch]) else:
set_version_to_minor_release([major, minor])
remove_beta_status()
print_separator()
# Step 6: Commit the change
print("Step 6: Committing version number changes...")
check_call_noisy(["hg", "commit", "-m", version_commit_message])
print_separator()
# Step 8: Generate new release note index
print("Step 8: Generating release notes index...")
generate_release_notes_index([version, esr_version])
print_separator()
input( "Are you making an ESR release? If so, please manually edit doc/rst/releases/index.rst to adjust the ESR / main version note. Press enter when done."
)
# Step 12: Check cf_status_nss on Bugzilla
print("Step 12: Checking cf_status_nss on Bugzilla...")
cf_status_script = os.path.join(
os.path.dirname(__file__), "bugzilla_cf_status_nss.py"
)
check_call_noisy([sys.executable, cf_status_script, version])
input("Review the cf_status_nss report above. Press Enter to continue.")
print_separator()
# Step 13: Push changes
response = input("Push these changes to the NSS repository? [yN]: ") if"y"in response.lower():
print("Pushing changes to default branch...")
check_call_noisy(["hg", "push", "-b", "default", remote])
print(f"Pushing changes to {branch_name} branch...")
check_call_noisy(["hg", "push", "-b", branch_name, remote])
print_separator()
print("SUCCESS: NSS release process completed!")
print_separator()
print()
print("NEXT STEPS:")
print(f"1. Wait for the changes to sync to Github")
print("2. In your mozilla-unified repository, run:")
print(f" ./mach nss-uplift {rtm_tag}")
print() else:
print("Changes have NOT been pushed to the repository.")
print("The local commits remain in your working directory.")
print_separator()
def create_nss_release_archive(args):
ensure_arguments_count(args, 2, "nss_release_version path_to_stage_directory")
nssrel = args[0].strip() # e.g. 3.19.3
stagedir = args[1].strip() # e.g. ../stage
# Determine which tar command to use (prefer gtar if available)
tar_cmd = "gtar" try:
check_call(
["which", "gtar"],
stdout=open(os.devnull, "w"),
stderr=open(os.devnull, "w"),
) except:
tar_cmd = "tar"
# Generate the release tag from the version
nssreltag = version_string_to_RTM_tag(nssrel)
if"y"notin input("Upload release tarball?[yN]"):
print("Release tarballs have NOT been uploaded")
exit(0)
os.chdir("../..")
gcp_proj = "moz-fx-productdelivery-pr-38b5"
check_call_noisy(["gcloud", "auth", "login"])
check_call_noisy(
[ "gcloud", "--project",
gcp_proj,
f"--impersonate-service-account=nss-team-prod@{gcp_proj}.iam.gserviceaccount.com", "storage", "cp", "--recursive", "--no-clobber",
nssreltag,
f"gs://{gcp_proj}-productdelivery/pub/security/nss/releases/",
]
)
print_separator()
print(
f"Release tarballs have been uploaded to Google Cloud Storage. You can find them at https://ftp.mozilla.org/pub/security/nss/releases/{nssreltag}/"
)
print_separator()
try:
options, args = o.parse_args()
action = args[0]
action_args = args[1:] # Get all arguments after the action except IndexError:
o.print_help()
sys.exit(2)
if action in ("remove_beta"):
remove_beta_status()
elif action in ("set_beta"):
set_beta_status()
elif action in ("print_library_versions"):
print_library_versions()
elif action in ("print_root_ca_version"):
print_root_ca_version()
elif action in ("set_root_ca_version"):
set_root_ca_version(action_args)
# x.y version number - 2 parameters elif action in ("set_version_to_minor_release"):
set_version_to_minor_release(action_args)
# x.y.z version number - 3 parameters elif action in ("set_version_to_patch_release"):
set_version_to_patch_release(action_args)
# change the release candidate number, usually increased by one, # usually if previous release candiate had a bug # 1 parameter elif action in ("set_release_candidate_number"):
set_release_candidate_number(action_args)
# use the build/release candiate number in the identifying version number # 4 parameters elif action in ("set_4_digit_release_number"):
set_4_digit_release_number(action_args)
# create a freeze branch and beta tag for a new release # 2 parameters elif action in ("make_release_branch"):
make_release_branch(action_args)
elif action in ("create_nss_release_archive"):
create_nss_release_archive(action_args)
elif action in ("generate_release_note"):
print(generate_release_note(action_args))
elif action in ("generate_release_notes_index"):
generate_release_notes_index(action_args)
elif action in ("release_nss"):
release_nss(action_args)
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.