1616"""
1717
1818# NB: This exact commit message is used to find commits for reverting during backports.
19- # Changing it requires a transition period where both old and new versions are supported.
19+ # Changing it requires a transition period where both old and new versions are supported.
2020BACKPORT_COMMIT_MESSAGE = 'Update version and changelog for v'
2121
22+ # Commit message used for rebuild commits, both those produced by this script and those produced
23+ # by the `Rebuild Action` workflow (`.github/workflows/rebuild.yml`).
24+ REBUILD_COMMIT_MESSAGE = 'Rebuild'
25+
2226# Name of the remote
2327ORIGIN = 'origin'
2428
29+ # Environment variables to check for a GitHub API token.
30+ TOKEN_ENVIRONMENT_VARIABLES = ('GH_TOKEN' , 'GITHUB_TOKEN' )
31+
32+ # Gets a GitHub API token from one of the supported environment variables.
33+ def get_github_token ():
34+ for variable_name in TOKEN_ENVIRONMENT_VARIABLES :
35+ token = os .environ .get (variable_name , '' ).strip ()
36+ if token :
37+ return token
38+ raise Exception ('Missing GitHub token. Set GITHUB_TOKEN or GH_TOKEN.' )
39+
2540# Runs git with the given args and returns the stdout.
2641# Raises an error if git does not exit successfully (unless passed
2742# allow_non_zero_exit_code=True).
@@ -32,6 +47,28 @@ def run_git(*args, allow_non_zero_exit_code=False):
3247 raise Exception (f'Call to { " " .join (cmd )} exited with code { p .returncode } stderr: { p .stderr .decode ("ascii" )} .' )
3348 return p .stdout .decode ('ascii' )
3449
50+ # Runs the given command, streaming output to the console.
51+ # Raises an error if the command does not exit successfully.
52+ def run_command (* args ):
53+ cmd = list (args )
54+ print (f'Running `{ " " .join (cmd )} `.' )
55+ subprocess .run (cmd , check = True )
56+
57+ # Rebuilds the action and commits any changes.
58+ def rebuild_action ():
59+ # For backports, the only source-level change vs the source branch is the new version number,
60+ # so we just need to refresh the version embedded in `lib/`.
61+ run_command ('npm' , 'ci' )
62+ run_command ('npm' , 'run' , 'build' )
63+
64+ run_git ('add' , '--all' )
65+ # `git diff --cached --quiet` exits 0 if there are no staged changes, 1 if there are.
66+ if subprocess .run (['git' , 'diff' , '--cached' , '--quiet' ]).returncode == 0 :
67+ print ('Rebuild produced no changes; skipping Rebuild commit.' )
68+ else :
69+ run_git ('commit' , '-m' , REBUILD_COMMIT_MESSAGE )
70+ print ('Created Rebuild commit.' )
71+
3572# Returns true if the given branch exists on the origin remote
3673def branch_exists_on_remote (branch_name ):
3774 return run_git ('ls-remote' , '--heads' , ORIGIN , branch_name ).strip () != ''
@@ -87,20 +124,18 @@ def open_pr(
87124 body .append ('Please do the following:' )
88125 if len (conflicted_files ) > 0 :
89126 body .append (' - [ ] Ensure `package.json` file contains the correct version.' )
90- body .append (' - [ ] Add commits to this branch to resolve the merge conflicts ' +
127+ body .append (' - [ ] Add a commit to this branch to resolve the merge conflicts ' +
91128 'in the following files:' )
92- body .extend ([f' - [ ] `{ file } `' for file in conflicted_files ])
129+ body .extend ([f' - `{ file } `' for file in conflicted_files ])
130+ body .append (' - [ ] Rebuild the Action locally (`npm run build`) and push any changes to the ' +
131+ f'built output in `lib` as a separate commit named exactly `{ REBUILD_COMMIT_MESSAGE } `.' )
93132 body .append (' - [ ] Ensure another maintainer has reviewed the additional commits you added to this ' +
94133 'branch to resolve the merge conflicts.' )
95134 body .append (' - [ ] Ensure the CHANGELOG displays the correct version and date.' )
96135 body .append (' - [ ] Ensure the CHANGELOG includes all relevant, user-facing changes since the last release.' )
97136 body .append (f' - [ ] Check that there are not any unexpected commits being merged into the `{ target_branch } ` branch.' )
98137 body .append (' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.' )
99138
100- if not is_primary_release :
101- body .append (' - [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.' )
102- body .append (' - [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.' )
103-
104139 body .append (' - [ ] Mark the PR as ready for review to trigger the full set of PR checks.' )
105140 body .append (' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.' )
106141
@@ -109,13 +144,11 @@ def open_pr(
109144 body .append (' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.' )
110145
111146 title = f'Merge { source_branch } into { target_branch } '
112- labels = ['Rebuild' ] if not is_primary_release else []
113147
114148 # Create the pull request
115149 # PR checks won't be triggered on PRs created by Actions. Therefore mark the PR as draft so that
116150 # a maintainer can take the PR out of draft, thereby triggering the PR checks.
117151 pr = repo .create_pull (title = title , body = '\n ' .join (body ), head = new_branch_name , base = target_branch , draft = True )
118- pr .add_to_labels (* labels )
119152 print (f'Created PR #{ str (pr .number )} ' )
120153
121154 # Assign the conductor
@@ -270,12 +303,6 @@ def update_changelog(version):
270303def main ():
271304 parser = argparse .ArgumentParser ('update-release-branch.py' )
272305
273- parser .add_argument (
274- '--github-token' ,
275- type = str ,
276- required = True ,
277- help = 'GitHub token, typically from GitHub Actions.'
278- )
279306 parser .add_argument (
280307 '--repository-nwo' ,
281308 type = str ,
@@ -313,7 +340,7 @@ def main():
313340 target_branch = args .target_branch
314341 is_primary_release = args .is_primary_release
315342
316- repo = Github (args . github_token ).get_repo (args .repository_nwo )
343+ repo = Github (get_github_token () ).get_repo (args .repository_nwo )
317344
318345 # the target branch will be of the form releases/vN, where N is the major version number
319346 target_branch_major_version = target_branch .strip ('releases/v' )
@@ -380,8 +407,9 @@ def main():
380407 # releases.
381408 run_git ('revert' , vOlder_update_commits [0 ], '--no-edit' )
382409
383- # Also revert the "Rebuild" commit created by Actions.
384- rebuild_commit = run_git ('log' , '--grep' , '^Rebuild$' , '--format=%H' ).split ()[0 ]
410+ # Also revert the "Rebuild" commit, whether created by this script or by the
411+ # `Rebuild Action` workflow.
412+ rebuild_commit = run_git ('log' , '--grep' , f'^{ REBUILD_COMMIT_MESSAGE } $' , '--format=%H' ).split ()[0 ]
385413 print (f' Reverting { rebuild_commit } ' )
386414 run_git ('revert' , rebuild_commit , '--no-edit' )
387415
@@ -396,9 +424,10 @@ def main():
396424 run_git ('add' , '.' )
397425 run_git ('commit' , '--no-edit' )
398426
399- # Migrate the package version number from a vLatest version number to a vOlder version number
427+ # Migrate the package version number from a vLatest version number to a vOlder version number.
428+ # `package-lock.json` is updated as part of the subsequent rebuild step (see `rebuild_action`).
400429 print (f'Setting version number to { version } in package.json' )
401- replace_version_package_json (get_current_version (), version ) # We rely on the `Rebuild` workflow to update package-lock.json
430+ replace_version_package_json (get_current_version (), version )
402431 run_git ('add' , 'package.json' )
403432
404433 # Migrate the changelog notes from vLatest version numbers to vOlder version numbers
@@ -421,6 +450,13 @@ def main():
421450 run_git ('add' , 'CHANGELOG.md' )
422451 run_git ('commit' , '-m' , f'Update changelog for v{ version } ' )
423452
453+ if not is_primary_release :
454+ if len (conflicted_files ) == 0 :
455+ print ('Rebuilding the Action.' )
456+ rebuild_action ()
457+ else :
458+ print (f'Skipping automatic rebuild because the merge produced conflicts in { conflicted_files } .' )
459+
424460 run_git ('push' , ORIGIN , new_branch_name )
425461
426462 # Open a PR to update the branch
0 commit comments