1919# 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
@@ -43,14 +47,36 @@ def run_git(*args, allow_non_zero_exit_code=False):
4347 raise Exception (f'Call to { " " .join (cmd )} exited with code { p .returncode } stderr: { p .stderr .decode ("ascii" )} .' )
4448 return p .stdout .decode ('ascii' )
4549
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+
4672# Returns true if the given branch exists on the origin remote
4773def branch_exists_on_remote (branch_name ):
4874 return run_git ('ls-remote' , '--heads' , ORIGIN , branch_name ).strip () != ''
4975
5076# Opens a PR from the given branch to the target branch
5177def open_pr (
5278 repo , all_commits , source_branch_short_sha , new_branch_name , source_branch , target_branch ,
53- conductor , is_primary_release , conflicted_files ):
79+ conductor , is_primary_release , conflicted_files , needs_rebuild ):
5480 # Sort the commits into the pull requests that introduced them,
5581 # and any commits that don't have a pull request
5682 pull_requests = []
@@ -108,10 +134,6 @@ def open_pr(
108134 body .append (f' - [ ] Check that there are not any unexpected commits being merged into the `{ target_branch } ` branch.' )
109135 body .append (' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.' )
110136
111- if not is_primary_release :
112- body .append (' - [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.' )
113- body .append (' - [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.' )
114-
115137 body .append (' - [ ] Mark the PR as ready for review to trigger the full set of PR checks.' )
116138 body .append (' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.' )
117139
@@ -120,13 +142,11 @@ def open_pr(
120142 body .append (' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.' )
121143
122144 title = f'Merge { source_branch } into { target_branch } '
123- labels = ['Rebuild' ] if not is_primary_release else []
124145
125146 # Create the pull request
126147 # PR checks won't be triggered on PRs created by Actions. Therefore mark the PR as draft so that
127148 # a maintainer can take the PR out of draft, thereby triggering the PR checks.
128149 pr = repo .create_pull (title = title , body = '\n ' .join (body ), head = new_branch_name , base = target_branch , draft = True )
129- pr .add_to_labels (* labels )
130150 print (f'Created PR #{ str (pr .number )} ' )
131151
132152 # Assign the conductor
@@ -385,8 +405,9 @@ def main():
385405 # releases.
386406 run_git ('revert' , vOlder_update_commits [0 ], '--no-edit' )
387407
388- # Also revert the "Rebuild" commit created by Actions.
389- rebuild_commit = run_git ('log' , '--grep' , '^Rebuild$' , '--format=%H' ).split ()[0 ]
408+ # Also revert the "Rebuild" commit, whether created by this script or by the
409+ # `Rebuild Action` workflow.
410+ rebuild_commit = run_git ('log' , '--grep' , f'^{ REBUILD_COMMIT_MESSAGE } $' , '--format=%H' ).split ()[0 ]
390411 print (f' Reverting { rebuild_commit } ' )
391412 run_git ('revert' , rebuild_commit , '--no-edit' )
392413
@@ -401,9 +422,10 @@ def main():
401422 run_git ('add' , '.' )
402423 run_git ('commit' , '--no-edit' )
403424
404- # Migrate the package version number from a vLatest version number to a vOlder version number
425+ # Migrate the package version number from a vLatest version number to a vOlder version number.
426+ # `package-lock.json` is updated as part of the subsequent rebuild step (see `rebuild_action`).
405427 print (f'Setting version number to { version } in package.json' )
406- replace_version_package_json (get_current_version (), version ) # We rely on the `Rebuild` workflow to update package-lock.json
428+ replace_version_package_json (get_current_version (), version )
407429 run_git ('add' , 'package.json' )
408430
409431 # Migrate the changelog notes from vLatest version numbers to vOlder version numbers
@@ -426,6 +448,13 @@ def main():
426448 run_git ('add' , 'CHANGELOG.md' )
427449 run_git ('commit' , '-m' , f'Update changelog for v{ version } ' )
428450
451+ if not is_primary_release :
452+ if len (conflicted_files ) == 0 :
453+ print ('Rebuilding the Action.' )
454+ rebuild_action ()
455+ else :
456+ print (f'Skipping automatic rebuild because the merge produced conflicts in { conflicted_files } .' )
457+
429458 run_git ('push' , ORIGIN , new_branch_name )
430459
431460 # Open a PR to update the branch
0 commit comments