From d0c216e27f8cdbf433d40076205a65e31f749053 Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Tue, 14 Oct 2025 14:22:29 -0400 Subject: [PATCH 1/7] INFRA-2567:Added updated release changelog workflow, script --- .github/scripts/update-release-changelog.sh | 253 ++++++++++++++++++ .../workflows/update-release-changelog.yml | 82 ++++++ 2 files changed, 335 insertions(+) create mode 100644 .github/scripts/update-release-changelog.sh create mode 100644 .github/workflows/update-release-changelog.yml diff --git a/.github/scripts/update-release-changelog.sh b/.github/scripts/update-release-changelog.sh new file mode 100644 index 00000000..3c8694ee --- /dev/null +++ b/.github/scripts/update-release-changelog.sh @@ -0,0 +1,253 @@ +#!/usr/bin/env bash + +# Updates (or creates) the changelog branch/PR for a given release branch. +# This script duplicates the logic of create_changelog_pr() from create-platform-release-pr.sh +# so it can run standalone (e.g. from automation) without sourcing the original script. +# +# Required arguments: +# 1. release_branch - Name of the release branch (e.g., Version-v13.3.0, release/6.20.0) +# 2. platform - Target platform (extension | mobile). Defaults to extension if empty. +# 3. repository_url - Full HTTPS URL for the invoking repository. +# +# Optional arguments: +# 4. previous_version_ref - Previous version reference (branch/tag/SHA). Defaults to literal "null" +# so that commits.csv generation is skipped, matching hotfix behaviour. +# +# Environment (optional): +# GH_TOKEN - Token for GitHub CLI operations (falls back to gh auth config) +# GIT_AUTHOR_NAME - Commit author name (defaults to metamaskbot) +# GIT_AUTHOR_EMAIL - Commit author email (defaults to metamaskbot@users.noreply.github.com) +# TEST_ONLY - When set to "true" the helper mirrors release automation test mode. + +set -euo pipefail + +RELEASE_BRANCH="${1:?release branch is required}" +PLATFORM="${2:-extension}" +REPOSITORY_URL="${3:?repository url is required}" +PREVIOUS_VERSION_REF="${4:-null}" + +AUTHOR_NAME="${GIT_AUTHOR_NAME:-metamaskbot}" +AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-metamaskbot@users.noreply.github.com}" +TEST_ONLY="${TEST_ONLY:-false}" + +# --- Helper functions copied or adapted from create-platform-release-pr.sh --- + +configure_git() { + # Configure git identity and fetch remote refs so subsequent operations run + # with the same context as create-platform-release-pr.sh. + echo "Configuring git.." + git config user.name "${AUTHOR_NAME}" + git config user.email "${AUTHOR_EMAIL}" + + echo "Fetching from remote..." + git fetch +} + +checkout_or_create_branch() { + # Ensure a branch exists locally for the changelog workflow. If it already + # exists locally or remotely, check it out; otherwise create it (optionally + # from a provided base branch) so changelog updates have the proper base. + local branch_name="$1" + local base_branch="${2:-}" + + echo "Checking for existing branch ${branch_name}" + + if git show-ref --verify --quiet "refs/heads/${branch_name}" || git ls-remote --heads origin "${branch_name}" | grep -q "${branch_name}"; then + echo "Branch ${branch_name} already exists, checking it out" + if git ls-remote --heads origin "${branch_name}" | grep -q "${branch_name}"; then + git fetch origin "${branch_name}" + git checkout "${branch_name}" + else + git checkout "${branch_name}" + fi + else + echo "Creating new branch ${branch_name}" + if [[ -n "${base_branch}" ]]; then + git checkout "${base_branch}" + git pull origin "${base_branch}" + fi + git checkout -b "${branch_name}" + fi + + echo "Branch ${branch_name} ready" +} + +push_branch_with_handling() { + # Push changelog updates upstream, tolerating no-op pushes while still + # surfacing failures when the remote branch is missing. + local branch_name="$1" + + echo "Pushing changes to the remote.." + if ! git push --set-upstream origin "${branch_name}"; then + echo "No changes to push to ${branch_name}" + if git ls-remote --heads origin "${branch_name}" | grep -q "${branch_name}"; then + echo "Branch ${branch_name} already exists remotely" + else + echo "Error: Failed to push and branch doesn't exist remotely" + exit 1 + fi + fi +} + +create_pr_if_not_exists() { + # Guard against duplicate changelog PRs by checking existing PRs before + # opening a draft that targets the release branch. + local branch_name="$1" + local title="$2" + local body="$3" + local base_branch="${4:-main}" + local labels="${5:-}" + local search_method="${6:-head}" + + echo "Creating PR for ${branch_name}.." + + local pr_exists=false + if [[ "${search_method}" == "search" ]]; then + if gh pr list --search "head:${branch_name}" --json number --jq 'length' | grep -q "1"; then + pr_exists=true + fi + else + if gh pr list --head "${branch_name}" --json number --jq 'length' | grep -q "1"; then + pr_exists=true + fi + fi + + if ${pr_exists}; then + echo "PR for branch ${branch_name} already exists" + else + local gh_cmd=(gh pr create --draft --title "${title}" --body "${body}" --base "${base_branch}" --head "${branch_name}") + if [[ -n "${labels}" ]]; then + gh_cmd+=(--label "${labels}") + fi + "${gh_cmd[@]}" + echo "PR Created: ${title}" + fi +} + +# ----------------------------------------------------------------- +# ----------------------------------------------------------------- + +# --- Local helper functions specific to this script --- + +ensure_release_branch() { + # Sync the release branch to the latest remote state so changelog commits + # build off the current release tip. + local release_branch="$1" + git fetch origin "${release_branch}" + git checkout "${release_branch}" + git reset --hard "origin/${release_branch}" +} + +determine_changelog_branch() { + # Prefer an existing remote changelog branch (release/-Changelog), + # falling back to the chore/ naming or the preferred default if none exist. + local version="$1" + local preferred="release/${version}-Changelog" + if git ls-remote --exit-code origin "${preferred}" > /dev/null 2>&1; then + echo "${preferred}" + elif git ls-remote --exit-code origin "chore/${version}-Changelog" > /dev/null 2>&1; then + echo "chore/${version}-Changelog" + else + echo "${preferred}" + fi +} + +generate_commits_csv_if_needed() { + # Reproduce the release automation behaviour: for non-hotfix releases, + # generate commits.csv using the github-tools helper so QA sheets can be + # produced; skip entirely for hotfixes. + local platform="$1" + local previous_version_ref="$2" + local release_branch="$3" + + if [[ "${previous_version_ref,,}" == "null" ]]; then + echo "Hotfix release detected (previous-version-ref is 'null'); skipping commits.csv generation." + return 0 + fi + + local project_git_dir + project_git_dir=$(pwd) + + local diff_base="${previous_version_ref}" + if [[ "${previous_version_ref}" =~ ^Version-v[0-9]+\.[0-9]+\.[0-9]+$ || "${previous_version_ref}" =~ ^release/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Previous version looks like a release branch: ${previous_version_ref}" + if git ls-remote --heads origin "${previous_version_ref}" | grep -q "."; then + echo "Detected remote branch for previous version: ${previous_version_ref}" + git fetch origin "${previous_version_ref}" + diff_base="origin/${previous_version_ref}" + else + echo "Remote branch not found on origin: ${previous_version_ref}. Will use as-is." + fi + else + echo "Previous version is not a recognized release branch pattern. Treating as tag or SHA: ${previous_version_ref}" + fi + + pushd ./github-tools/ > /dev/null + ls -ltra + corepack prepare yarn@4.5.1 --activate + yarn --cwd install + + echo "Generating test plan csv.." + yarn run gen:commits "${platform}" "${diff_base}" "${release_branch}" "${project_git_dir}" + popd > /dev/null +} + +commit_and_push_changelog() { + # Commit changelog updates (with the same messaging as the release script), + # push the branch, and ensure a draft PR exists targeting the release branch. + local version="$1" + local previous_version_ref="$2" + local changelog_branch="$3" + local release_branch="$4" + + echo "Adding and committing changes.." + local commit_msg="update changelog for ${version}" + if [[ "${previous_version_ref,,}" == "null" ]]; then + commit_msg="${commit_msg} (hotfix - no test plan)" + fi + if ! git commit -am "${commit_msg}"; then + echo "No changes detected; skipping commit." + fi + + local pr_body="This PR updates the change log for ${version}." + if [[ "${previous_version_ref,,}" == "null" ]]; then + pr_body="${pr_body} (Hotfix - no test plan generated.)" + fi + + push_branch_with_handling "${changelog_branch}" + create_pr_if_not_exists "${changelog_branch}" "release: ${changelog_branch}" "${pr_body}" "${release_branch}" "" "search" + echo "Changelog PR Ready" +} + +# ----------------------------------------------------------------- + +# Derive the semantic version from the branch naming convention. +if [[ "${RELEASE_BRANCH}" =~ ^Version-v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[1]}" +elif [[ "${RELEASE_BRANCH}" =~ ^release/([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + VERSION="${BASH_REMATCH[1]}" +else + echo "Release branch '${RELEASE_BRANCH}' does not match known patterns." >&2 + exit 0 +fi + +GITHUB_REPOSITORY_URL="${REPOSITORY_URL}" + +configure_git + +ensure_release_branch "${RELEASE_BRANCH}" + +CHANGELOG_BRANCH=$(determine_changelog_branch "${VERSION}") +checkout_or_create_branch "${CHANGELOG_BRANCH}" "${RELEASE_BRANCH}" + +echo "Generating changelog for ${PLATFORM} ${VERSION}.." +if [[ "${PLATFORM}" == "extension" ]]; then + yarn auto-changelog update --rc --repo "${GITHUB_REPOSITORY_URL}" --currentVersion "${VERSION}" --autoCategorize --useChangelogEntry --useShortPrLink +else + npx @metamask/auto-changelog@4.1.0 update --rc --repo "${GITHUB_REPOSITORY_URL}" --currentVersion "${VERSION}" --autoCategorize +fi + +generate_commits_csv_if_needed "${PLATFORM}" "${PREVIOUS_VERSION_REF}" "${RELEASE_BRANCH}" + +commit_and_push_changelog "${VERSION}" "${PREVIOUS_VERSION_REF}" "${CHANGELOG_BRANCH}" "${RELEASE_BRANCH}" + diff --git a/.github/workflows/update-release-changelog.yml b/.github/workflows/update-release-changelog.yml new file mode 100644 index 00000000..3a65c6fd --- /dev/null +++ b/.github/workflows/update-release-changelog.yml @@ -0,0 +1,82 @@ +name: Update Release Changelog + +on: + workflow_call: + inputs: + release-branch: + required: true + type: string + description: 'Release branch name (e.g., Version-v13.3.0 or release/6.20.0).' + platform: + required: false + type: string + default: 'extension' + description: 'Target platform (extension | mobile). Defaults to extension.' + repository-url: + required: true + type: string + description: 'Full HTTPS URL for the invoking repository.' + previous-version-ref: + required: false + type: string + default: 'null' + description: 'Previous release version reference (branch/tag/SHA). Use "null" for hotfixes.' + github-tools-version: + required: false + type: string + default: 'main' + description: 'Version of github-tools to use (branch, tag, or SHA).' + secrets: + github-token: + required: true + description: 'GitHub token with write access to the invoking repository and read access to planning resources.' + +jobs: + update-changelog: + name: Update release changelog + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout invoking repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.release-branch }} + path: repo + + - name: Checkout github-tools repository + uses: actions/checkout@v4 + with: + repository: MetaMask/github-tools + ref: ${{ inputs.github-tools-version }} + path: repo/github-tools + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install dependencies + working-directory: repo + run: | + set -euo pipefail + corepack enable + yarn install --immutable + + - name: Update changelog branch + env: + GH_TOKEN: ${{ secrets.github-token }} + GIT_AUTHOR_NAME: metamaskbot + GIT_AUTHOR_EMAIL: metamaskbot@users.noreply.github.com + working-directory: repo + run: | + set -euo pipefail + + bash github-tools/.github/scripts/update-release-changelog.sh \ + "${{ inputs.release-branch }}" \ + "${{ inputs.platform }}" \ + "${{ inputs.repository-url }}" \ + "${{ inputs.previous-version-ref }}" + From cbfe6d0d06d0f17ad6ecb4656e60b15f2855a06d Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Tue, 14 Oct 2025 14:29:24 -0400 Subject: [PATCH 2/7] INFRA-2567:Added updated release changelog workflow, script --- .../workflows/update-release-changelog.yml | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/update-release-changelog.yml b/.github/workflows/update-release-changelog.yml index 3a65c6fd..461a28c3 100644 --- a/.github/workflows/update-release-changelog.yml +++ b/.github/workflows/update-release-changelog.yml @@ -39,42 +39,41 @@ jobs: contents: write pull-requests: write steps: + # Step 1: Checkout invoking repository (metamask-mobile | metamask-extension) - name: Checkout invoking repository uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ inputs.release-branch }} - path: repo + token: ${{ secrets.github-token }} + # Step 2: Checkout github-tools repository - name: Checkout github-tools repository uses: actions/checkout@v4 with: repository: MetaMask/github-tools ref: ${{ inputs.github-tools-version }} - path: repo/github-tools + path: github-tools - - name: Set up Node.js - uses: actions/setup-node@v4 + # Step 3: Setup environment from github-tools + - name: Checkout and setup environment + uses: ./github-tools/.github/actions/checkout-and-setup with: - node-version: '18' - - - name: Install dependencies - working-directory: repo - run: | - set -euo pipefail - corepack enable - yarn install --immutable + is-high-risk-environment: true + # Step 4: Update changelog using shared helper script - name: Update changelog branch env: GH_TOKEN: ${{ secrets.github-token }} GIT_AUTHOR_NAME: metamaskbot GIT_AUTHOR_EMAIL: metamaskbot@users.noreply.github.com - working-directory: repo run: | set -euo pipefail - bash github-tools/.github/scripts/update-release-changelog.sh \ + corepack enable + yarn install --immutable + + bash .github/scripts/update-release-changelog.sh \ "${{ inputs.release-branch }}" \ "${{ inputs.platform }}" \ "${{ inputs.repository-url }}" \ From 213ebde5e4d36a7a51107a012a8e318c84615f4f Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Tue, 14 Oct 2025 15:11:28 -0400 Subject: [PATCH 3/7] INFRA-2567:Added updated release changelog workflow, script --- .github/workflows/update-release-changelog.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-release-changelog.yml b/.github/workflows/update-release-changelog.yml index 461a28c3..ddf35d0a 100644 --- a/.github/workflows/update-release-changelog.yml +++ b/.github/workflows/update-release-changelog.yml @@ -63,6 +63,7 @@ jobs: # Step 4: Update changelog using shared helper script - name: Update changelog branch + shell: bash env: GH_TOKEN: ${{ secrets.github-token }} GIT_AUTHOR_NAME: metamaskbot @@ -73,7 +74,7 @@ jobs: corepack enable yarn install --immutable - bash .github/scripts/update-release-changelog.sh \ + ./github-tools/.github/scripts/update-release-changelog.sh \ "${{ inputs.release-branch }}" \ "${{ inputs.platform }}" \ "${{ inputs.repository-url }}" \ From e3072cd9cb3a1fb5027ea2aaadafe3f987e7f7d4 Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Tue, 14 Oct 2025 15:40:57 -0400 Subject: [PATCH 4/7] INFRA-2567:Added updated release changelog workflow, script Signed-off-by: Pavel Dvorkin --- .github/scripts/update-release-changelog.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 .github/scripts/update-release-changelog.sh diff --git a/.github/scripts/update-release-changelog.sh b/.github/scripts/update-release-changelog.sh old mode 100644 new mode 100755 From 5b7d7951f28d58644d987d49813e94bce7c1d88f Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Wed, 15 Oct 2025 09:54:42 -0400 Subject: [PATCH 5/7] INFRA-2567:Added updated release changelog workflow, script Signed-off-by: Pavel Dvorkin --- .github/workflows/update-release-changelog.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-release-changelog.yml b/.github/workflows/update-release-changelog.yml index ddf35d0a..082991e3 100644 --- a/.github/workflows/update-release-changelog.yml +++ b/.github/workflows/update-release-changelog.yml @@ -65,6 +65,7 @@ jobs: - name: Update changelog branch shell: bash env: + GITHUB_TOKEN: ${{ secrets.github-token }} GH_TOKEN: ${{ secrets.github-token }} GIT_AUTHOR_NAME: metamaskbot GIT_AUTHOR_EMAIL: metamaskbot@users.noreply.github.com From a2054a3017876f8c3a64d98099554f0a85b553e6 Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Fri, 17 Oct 2025 09:39:48 -0400 Subject: [PATCH 6/7] Implemented Code review suggestions --- .github/scripts/update-release-changelog.sh | 49 ++------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/.github/scripts/update-release-changelog.sh b/.github/scripts/update-release-changelog.sh index 3c8694ee..ff634935 100755 --- a/.github/scripts/update-release-changelog.sh +++ b/.github/scripts/update-release-changelog.sh @@ -5,7 +5,7 @@ # so it can run standalone (e.g. from automation) without sourcing the original script. # # Required arguments: -# 1. release_branch - Name of the release branch (e.g., Version-v13.3.0, release/6.20.0) +# 1. release_branch - Name of the release branch (e.g., release/6.20.0) # 2. platform - Target platform (extension | mobile). Defaults to extension if empty. # 3. repository_url - Full HTTPS URL for the invoking repository. # @@ -152,45 +152,6 @@ determine_changelog_branch() { fi } -generate_commits_csv_if_needed() { - # Reproduce the release automation behaviour: for non-hotfix releases, - # generate commits.csv using the github-tools helper so QA sheets can be - # produced; skip entirely for hotfixes. - local platform="$1" - local previous_version_ref="$2" - local release_branch="$3" - - if [[ "${previous_version_ref,,}" == "null" ]]; then - echo "Hotfix release detected (previous-version-ref is 'null'); skipping commits.csv generation." - return 0 - fi - - local project_git_dir - project_git_dir=$(pwd) - - local diff_base="${previous_version_ref}" - if [[ "${previous_version_ref}" =~ ^Version-v[0-9]+\.[0-9]+\.[0-9]+$ || "${previous_version_ref}" =~ ^release/[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Previous version looks like a release branch: ${previous_version_ref}" - if git ls-remote --heads origin "${previous_version_ref}" | grep -q "."; then - echo "Detected remote branch for previous version: ${previous_version_ref}" - git fetch origin "${previous_version_ref}" - diff_base="origin/${previous_version_ref}" - else - echo "Remote branch not found on origin: ${previous_version_ref}. Will use as-is." - fi - else - echo "Previous version is not a recognized release branch pattern. Treating as tag or SHA: ${previous_version_ref}" - fi - - pushd ./github-tools/ > /dev/null - ls -ltra - corepack prepare yarn@4.5.1 --activate - yarn --cwd install - - echo "Generating test plan csv.." - yarn run gen:commits "${platform}" "${diff_base}" "${release_branch}" "${project_git_dir}" - popd > /dev/null -} commit_and_push_changelog() { # Commit changelog updates (with the same messaging as the release script), @@ -221,10 +182,8 @@ commit_and_push_changelog() { # ----------------------------------------------------------------- -# Derive the semantic version from the branch naming convention. -if [[ "${RELEASE_BRANCH}" =~ ^Version-v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then - VERSION="${BASH_REMATCH[1]}" -elif [[ "${RELEASE_BRANCH}" =~ ^release/([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then +# Derive the semantic version from the branch naming convention (release/x.y.z only). +if [[ "${RELEASE_BRANCH}" =~ ^release/([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then VERSION="${BASH_REMATCH[1]}" else echo "Release branch '${RELEASE_BRANCH}' does not match known patterns." >&2 @@ -247,7 +206,7 @@ else npx @metamask/auto-changelog@4.1.0 update --rc --repo "${GITHUB_REPOSITORY_URL}" --currentVersion "${VERSION}" --autoCategorize fi -generate_commits_csv_if_needed "${PLATFORM}" "${PREVIOUS_VERSION_REF}" "${RELEASE_BRANCH}" +# commits.csv generation removed (no longer required) commit_and_push_changelog "${VERSION}" "${PREVIOUS_VERSION_REF}" "${CHANGELOG_BRANCH}" "${RELEASE_BRANCH}" From bf1f24c818a4e19312ee54b675b58f6c13d2a6f7 Mon Sep 17 00:00:00 2001 From: Pavel Dvorkin Date: Fri, 17 Oct 2025 13:53:06 -0400 Subject: [PATCH 7/7] Added code review fixes --- .github/scripts/update-release-changelog.sh | 4 ++-- .github/workflows/update-release-changelog.yml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/scripts/update-release-changelog.sh b/.github/scripts/update-release-changelog.sh index ff634935..e6201ea6 100755 --- a/.github/scripts/update-release-changelog.sh +++ b/.github/scripts/update-release-changelog.sh @@ -14,7 +14,7 @@ # so that commits.csv generation is skipped, matching hotfix behaviour. # # Environment (optional): -# GH_TOKEN - Token for GitHub CLI operations (falls back to gh auth config) +# GITHUB_TOKEN - Token for GitHub CLI operations (falls back to gh auth config) # GIT_AUTHOR_NAME - Commit author name (defaults to metamaskbot) # GIT_AUTHOR_EMAIL - Commit author email (defaults to metamaskbot@users.noreply.github.com) # TEST_ONLY - When set to "true" the helper mirrors release automation test mode. @@ -187,7 +187,7 @@ if [[ "${RELEASE_BRANCH}" =~ ^release/([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then VERSION="${BASH_REMATCH[1]}" else echo "Release branch '${RELEASE_BRANCH}' does not match known patterns." >&2 - exit 0 + exit 1 fi GITHUB_REPOSITORY_URL="${REPOSITORY_URL}" diff --git a/.github/workflows/update-release-changelog.yml b/.github/workflows/update-release-changelog.yml index 082991e3..8bf58f8d 100644 --- a/.github/workflows/update-release-changelog.yml +++ b/.github/workflows/update-release-changelog.yml @@ -66,7 +66,6 @@ jobs: shell: bash env: GITHUB_TOKEN: ${{ secrets.github-token }} - GH_TOKEN: ${{ secrets.github-token }} GIT_AUTHOR_NAME: metamaskbot GIT_AUTHOR_EMAIL: metamaskbot@users.noreply.github.com run: |