Skip to content
Merged
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
29 changes: 7 additions & 22 deletions .github/actions/create-release-pr/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ inputs:
google-application-creds-base64:
description: 'Google application credentials base64 encoded.'
required: true
github-tools-repository:
description: 'The GitHub repository containing the GitHub tools. Defaults to the GitHub tools action repositor, and usually does not need to be changed.'
required: false
default: ${{ github.action_repository }}
github-tools-ref:
description: 'The SHA of the action to use. Defaults to the current action ref, and usually does not need to be changed.'
required: false
default: ${{ github.action_ref }}

runs:
using: composite
Expand All @@ -68,21 +60,13 @@ runs:
ref: ${{ inputs.checkout-base-branch }}
token: ${{ inputs.github-token }}

# Step 2: Checkout github-tools repository
- name: Checkout github-tools repository
uses: actions/checkout@v6
with:
repository: ${{ inputs.github-tools-repository }}
ref: ${{ inputs.github-tools-ref }}
path: github-tools

# Step 3: Setup environment
# Step 2: Setup environment
- name: Checkout and setup environment
uses: MetaMask/action-checkout-and-setup@v3
with:
is-high-risk-environment: true

# Step 4: Print Input Values
# Step 3: Print Input Values
- name: Print Input Values
env:
PLATFORM: ${{ inputs.platform }}
Expand Down Expand Up @@ -117,7 +101,7 @@ runs:
echo "Git User Email: $GIT_USER_EMAIL"
echo "-------------"

# Step 5: Create Release PR
# Step 4: Create Release PR
- name: Create Release PR
id: create-release-pr
shell: bash
Expand All @@ -137,18 +121,19 @@ runs:
MOBILE_BUILD_VERSION: ${{ inputs.mobile-build-version }}
GIT_USER_NAME: ${{ inputs.git-user-name }}
GIT_USER_EMAIL: ${{ inputs.git-user-email }}
GITHUB_TOOLS_DIR: ${{ github.action_path }}/../../..
working-directory: ${{ github.workspace }}
run: |
# Execute the script from github-tools
./github-tools/.github/scripts/create-platform-release-pr.sh \
# Use the action revision GitHub resolved for this run.
"$GITHUB_TOOLS_DIR/.github/scripts/create-platform-release-pr.sh" \
"$PLATFORM" \
"$PREVIOUS_VERSION_REF" \
"$SEMVER_VERSION" \
"$MOBILE_BUILD_VERSION" \
"$GIT_USER_NAME" \
"$GIT_USER_EMAIL"

# Step 6: Upload commits.csv as artifact (if generated)
# Step 5: Upload commits.csv as artifact (if generated)
- name: Upload commits.csv artifact
if: ${{ hashFiles('commits.csv') != '' }}
uses: actions/upload-artifact@v7
Expand Down
62 changes: 31 additions & 31 deletions .github/actions/update-release-changelog/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,48 +12,42 @@ inputs:
repository-url:
required: true
description: 'Full HTTPS URL for the invoking repository.'
previous-version-ref:
required: false
default: 'null'
description: 'Previous release version reference (branch/tag/SHA). Use "null" for hotfixes.'
github-token:
required: true
description: 'GitHub token with write access to the invoking repository and read access to planning resources.'
github-tools-repository:
description: 'The GitHub repository containing the GitHub tools. Defaults to the GitHub tools action repositor, and usually does not need to be changed.'
required: false
default: ${{ github.action_repository }}
github-tools-ref:
description: 'The SHA of the action to use. Defaults to the current action ref, and usually does not need to be changed.'
required: false
default: ${{ github.action_ref }}

runs:
using: composite
steps:
# Step 1: Checkout invoking repository (metamask-mobile | metamask-extension)
- name: Checkout invoking repository
uses: actions/checkout@v6
env:
AI_ANALYZER_LITELLM_KEY: ''
with:
fetch-depth: 0
ref: ${{ inputs.release-branch }}
token: ${{ inputs.github-token }}

