...

Text file src/github.com/emissary-ingress/emissary/v3/releng/release-go-changelog-update

Documentation: github.com/emissary-ingress/emissary/v3/releng

     1#!/usr/bin/env python3
     2"""Updates the dates in the changelog to today's date, then
     3launch the `git citool` GUI to create a commit of it, and uses
     4the `gh` tool to file a pull request.
     5To be run after the GA version of emissary has been tagged.
     6change the appropriate files, then launch the `git citool` GUI to
     7create a commit of it.
     8"""
     9
    10from os import getenv
    11import datetime
    12import fileinput
    13import os
    14import re
    15import sys
    16from contextlib import contextmanager
    17from typing import Generator
    18from shutil import which
    19
    20from lib import base_version, build_version, git_add, git_check_clean, re_ga, re_ea, vX, vY, get_gh_repo
    21from lib.uiutil import Checker, CheckResult, run
    22from lib.uiutil import run_txtcapture as run_capture
    23from lib import ansiterm
    24from lib.gitutil import has_open_pr
    25
    26
    27def main(next_ver: str, today: datetime.date, quiet: bool=False, commit: bool = True) -> int:
    28    """This edits several files (the Git directory "tree"), then launches
    29    the `git citool` GUI to commit them.  This _should_ be an utterly
    30    trivial and readable list of
    31        for line in fileinput.FileInput("FILENAME", inplace=True):
    32            # edit 'line' as appropriate
    33            sys.stdout.write(line)
    34        git_add("FILENAME")
    35    blocks.  However, the block to edit the releaseNotes.yml file is unfortunately
    36    more complex, given the structuring of YAML... maybe line-oriented processing
    37    wasn't the best choice for that file, but parsing and reconstructing at as YAML
    38    probably isn't great either. Hmmmm.
    39    """
    40
    41    if not quiet:
    42        print()
    43        print(f'Doing basic updates for v{next_ver}...')
    44        print()
    45
    46    if which("gh") is None:
    47        print("gh tool is not installed.")
    48        print("Please install the tool and rerun this script:")
    49        print("https://github.com/cli/cli#installation")
    50        return 1
    51
    52    warning = """
    53 ==> Warning: FIXME: This script does not have the property that if
    54     something goes wrong, you can just restart it; put another way:
    55     it does not have the property that each step is idempotent.
    56     If something does go wrong, then you'll have to address it, then
    57     resume where the script left off by going through the checklist
    58     manually (or by commenting out the already-completed steps).
    59"""
    60    print(f"{ansiterm.sgr.fg_red}{warning}{ansiterm.sgr}")
    61
    62    # This context manager and check function are pretty much just to produce
    63    # a nice list of steps...
    64    checker = Checker()
    65
    66    @contextmanager
    67    def check(name: str) -> Generator[CheckResult, None, None]:
    68        with checker.check(name) as subcheck:
    69            yield subcheck
    70
    71    if not getenv("GIT_TOKEN"):
    72        run(["gh", "auth", "login"])
    73
    74    m = re_ga.match(next_ver)
    75    if not m:
    76        m = re_ea.match(next_ver)
    77    assert m
    78    release_branch = f"release/v{m[vX]}.{m[vY]}"
    79    with check(f"Checking that {release_branch} exists"):
    80        run(["git", "rev-parse", "--verify", release_branch])
    81    if not checker.ok:
    82        checker.ok = True
    83        with check(f"Creating {release_branch}"):
    84            run(["git", "checkout", "-b", release_branch])
    85            run(["git", "push", "-u", "origin", release_branch])
    86        if not checker.ok:
    87            print(f"Could not create {release_branch}")
    88            print("Please create this branch then rerun this script")
    89            return 1
    90
    91    user = os.getenv("USER")
    92    if user is None or user == '':
    93        user = 'unknownuser'
    94    workbranch = f"{user}/v{next_ver}/changelog-updates"
    95    with check(f"Creating new branch {workbranch}"):
    96        if getenv('CI') == '':
    97            run(["git", "fetch", "--tags"])
    98        run(["git", "checkout", f"v{next_ver}", "-b", workbranch])
    99    if not checker.ok:
   100        return 1
   101
   102    with check(f"Updating releaseNotes.yml with date for {next_ver}..."):
   103        state = 0
   104
   105        for line in fileinput.FileInput("docs/releaseNotes.yml", inplace=True):
   106            if state == 0:
   107                if line.strip() == f"- version: {next_ver}":
   108                    state = 1
   109            elif state == 1:
   110                if m := re.match(r"(\s+date:)\s+.*$", line):
   111                    line = f"{m.group(1)} '{today.strftime('%Y-%m-%d')}'\n"
   112                    state = 2
   113            
   114            sys.stdout.write(line)
   115
   116        if state != 2:
   117            # Something didn't go right.
   118            sadness = f"""
   119 ==> ERROR: could not find the date for version '{next_ver}' in 
   120     docs/releaseNotes.yml. Make sure that the block for that release
   121     starts with
   122
   123     - version: {next_ver}
   124       date: ....
   125
   126     as the start of the block."""
   127
   128            raise Exception(sadness)
   129
   130    # Once this is done, update the CHANGELOG from the release notes.
   131    with check(f"Updating CHANGELOG.md from releaseNotes.yml..."):
   132        run([ "make", f"{os.getcwd()}/CHANGELOG.md" ])
   133
   134    if checker.ok and commit:
   135        with check(f"Committing changes..."):
   136            gitdir = run_capture(['git', 'rev-parse', '--git-dir'])
   137            with open(os.path.join(gitdir, 'GITGUI_MSG'), 'w') as msgfile:
   138                msgfile.write(f"Update for v{next_ver}\n")
   139            if getenv("AMBASSADOR_RELENG_NO_GUI"):
   140                run(['git', 'commit', '-am', f'Update for v{next_ver}'])
   141            else:
   142                run(['git', 'citool'])
   143            run(["git", "push", "-u", "origin", workbranch])
   144        if not checker.ok:
   145            return 1
   146
   147        with check(f"Creating PR for branch {workbranch}"):
   148            # TODO: dont hardcode owners
   149            pr_body = """Changelog date updates for {next_ver}
   150
   151Reviewers: The only changes in this PR should be updating the changelog entry for {next_ver} to the date it was released.
   152
   153If there are any other changes, the PR creator should note the reason."""
   154            curr_release_branch = f"rel/v{next_ver}"
   155            target_branch = release_branch
   156            if has_open_pr(get_gh_repo(), release_branch, curr_release_branch):
   157                target_branch = curr_release_branch
   158            run(["gh", "pr", "create",
   159                    "--repo", get_gh_repo(),
   160                    "--base", target_branch,
   161                    "--title", f"[v{next_ver}] Changelog date update",
   162                    "--body", pr_body.format(next_ver=next_ver),
   163                    "--reviewer", "kflynn,rhs,esmet,acookin"])
   164
   165    if checker.ok:
   166        if not getenv("AMBASSADOR_RELENG_NO_GUI"):
   167            run(["gh", "pr", "view", workbranch, "--repo", get_gh_repo(), "--web"])
   168        return 0
   169    else:
   170        return 1
   171
   172
   173if __name__ == '__main__':
   174    args = sys.argv[1:]
   175
   176    quiet = False
   177    commit = True
   178
   179    while args and args[0].startswith("--"):
   180        if args[0] == '--quiet':
   181            quiet = True
   182            args.pop(0)
   183        elif args and (args[0] == '--no-commit'):
   184            commit = False
   185            args.pop(0)
   186
   187    if len(args) != 1 or (not re_ga.match(args[0]) and not re_ea.match(args[0])):
   188        sys.stderr.write(f"Usage: {os.path.basename(sys.argv[0])} X.Y.Z\n")
   189        sys.exit(2)
   190
   191    sys.exit(main(
   192        next_ver=args[0],
   193        today=datetime.date.today(),
   194        quiet=quiet,
   195        commit=commit,
   196    ))

View as plain text