Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions .github/actions/prepare-mergeback-branch/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,38 @@ runs:
git add .
git commit -m "Update changelog and version after ${VERSION}"

git push origin "${NEW_BRANCH}"
# Update the build artifacts with the new version number
- name: Rebuild the Action
shell: bash
run: |
set -exu
npm ci
npm run build

- name: Check for rebuild changes
id: rebuild_changes
shell: bash
run: |
set -exu
git add --all
if git diff --cached --quiet; then
echo "has_changes=false" >> "${GITHUB_OUTPUT}"
else
echo "has_changes=true" >> "${GITHUB_OUTPUT}"
fi

- name: Commit rebuild
if: steps.rebuild_changes.outputs.has_changes == 'true'
shell: bash
run: |
set -exu
git commit -m "Rebuild"
Comment on lines +45 to +69
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this a reusable action?

Copy link
Copy Markdown
Contributor Author

@henrymercer henrymercer May 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that, but in this case we're only interested in running npm run build — running the linter, sync_back and sync is pointless.


- name: Push mergeback branch
shell: bash
env:
NEW_BRANCH: "${{ inputs.branch }}"
run: git push origin "${NEW_BRANCH}"

- name: Create PR
shell: bash
Expand All @@ -60,8 +91,6 @@ runs:

Please do the following:

