Skip to content

Commit eb7fc7f

Browse files
committed
feat(release/auto-changelog): update release changelogs deterministically from a predecessor baseline
Replace update-release-changelog.sh with a TypeScript updater that builds the release section from the predecessor release's changelog plus the new PRs, keeps reviewer edits on an open changelog PR, and sends only new entries to the clean-room proofreader.
1 parent b8974d5 commit eb7fc7f

17 files changed

Lines changed: 4518 additions & 306 deletions

‎.github/actions/create-release-pr/action.yml‎

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,6 @@ inputs:
4848
google-application-creds-base64:
4949
description: 'Google application credentials base64 encoded.'
5050
required: true
51-
github-tools-repository:
52-
description: 'The GitHub repository containing the GitHub tools. Defaults to the GitHub tools action repositor, and usually does not need to be changed.'
53-
required: false
54-
default: ${{ github.action_repository }}
55-
github-tools-ref:
56-
description: 'The SHA of the action to use. Defaults to the current action ref, and usually does not need to be changed.'
57-
required: false
58-
default: ${{ github.action_ref }}
5951

6052
runs:
6153
using: composite
@@ -68,21 +60,13 @@ runs:
6860
ref: ${{ inputs.checkout-base-branch }}
6961
token: ${{ inputs.github-token }}
7062

71-
# Step 2: Checkout github-tools repository
72-
- name: Checkout github-tools repository
73-
uses: actions/checkout@v6
74-
with:
75-
repository: ${{ inputs.github-tools-repository }}
76-
ref: ${{ inputs.github-tools-ref }}
77-
path: github-tools
78-
79-
# Step 3: Setup environment
63+
# Step 2: Setup environment
8064
- name: Checkout and setup environment
8165
uses: MetaMask/action-checkout-and-setup@v3
8266
with:
8367
is-high-risk-environment: true
8468

85-
# Step 4: Print Input Values
69+
# Step 3: Print Input Values
8670
- name: Print Input Values
8771
env:
8872
PLATFORM: ${{ inputs.platform }}
@@ -117,7 +101,7 @@ runs:
117101
echo "Git User Email: $GIT_USER_EMAIL"
118102
echo "-------------"
119103
120-
# Step 5: Create Release PR
104+
# Step 4: Create Release PR
121105
- name: Create Release PR
122106
id: create-release-pr
123107
shell: bash
@@ -137,18 +121,19 @@ runs:
137121
MOBILE_BUILD_VERSION: ${{ inputs.mobile-build-version }}
138122
GIT_USER_NAME: ${{ inputs.git-user-name }}
139123
GIT_USER_EMAIL: ${{ inputs.git-user-email }}
124+
GITHUB_TOOLS_DIR: ${{ github.action_path }}/../../..
140125
working-directory: ${{ github.workspace }}
141126
run: |
142-
# Execute the script from github-tools
143-
./github-tools/.github/scripts/create-platform-release-pr.sh \
127+
# Use the action revision GitHub resolved for this run.
128+
"$GITHUB_TOOLS_DIR/.github/scripts/create-platform-release-pr.sh" \
144129
"$PLATFORM" \
145130
"$PREVIOUS_VERSION_REF" \
146131
"$SEMVER_VERSION" \
147132
"$MOBILE_BUILD_VERSION" \
148133
"$GIT_USER_NAME" \
149134
"$GIT_USER_EMAIL"
150135
151-
# Step 6: Upload commits.csv as artifact (if generated)
136+
# Step 5: Upload commits.csv as artifact (if generated)
152137
- name: Upload commits.csv artifact
153138
if: ${{ hashFiles('commits.csv') != '' }}
154139
uses: actions/upload-artifact@v7

‎.github/actions/update-release-changelog/action.yml‎

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,42 @@ inputs:
1212
repository-url:
1313
required: true
1414
description: 'Full HTTPS URL for the invoking repository.'
15-
previous-version-ref:
16-
required: false
17-
default: 'null'
18-
description: 'Previous release version reference (branch/tag/SHA). Use "null" for hotfixes.'
1915
github-token:
2016
required: true
2117
description: 'GitHub token with write access to the invoking repository and read access to planning resources.'
22-
github-tools-repository:
23-
description: 'The GitHub repository containing the GitHub tools. Defaults to the GitHub tools action repositor, and usually does not need to be changed.'
24-
required: false
25-
default: ${{ github.action_repository }}
26-
github-tools-ref:
27-
description: 'The SHA of the action to use. Defaults to the current action ref, and usually does not need to be changed.'
28-
required: false
29-
default: ${{ github.action_ref }}
3018