# Step 2: Checkout github-tools repository
- name: Checkout GitHub tools repository
uses: actions/checkout@v6
- name: Set up Node 24 for release changelog automation
uses: actions/setup-node@v6
env:
AI_ANALYZER_LITELLM_KEY: ''
with:
repository: ${{ inputs.github-tools-repository }}
ref: ${{ inputs.github-tools-ref }}
path: ./github-tools
node-version: '24'

# Step 3: Setup environment
- name: Checkout and setup environment
uses: MetaMask/action-checkout-and-setup@v3
with:
is-high-risk-environment: true
# Corepack resolves Yarn from the working directory, so install from the
# github-tools root to use its own pinned Yarn and lockfile.
- name: Install github-tools dependencies
shell: bash
working-directory: ${{ github.action_path }}/../../..
env:
AI_ANALYZER_LITELLM_KEY: ''
run: |
set -euo pipefail
corepack enable
corepack yarn install --immutable

# Step 4: Update changelog using shared helper script
- name: Update changelog branch
shell: bash
env:
Expand All @@ -63,15 +57,21 @@ runs:
RELEASE_BRANCH: ${{ inputs.release-branch }}
PLATFORM: ${{ inputs.platform }}
REPOSITORY_URL: ${{ inputs.repository-url }}
PREVIOUS_VERSION_REF: ${{ inputs.previous-version-ref }}
AUTO_CHANGELOG_CLI: ${{ github.action_path }}/../../../node_modules/@metamask/auto-changelog/dist/cli.js
run: |
set -euo pipefail

corepack enable
yarn install --immutable

./github-tools/.github/scripts/update-release-changelog.sh \
node "$GITHUB_ACTION_PATH/../../scripts/update-release-changelog.mts" \
"$RELEASE_BRANCH" \
"$PLATFORM" \
"$REPOSITORY_URL" \
"$PREVIOUS_VERSION_REF"
"$REPOSITORY_URL"

