tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

generate_release_email.py (5751B)


      1 #!/usr/bin/env python3
      2 # This Source Code Form is subject to the terms of the Mozilla Public
      3 # License, v. 2.0. If a copy of the MPL was not distributed with this
      4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      5 
      6 """
      7 Generate NSS release email text based on version number.
      8 
      9 Usage: python3 generate_release_email.py <version> <previous_version> [output_file]
     10 
     11 Example:
     12  python3 generate_release_email.py 3.118 3.117
     13  python3 generate_release_email.py 3.118.1 3.118 release_email_3.118.1.txt
     14 """
     15 
     16 import os
     17 import re
     18 import sys
     19 from datetime import datetime
     20 from subprocess import check_output
     21 
     22 
     23 def exit_with_failure(message):
     24    """Exit the script with an error message."""
     25    print(f"ERROR: {message}", file=sys.stderr)
     26    sys.exit(1)
     27 
     28 
     29 def version_string_to_underscore(version_string):
     30    """Convert version string like '3.118' to '3_118'."""
     31    return version_string.replace('.', '_')
     32 
     33 
     34 def version_string_to_RTM_tag(version_string):
     35    """Convert version string like '3.118' to 'NSS_3_118_RTM'."""
     36    parts = version_string.split('.')
     37    return "NSS_" + "_".join(parts) + "_RTM"
     38 
     39 
     40 def get_nspr_version():
     41    """Read the NSPR version from automation/release/nspr-version.txt."""
     42    nspr_version_file = "automation/release/nspr-version.txt"
     43    try:
     44        with open(nspr_version_file, 'r') as f:
     45            return f.readline().strip()
     46    except FileNotFoundError:
     47        exit_with_failure(f"Could not find {nspr_version_file}. Are you running from the NSS root directory?")
     48 
     49 
     50 def get_changes_from_hg(current_tag, previous_tag):
     51    """Extract bug changes from Mercurial log between two tags."""
     52    try:
     53        # Get log entries between previous tag and current tag
     54        command = ["hg", "log", "-r", f"{previous_tag}:{current_tag}", "--template", "{desc|firstline}\\n"]
     55        log_output = check_output(command).decode('utf-8')
     56    except Exception as e:
     57        exit_with_failure(f"Failed to get hg log: {e}")
     58 
     59    # Extract bug numbers and descriptions
     60    bug_lines = []
     61    for line in reversed(log_output.split('\n')):
     62        if 'Bug' in line or 'bug' in line:
     63            line = line.strip()
     64            # Remove reviewer information
     65            line = line.split("r=")[0].strip()
     66 
     67            # Match patterns like "Bug 1234567 Something" and convert to "Bug 1234567 - Something"
     68            line = re.sub(r'(Bug\s+\d+)\s+([^-])', r'\1 - \2', line, flags=re.IGNORECASE)
     69 
     70            # Clean up punctuation
     71            if line:
     72                line = line.rstrip(',')
     73 
     74            # Add a full stop at the end if there isn't one
     75            if line and not line.endswith('.'):
     76                line = line + '.'
     77 
     78            if line and line not in bug_lines:
     79                bug_lines.append(line)
     80 
     81    return bug_lines
     82 
     83 
     84 def generate_email_content(version, nspr_version, bug_lines, release_date):
     85    """Generate the email content for the release announcement."""
     86    version_underscore = version_string_to_underscore(version)
     87    changes_text = "\n\n".join([f"    {line}" for line in bug_lines])
     88 
     89    email_content = f"""Network Security Services (NSS) {version} was released on {release_date}.
     90 
     91 
     92 
     93 The HG tag is NSS_{version_underscore}_RTM. This version of NSS requires NSPR {nspr_version} or newer. The latest version of NSPR is {nspr_version}.
     94 
     95 NSS {version} source distributions are available on ftp.mozilla.org for secure HTTPS download:
     96 
     97 <https://ftp.mozilla.org/pub/security/nss/releases/NSS_{version_underscore}_RTM/src/>
     98 
     99 Changes:
    100 
    101 {changes_text}
    102 
    103 NSS {version} shared libraries are backwards-compatible with all older NSS 3.x shared libraries. A program linked with older NSS 3.x shared libraries will work with this new version of the shared libraries without recompiling or relinking. Furthermore, applications that restrict their use of NSS APIs to the functions listed in NSS Public Functions will remain compatible with future versions of the NSS shared libraries.
    104 
    105 Bugs discovered should be reported by filing a bug report at <https://bugzilla.mozilla.org/enter_bug.cgi?product=NSS>
    106 
    107 Release notes are available at <https://firefox-source-docs.mozilla.org/security/nss/releases/index.html>.
    108 """
    109    return email_content
    110 
    111 
    112 def main():
    113    if len(sys.argv) < 3:
    114        print(__doc__)
    115        sys.exit(1)
    116 
    117    version = sys.argv[1].strip()
    118    previous_version = sys.argv[2].strip()
    119 
    120    # Determine output file (optional)
    121    output_file = None
    122    if len(sys.argv) >= 4:
    123        output_file = sys.argv[3].strip()
    124 
    125    # Get current date
    126    current_date = datetime.now().strftime("%-d %B %Y")
    127 
    128    # Get NSPR version
    129    nspr_version = get_nspr_version()
    130 
    131    # Convert versions to tags
    132    current_tag = version_string_to_RTM_tag(version)
    133    previous_tag = version_string_to_RTM_tag(previous_version)
    134 
    135    print(f"Generating release email for NSS {version}")
    136    print(f"Previous version: {previous_version}")
    137    print(f"Current tag: {current_tag}")
    138    print(f"Previous tag: {previous_tag}")
    139    print(f"NSPR version: {nspr_version}")
    140    print(f"Release date: {current_date}")
    141    print()
    142 
    143    # Get changes from Mercurial
    144    print("Extracting changes from Mercurial...")
    145    bug_lines = get_changes_from_hg(current_tag, previous_tag)
    146    print(f"Found {len(bug_lines)} bug entries")
    147    print()
    148 
    149    # Generate email content
    150    email_content = generate_email_content(version, nspr_version, bug_lines, current_date)
    151 
    152    # Write to file if specified, otherwise print to stdout
    153    if output_file:
    154        with open(output_file, 'w') as f:
    155            f.write(email_content)
    156        print(f"Release email written to: {output_file}")
    157        print()
    158 
    159    print("=" * 70)
    160    print("Email Content:")
    161    print("=" * 70)
    162    print(email_content)
    163 
    164 
    165 if __name__ == "__main__":
    166    main()