3119
runs:
3220
using: composite
3321
steps:
3422
# Step 1: Checkout invoking repository (metamask-mobile | metamask-extension)
3523
- name: Checkout invoking repository
3624
uses: actions/checkout@v6
25+
env:
26+
AI_ANALYZER_LITELLM_KEY: ''
3727
with:
3828
fetch-depth: 0
3929
ref: ${{ inputs.release-branch }}
4030
token: ${{ inputs.github-token }}
4131

42-
# Step 2: Checkout github-tools repository
43-
- name: Checkout GitHub tools repository
44-
uses: actions/checkout@v6
32+
- name: Set up Node 24 for release changelog automation
33+
uses: actions/setup-node@v6
34+
env:
35+
AI_ANALYZER_LITELLM_KEY: ''
4536
with:
46-
repository: ${{ inputs.github-tools-repository }}
47-
ref: ${{ inputs.github-tools-ref }}
48-
path: ./github-tools
37+
node-version: '24'
4938

50-
# Step 3: Setup environment
51-
- name: Checkout and setup environment
52-
uses: MetaMask/action-checkout-and-setup@v3
53-
with:
54-
is-high-risk-environment: true
39+
# Corepack resolves Yarn from the working directory, so install from the
40+
# github-tools root to use its own pinned Yarn and lockfile.
41+
- name: Install github-tools dependencies
42+
shell: bash
43+
working-directory: ${{ github.action_path }}/../../..
44+
env:
45+
AI_ANALYZER_LITELLM_KEY: ''
46+
run: |
47+
set -euo pipefail
48+
corepack enable
49+
corepack yarn install --immutable
5550
56-
# Step 4: Update changelog using shared helper script
5751
- name: Update changelog branch
5852
shell: bash
5953
env:
@@ -63,15 +57,21 @@ runs:
6357
RELEASE_BRANCH: ${{ inputs.release-branch }}
6458
PLATFORM: ${{ inputs.platform }}
6559
REPOSITORY_URL: ${{ inputs.repository-url }}
66-
PREVIOUS_VERSION_REF: ${{ inputs.previous-version-ref }}
60+
AUTO_CHANGELOG_CLI: ${{ github.action_path }}/../../../node_modules/@metamask/auto-changelog/dist/cli.js
6761
run: |
6862
set -euo pipefail
6963
70-
corepack enable
71-
yarn install --immutable
72-
73-
./github-tools/.github/scripts/update-release-changelog.sh \
64+
node "$GITHUB_ACTION_PATH/../../scripts/update-release-changelog.mts" \
7465
"$RELEASE_BRANCH" \
7566
"$PLATFORM" \
76-
"$REPOSITORY_URL" \
77-
"$PREVIOUS_VERSION_REF"
67+
"$REPOSITORY_URL"
68+
69+
- name: Upload clean-room proofread report
70+
if: ${{ always() && hashFiles('.tmp/release-changelog-ai/*.json') != '' }}
71+
uses: actions/upload-artifact@v7
72+
env:
73+
AI_ANALYZER_LITELLM_KEY: ''
74+
with:
75+
name: release-changelog-ai-report
76+
path: .tmp/release-changelog-ai/*.json
77+
if-no-files-found: ignore

‎.github/scripts/clean-release-changelog.mts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ function assertNotAdjacentListItems(line: string): void {
270270

271271
/**
272272
* Groups a model response's entries by category, rejecting stray Markdown.
273-
* It is deliberately strict because the input is untrusted model output.
273+
* This is stricter than `parseReleaseSectionCategories` because the input is
274+
* untrusted model output, not auto-changelog's own format.
274275
*
275276
* @param section - One chunk's cleaned section.
276277
* @param version - The release version.

‎.github/scripts/create-platform-release-pr.sh‎

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
# Script to create platform release PRs for MetaMask
44
# This script handles the creation of release PRs for both mobile and extension platforms
5-
# It creates three PRs:
5+
# It creates two PRs:
66
# 1. A release PR with version updates
7-
# 2. A changelog PR with updated changelog and test plan (skipped in test mode)
8-
# 3. A version bump PR for the main branch
7+
# 2. A version bump PR for the main branch (skipped for hotfix releases)
8+
# The changelog PR is created separately by the release-branch push workflow.
99
#
1010
# Usage:
1111
# create-platform-release-pr.sh <platform> <previous_version_ref> <new_version> [new_version_number] [git_user_name] [git_user_email]
@@ -21,15 +21,15 @@
2121
# Environment:
2222
# BASE_BRANCH - The base branch for the release PR (defaults to 'main')
2323
# GITHUB_TOKEN - Token for GitHub CLI operations (falls back to gh auth config)
24-
# TEST_ONLY - When set to "true", uses test branch prefixes and skips changelog PR
25-
# GITHUB_REPOSITORY_URL - Full HTTPS URL for the repository (required for changelog generation when TEST_ONLY is not "true")
24+
# TEST_ONLY - When set to "true", uses test branch prefixes and skips test plan generation
2625

2726
set -e
2827
set -u
2928
set -o pipefail
3029

3130
# Sourcing helper functions
3231
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
32+
GITHUB_TOOLS_DIR="${GITHUB_TOOLS_DIR:-$(cd "${SCRIPT_DIR}/../.." && pwd)}"
3333
# shellcheck source=.github/scripts/utils.sh
3434
source "${SCRIPT_DIR}/utils.sh"
3535

@@ -145,7 +145,6 @@ create_release_pr() {
145145
local new_version="$2"
146146
local new_version_number="$3"
147147
local release_branch_name="$4"
148-
local changelog_branch_name="$5"
149148

150149
echo "Checking out the release branch: ${release_branch_name}"
151150
checkout_or_create_branch "${release_branch_name}" "${BASE_BRANCH}"
@@ -156,7 +155,7 @@ create_release_pr() {
156155

157156
# Version Updates
158157
echo "Running version update scripts.."
159-
./github-tools/.github/scripts/set-semvar-version.sh "${new_version}" "${platform}"
158+
"${GITHUB_TOOLS_DIR}/.github/scripts/set-semvar-version.sh" "${new_version}" "${platform}"
160159

161160
# Commit Changes
162161
local changed_files
@@ -251,21 +250,20 @@ Many thanks in advance
251250
create_pr_if_not_exists "${release_branch_name}" "release: ${new_version}" "${release_body}" "${BASE_BRANCH}" "" "head"
252251
}
253252

254-
# Create changelog branch and generate changelog
255-
create_changelog_pr() {
253+
# Generate the release test plan. The release-branch push workflow publishes
254+
# the changelog separately so this script never races it for the same branch.
255+
generate_release_test_plan() {
256256
local platform="$1"
257-
local new_version="$2"
258-
local previous_version_ref="$3"
259-
local release_branch_name="$4"
260-
local changelog_branch_name="$5"
257+
local previous_version_ref="$2"
258+
local release_branch_name="$3"
261259

262260
# Skip commits.csv for hotfix releases (previous_version_ref is literal "null")
263261
# - When we create a new major/minor release, we fetch all commits included in the release, by fetching the diff between HEAD and previous version reference.
264262
# - When we create a new hotfix release, there are no commits included in the release by default (they will be cherry-picked one by one). So we don't have previous version reference, which is why the value is set to 'null'.
265263
if [[ "${previous_version_ref,,}" == "null" ]]; then
266264
echo "Hotfix release detected (previous-version-ref is 'null'); skipping commits.csv generation."
267265
else
268-
# Need to run from .github-tools context to inherit it's dependencies/environment
266+
# Generate the CSV with GitHub Tools' own dependencies, not the consumer's.
269267
echo "Current Directory: $(pwd)"
270268
PROJECT_GIT_DIR=$(pwd)
271269

@@ -290,37 +288,18 @@ create_changelog_pr() {
290288
echo "Previous version is not a recognized release branch pattern. Treating as tag or SHA: ${previous_version_ref}"
291289
fi
292290

293-
# Switch to github-tools directory
294-
cd ./github-tools/
291+
# Switch to the immutable action source directory.
292+
cd "${GITHUB_TOOLS_DIR}"
295293
ls -ltra
296294
corepack prepare yarn@4.14.1 --activate
297295
# This can't be done from the actions context layer due to the upstream repository having it's own context set with yarn
298-
yarn --cwd install
296+
yarn install
299297

300298
echo "Generating test plan csv.."
301299
yarn run gen:commits "${platform}" "${DIFF_BASE}" "${release_branch_name}" "${PROJECT_GIT_DIR}"
302-
# Return to project root after generating commits.csv
303-
cd ../
300+
# The action checkout is not nested under the consumer repository.
301+
cd "${PROJECT_GIT_DIR}"
304302
fi
305-
306-
# Delegate changelog update and PR creation to the shared update-release-changelog.sh script
307-
echo "Updating changelog and creating PR.."
308-
309-
# Export git identity for the shared script
310-
export GIT_AUTHOR_NAME="${GIT_USER_NAME}"
311-
export GIT_AUTHOR_EMAIL="${GIT_USER_EMAIL}"
312-
313-
# Call the shared script
314-
# The script is located in the same directory as this one
315-
"${SCRIPT_DIR}/update-release-changelog.sh" \
316-
"${release_branch_name}" \
317-
"${platform}" \
318-
"${GITHUB_REPOSITORY_URL}" \
319-
"${previous_version_ref}" \
320-
"${changelog_branch_name}" \
321-
"${new_version}"
322-
323-
echo "Changelog PR Ready"
324303
}
325304

326305
# Create version bump PR for main branch
@@ -339,7 +318,7 @@ create_version_bump_pr() {
339318

340319
# Update version files on main branch
341320
echo "Running version update scripts for ${main_branch} branch.."
342-
./github-tools/.github/scripts/set-semvar-version.sh "${next_version}" "${platform}"
321+
"${GITHUB_TOOLS_DIR}/.github/scripts/set-semvar-version.sh" "${next_version}" "${platform}"
343322

344323
# Commit version bump changes
345324
echo "Committing version bump changes.."
@@ -436,20 +415,20 @@ main() {
436415
next_version=$(get_next_version "$NEW_VERSION")
437416

438417
# Initialize branch names
439-
local release_branch_name changelog_branch_name version_bump_branch_name
418+
local release_branch_name version_bump_branch_name
440419
release_branch_name=$(get_release_branch_name "$NEW_VERSION")
441-
changelog_branch_name="release-changelog/${NEW_VERSION}"
442420
version_bump_branch_name=$(get_version_bump_branch_name "$next_version") # Execute main workflow
443421
configure_git "${GIT_USER_NAME}" "${GIT_USER_EMAIL}"
444422

445423
# Step 1: Create release branch and PR
446-
create_release_pr "$PLATFORM" "$NEW_VERSION" "$NEW_VERSION_NUMBER" "$release_branch_name" "$changelog_branch_name"
424+
create_release_pr "$PLATFORM" "$NEW_VERSION" "$NEW_VERSION_NUMBER" "$release_branch_name"
447425

448-
# Step 2: Create changelog PR (skip in test mode)
426+
# Step 2: The release-branch push workflow owns changelog publication.
427+
# Test-plan generation is still needed by release creation in production.
449428
if [ "$TEST_ONLY" == "true" ]; then
450-
echo "Skipping changelog generation in test mode"
429+
echo "Skipping test plan generation in test mode"
451430
else
452-
create_changelog_pr "$PLATFORM" "$NEW_VERSION" "$PREVIOUS_VERSION_REF" "$release_branch_name" "$changelog_branch_name"
431+
generate_release_test_plan "$PLATFORM" "$PREVIOUS_VERSION_REF" "$release_branch_name"
453432
fi
454433

455434
# Step 3: Create version bump PR for main branch (skip for hotfix releases)
@@ -467,7 +446,7 @@ main() {
467446
echo "Created PRs:"
468447
echo "1. Release PR: release: ${NEW_VERSION}"
469448
if [ "$TEST_ONLY" != "true" ]; then
470-
echo "2. Changelog PR: release: ${changelog_branch_name}"
449+
echo "2. Changelog PR: generated by the release-branch workflow"
471450
if [[ "${PREVIOUS_VERSION_REF,,}" == "null" ]]; then
472451
echo "(Hotfix) Skipped version bump PR"
473452
else

‎.github/scripts/merge-previous-release-changelog.mts‎

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,7 @@ import {
2525
hasSingleReleaseHeading,
2626
LINK_REFERENCE,
2727
} from './release-section.mts';
28-
29-
/**
30-
* Compares two `major.minor.patch` versions.
31-
* @param left - The first version.
32-
* @param right - The second version.
33-
* @returns A positive number when `left` is newer, negative when older, else 0.
34-
*/
35-
function compareVersions(left: string, right: string): number {
36-
const leftParts = left.split('.').map(Number);
37-
const rightParts = right.split('.').map(Number);
38-
39-
for (let index = 0; index < leftParts.length; index += 1) {
40-
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
41-
if (difference !== 0) {
42-
return difference;
43-
}
44-
}
45-
return 0;
46-
}
28+
import { compareSemver } from './release-version-utils.mts';
4729

4830
/**
4931
* Finds the `[version]: url` link reference for a release.
@@ -81,7 +63,7 @@ function findInsertionIndex(
8163
if (
8264
candidate &&
8365
match.index !== undefined &&
84-
compareVersions(version, candidate) > 0
66+
compareSemver(version, candidate) > 0
8567
) {
8668
return match.index;
8769
}

0 commit comments

Comments
 (0)