- name: Upload clean-room proofread report
if: ${{ always() && hashFiles('.tmp/release-changelog-ai/*.json') != '' }}
uses: actions/upload-artifact@v7
env:
AI_ANALYZER_LITELLM_KEY: ''
with:
name: release-changelog-ai-report
path: .tmp/release-changelog-ai/*.json
if-no-files-found: ignore
3 changes: 2 additions & 1 deletion .github/scripts/clean-release-changelog.mts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ function assertNotAdjacentListItems(line: string): void {

/**
* Groups a model response's entries by category, rejecting stray Markdown.
* It is deliberately strict because the input is untrusted model output.
* This is stricter than `parseReleaseSectionCategories` because the input is
* untrusted model output, not auto-changelog's own format.
*
* @param section - One chunk's cleaned section.
* @param version - The release version.
Expand Down
71 changes: 25 additions & 46 deletions .github/scripts/create-platform-release-pr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

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

set -e
set -u
set -o pipefail

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

Expand Down Expand Up @@ -145,7 +145,6 @@ create_release_pr() {
local new_version="$2"
local new_version_number="$3"
local release_branch_name="$4"
local changelog_branch_name="$5"

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

# Version Updates
echo "Running version update scripts.."
./github-tools/.github/scripts/set-semvar-version.sh "${new_version}" "${platform}"
"${GITHUB_TOOLS_DIR}/.github/scripts/set-semvar-version.sh" "${new_version}" "${platform}"

# Commit Changes
local changed_files
Expand Down Expand Up @@ -251,21 +250,20 @@ Many thanks in advance
create_pr_if_not_exists "${release_branch_name}" "release: ${new_version}" "${release_body}" "${BASE_BRANCH}" "" "head"
}

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

# Skip commits.csv for hotfix releases (previous_version_ref is literal "null")
# - 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.
# - 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'.
if [[ "${previous_version_ref,,}" == "null" ]]; then
echo "Hotfix release detected (previous-version-ref is 'null'); skipping commits.csv generation."
else
# Need to run from .github-tools context to inherit it's dependencies/environment
# Generate the CSV with GitHub Tools' own dependencies, not the consumer's.
echo "Current Directory: $(pwd)"
PROJECT_GIT_DIR=$(pwd)

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

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

echo "Generating test plan csv.."
yarn run gen:commits "${platform}" "${DIFF_BASE}" "${release_branch_name}" "${PROJECT_GIT_DIR}"
# Return to project root after generating commits.csv
cd ../
# The action checkout is not nested under the consumer repository.
cd "${PROJECT_GIT_DIR}"
fi

# Delegate changelog update and PR creation to the shared update-release-changelog.sh script
echo "Updating changelog and creating PR.."

# Export git identity for the shared script
export GIT_AUTHOR_NAME="${GIT_USER_NAME}"
export GIT_AUTHOR_EMAIL="${GIT_USER_EMAIL}"

# Call the shared script
# The script is located in the same directory as this one
"${SCRIPT_DIR}/update-release-changelog.sh" \
"${release_branch_name}" \
"${platform}" \
"${GITHUB_REPOSITORY_URL}" \
"${previous_version_ref}" \
"${changelog_branch_name}" \
"${new_version}"

echo "Changelog PR Ready"
}

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

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

# Commit version bump changes
echo "Committing version bump changes.."
Expand Down Expand Up @@ -436,20 +415,20 @@ main() {
next_version=$(get_next_version "$NEW_VERSION")

# Initialize branch names
local release_branch_name changelog_branch_name version_bump_branch_name
local release_branch_name version_bump_branch_name
release_branch_name=$(get_release_branch_name "$NEW_VERSION")
changelog_branch_name="release-changelog/${NEW_VERSION}"
version_bump_branch_name=$(get_version_bump_branch_name "$next_version") # Execute main workflow
configure_git "${GIT_USER_NAME}" "${GIT_USER_EMAIL}"

# Step 1: Create release branch and PR
create_release_pr "$PLATFORM" "$NEW_VERSION" "$NEW_VERSION_NUMBER" "$release_branch_name" "$changelog_branch_name"
create_release_pr "$PLATFORM" "$NEW_VERSION" "$NEW_VERSION_NUMBER" "$release_branch_name"

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

# Step 3: Create version bump PR for main branch (skip for hotfix releases)
Expand All @@ -467,7 +446,7 @@ main() {
echo "Created PRs:"
echo "1. Release PR: release: ${NEW_VERSION}"
if [ "$TEST_ONLY" != "true" ]; then
echo "2. Changelog PR: release: ${changelog_branch_name}"
echo "2. Changelog PR: generated by the release-branch workflow"
if [[ "${PREVIOUS_VERSION_REF,,}" == "null" ]]; then
echo "(Hotfix) Skipped version bump PR"
else
Expand Down
22 changes: 2 additions & 20 deletions .github/scripts/merge-previous-release-changelog.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,7 @@ import {
hasSingleReleaseHeading,
LINK_REFERENCE,
} from './release-section.mts';

/**
* Compares two `major.minor.patch` versions.
* @param left - The first version.
* @param right - The second version.
* @returns A positive number when `left` is newer, negative when older, else 0.
*/
function compareVersions(left: string, right: string): number {
const leftParts = left.split('.').map(Number);
const rightParts = right.split('.').map(Number);

for (let index = 0; index < leftParts.length; index += 1) {
const difference = (leftParts[index] ?? 0) - (rightParts[index] ?? 0);
if (difference !== 0) {
return difference;
}
}
return 0;
}
import { compareSemver } from './release-version-utils.mts';

/**
* Finds the `[version]: url` link reference for a release.
Expand Down Expand Up @@ -81,7 +63,7 @@ function findInsertionIndex(
if (
candidate &&
match.index !== undefined &&
compareVersions(version, candidate) > 0
compareSemver(version, candidate) > 0
) {
return match.index;
}
Expand Down
Loading
Loading