tor-browser

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

release.py (4498B)


      1 # mypy: disallow-untyped-defs
      2 """Invoke development tasks."""
      3 
      4 import argparse
      5 import os
      6 from pathlib import Path
      7 from subprocess import call
      8 from subprocess import check_call
      9 from subprocess import check_output
     10 
     11 from colorama import Fore
     12 from colorama import init
     13 
     14 
     15 def announce(version: str, template_name: str, doc_version: str) -> None:
     16    """Generates a new release announcement entry in the docs."""
     17    # Get our list of authors
     18    stdout = check_output(["git", "describe", "--abbrev=0", "--tags"], encoding="UTF-8")
     19    last_version = stdout.strip()
     20 
     21    stdout = check_output(
     22        ["git", "log", f"{last_version}..HEAD", "--format=%aN"], encoding="UTF-8"
     23    )
     24 
     25    contributors = {
     26        name
     27        for name in stdout.splitlines()
     28        if not name.endswith("[bot]") and name != "pytest bot"
     29    }
     30 
     31    template_text = (
     32        Path(__file__).parent.joinpath(template_name).read_text(encoding="UTF-8")
     33    )
     34 
     35    contributors_text = "\n".join(f"* {name}" for name in sorted(contributors)) + "\n"
     36    text = template_text.format(
     37        version=version, contributors=contributors_text, doc_version=doc_version
     38    )
     39 
     40    target = Path(__file__).parent.joinpath(f"../doc/en/announce/release-{version}.rst")
     41    target.write_text(text, encoding="UTF-8")
     42    print(f"{Fore.CYAN}[generate.announce] {Fore.RESET}Generated {target.name}")
     43 
     44    # Update index with the new release entry
     45    index_path = Path(__file__).parent.joinpath("../doc/en/announce/index.rst")
     46    lines = index_path.read_text(encoding="UTF-8").splitlines()
     47    indent = "   "
     48    for index, line in enumerate(lines):
     49        if line.startswith(f"{indent}release-"):
     50            new_line = indent + target.stem
     51            if line != new_line:
     52                lines.insert(index, new_line)
     53                index_path.write_text("\n".join(lines) + "\n", encoding="UTF-8")
     54                print(
     55                    f"{Fore.CYAN}[generate.announce] {Fore.RESET}Updated {index_path.name}"
     56                )
     57            else:
     58                print(
     59                    f"{Fore.CYAN}[generate.announce] {Fore.RESET}Skip {index_path.name} (already contains release)"
     60                )
     61            break
     62 
     63    check_call(["git", "add", str(target)])
     64 
     65 
     66 def regen(version: str) -> None:
     67    """Call regendoc tool to update examples and pytest output in the docs."""
     68    print(f"{Fore.CYAN}[generate.regen] {Fore.RESET}Updating docs")
     69    check_call(
     70        ["tox", "-e", "regen"],
     71        env={**os.environ, "SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST": version},
     72    )
     73 
     74 
     75 def fix_formatting() -> None:
     76    """Runs pre-commit in all files to ensure they are formatted correctly"""
     77    print(
     78        f"{Fore.CYAN}[generate.fix linting] {Fore.RESET}Fixing formatting using pre-commit"
     79    )
     80    call(["pre-commit", "run", "--all-files"])
     81 
     82 
     83 def check_links() -> None:
     84    """Runs sphinx-build to check links"""
     85    print(f"{Fore.CYAN}[generate.check_links] {Fore.RESET}Checking links")
     86    check_call(["tox", "-e", "docs-checklinks"])
     87 
     88 
     89 def pre_release(
     90    version: str, template_name: str, doc_version: str, *, skip_check_links: bool
     91 ) -> None:
     92    """Generates new docs, release announcements and creates a local tag."""
     93    announce(version, template_name, doc_version)
     94    regen(version)
     95    changelog(version, write_out=True)
     96    fix_formatting()
     97    if not skip_check_links:
     98        check_links()
     99 
    100    msg = f"Prepare release version {version}"
    101    check_call(["git", "commit", "-a", "-m", msg])
    102 
    103    print()
    104    print(f"{Fore.CYAN}[generate.pre_release] {Fore.GREEN}All done!")
    105    print()
    106    print("Please push your branch and open a PR.")
    107 
    108 
    109 def changelog(version: str, write_out: bool = False) -> None:
    110    addopts = [] if write_out else ["--draft"]
    111    check_call(["towncrier", "--yes", "--version", version, *addopts])
    112 
    113 
    114 def main() -> None:
    115    init(autoreset=True)
    116    parser = argparse.ArgumentParser()
    117    parser.add_argument("version", help="Release version")
    118    parser.add_argument(
    119        "template_name", help="Name of template file to use for release announcement"
    120    )
    121    parser.add_argument(
    122        "doc_version", help="For prereleases, the version to link to in the docs"
    123    )
    124    parser.add_argument("--skip-check-links", action="store_true", default=False)
    125    options = parser.parse_args()
    126    pre_release(
    127        options.version,
    128        options.template_name,
    129        options.doc_version,
    130        skip_check_links=options.skip_check_links,
    131    )
    132 
    133 
    134 if __name__ == "__main__":
    135    main()