- [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.
- [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.
- [ ] Mark the PR as ready for review to trigger the full set of PR checks.
- [ ] Approve and merge the PR. When merging the PR, make sure "Create a merge commit" is
selected rather than "Squash and merge" or "Rebase and merge".
Expand All @@ -74,7 +103,6 @@ runs:
--head "${NEW_BRANCH}" \
--base "${BASE_BRANCH}" \
--title "${pr_title}" \
--label "Rebuild" \
--body "${pr_body}" \
--assignee "${GITHUB_ACTOR}" \
--draft
2 changes: 1 addition & 1 deletion .github/actions/release-initialise/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ runs:
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: 'npm'

- name: Set up Python
Expand Down
55 changes: 43 additions & 12 deletions .github/update-release-branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
# Changing it requires a transition period where both old and new versions are supported.
BACKPORT_COMMIT_MESSAGE = 'Update version and changelog for v'

# Commit message used for rebuild commits, both those produced by this script and those produced
# by the `Rebuild Action` workflow (`.github/workflows/rebuild.yml`).
REBUILD_COMMIT_MESSAGE = 'Rebuild'

# Name of the remote
ORIGIN = 'origin'

Expand All @@ -43,6 +47,28 @@ def run_git(*args, allow_non_zero_exit_code=False):
raise Exception(f'Call to {" ".join(cmd)} exited with code {p.returncode} stderr: {p.stderr.decode("ascii")}.')
return p.stdout.decode('ascii')

# Runs the given command, streaming output to the console.
# Raises an error if the command does not exit successfully.
def run_command(*args):
cmd = list(args)
print(f'Running `{" ".join(cmd)}`.')
subprocess.run(cmd, check=True)

# Rebuilds the action and commits any changes.
def rebuild_action():
# For backports, the only source-level change vs the source branch is the new version number,
# so we just need to refresh the version embedded in `lib/`.
run_command('npm', 'ci')
run_command('npm', 'run', 'build')

run_git('add', '--all')
# `git diff --cached --quiet` exits 0 if there are no staged changes, 1 if there are.
if subprocess.run(['git', 'diff', '--cached', '--quiet']).returncode == 0:
print('Rebuild produced no changes; skipping Rebuild commit.')
else:
run_git('commit', '-m', REBUILD_COMMIT_MESSAGE)
print('Created Rebuild commit.')

# Returns true if the given branch exists on the origin remote
def branch_exists_on_remote(branch_name):
return run_git('ls-remote', '--heads', ORIGIN, branch_name).strip() != ''
Expand Down Expand Up @@ -98,20 +124,18 @@ def open_pr(
body.append('Please do the following:')
if len(conflicted_files) > 0:
body.append(' - [ ] Ensure `package.json` file contains the correct version.')
body.append(' - [ ] Add commits to this branch to resolve the merge conflicts ' +
body.append(' - [ ] Add a commit to this branch to resolve the merge conflicts ' +
'in the following files:')
body.extend([f' - [ ] `{file}`' for file in conflicted_files])
body.extend([f' - `{file}`' for file in conflicted_files])
body.append(' - [ ] Rebuild the Action locally (`npm run build`) and push any changes to the ' +
f'built output in `lib` as a separate commit named exactly `{REBUILD_COMMIT_MESSAGE}`.')
body.append(' - [ ] Ensure another maintainer has reviewed the additional commits you added to this ' +
'branch to resolve the merge conflicts.')
body.append(' - [ ] Ensure the CHANGELOG displays the correct version and date.')
body.append(' - [ ] Ensure the CHANGELOG includes all relevant, user-facing changes since the last release.')
body.append(f' - [ ] Check that there are not any unexpected commits being merged into the `{target_branch}` branch.')
body.append(' - [ ] Ensure the docs team is aware of any documentation changes that need to be released.')

if not is_primary_release:
body.append(' - [ ] Remove and re-add the "Rebuild" label to the PR to trigger just this workflow.')
body.append(' - [ ] Wait for the "Rebuild" workflow to push a commit updating the distribution files.')

body.append(' - [ ] Mark the PR as ready for review to trigger the full set of PR checks.')
body.append(' - [ ] Approve and merge this PR. Make sure `Create a merge commit` is selected rather than `Squash and merge` or `Rebase and merge`.')

Expand All @@ -120,13 +144,11 @@ def open_pr(
body.append(' - [ ] Merge all backport PRs to older release branches, that will automatically be created once this PR is merged.')

title = f'Merge {source_branch} into {target_branch}'
labels = ['Rebuild'] if not is_primary_release else []

# Create the pull request
# PR checks won't be triggered on PRs created by Actions. Therefore mark the PR as draft so that
# a maintainer can take the PR out of draft, thereby triggering the PR checks.
pr = repo.create_pull(title=title, body='\n'.join(body), head=new_branch_name, base=target_branch, draft=True)
pr.add_to_labels(*labels)
print(f'Created PR #{str(pr.number)}')

# Assign the conductor
Expand Down Expand Up @@ -385,8 +407,9 @@ def main():
# releases.
run_git('revert', vOlder_update_commits[0], '--no-edit')

# Also revert the "Rebuild" commit created by Actions.
rebuild_commit = run_git('log', '--grep', '^Rebuild$', '--format=%H').split()[0]
# Also revert the "Rebuild" commit, whether created by this script or by the
# `Rebuild Action` workflow.
rebuild_commit = run_git('log', '--grep', f'^{REBUILD_COMMIT_MESSAGE}$', '--format=%H').split()[0]
print(f' Reverting {rebuild_commit}')
run_git('revert', rebuild_commit, '--no-edit')
Comment thread
henrymercer marked this conversation as resolved.

Expand All @@ -401,9 +424,10 @@ def main():
run_git('add', '.')
run_git('commit', '--no-edit')

# Migrate the package version number from a vLatest version number to a vOlder version number
# Migrate the package version number from a vLatest version number to a vOlder version number.
# `package-lock.json` is updated as part of the subsequent rebuild step (see `rebuild_action`).
print(f'Setting version number to {version} in package.json')
replace_version_package_json(get_current_version(), version) # We rely on the `Rebuild` workflow to update package-lock.json
replace_version_package_json(get_current_version(), version)
run_git('add', 'package.json')

# Migrate the changelog notes from vLatest version numbers to vOlder version numbers
Expand All @@ -426,6 +450,13 @@ def main():
run_git('add', 'CHANGELOG.md')
run_git('commit', '-m', f'Update changelog for v{version}')

if not is_primary_release:
if len(conflicted_files) == 0:
print('Rebuilding the Action.')
rebuild_action()
else:
print(f'Skipping automatic rebuild because the merge produced conflicts in {conflicted_files}.')

run_git('push', ORIGIN, new_branch_name)

# Open a PR to update the branch
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/post-release-mergeback.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ jobs:
with:
fetch-depth: 0 # ensure we have all tags and can push commits
- uses: actions/setup-node@v6
with:
node-version: 24
cache: 'npm'
- uses: actions/setup-python@v6
with:
python-version: '3.12'
Expand Down
Loading