diff --git a/.github/scripts/create-platform-release-pr.sh b/.github/scripts/create-platform-release-pr.sh index 8d47e749..dc218254 100755 --- a/.github/scripts/create-platform-release-pr.sh +++ b/.github/scripts/create-platform-release-pr.sh @@ -1,14 +1,22 @@ #!/usr/bin/env bash +# Script to create platform release PRs for MetaMask +# This script handles the creation of release PRs for both mobile and extension platforms +# It creates two PRs: +# 1. A release PR with version updates +# 2. A changelog PR with updated changelog and test plan + set -e set -u set -o pipefail +# Input validation PLATFORM="${1}" PREVIOUS_VERSION="${2}" NEW_VERSION="${3}" NEW_VERSION_NUMBER="${4:-}" +# Validate required parameters if [[ -z $PLATFORM ]]; then echo "Error: No platform specified." exit 1 @@ -24,8 +32,14 @@ if [[ -z $NEW_VERSION_NUMBER && $PLATFORM == "mobile" ]]; then exit 1 fi -get_expected_changed_files() { + + +# Helper Functions +# --------------- + +# Returns a space-separated list of files that are expected to change for a given platform +get_expected_changed_files() { local platform="$1" local expected_changed_files="" @@ -41,37 +55,37 @@ get_expected_changed_files() { echo "$expected_changed_files" } -# Returns the release branch name to use for the given platform +# Returns the release branch name based on platform and version +# For all platforms: release/{version} +# If TEST_ONLY=true: release-testing/{version} get_release_branch_name() { - # Input arguments - local platform="$1" # Platform can be 'mobile' or 'extension' - local new_version="$2" # Semantic version, e.g., '12.9.2' + local platform="$1" + local new_version="$2" - # Check if TEST_ONLY environment variable is set to "true" - # This is to run a development version of the release process without impacting downstream automation - if [ "$TEST_ONLY" == "true" ]; then - echo "release-testing/${new_version}" - return 0 + # Validate platform + if [[ "$platform" != "mobile" && "$platform" != "extension" ]]; then + echo "Error: Unknown platform '$platform'. Must be 'mobile' or 'extension'." + exit 1 fi - # Determine the release branch name based on platform - if [ "$platform" == "mobile" ]; then - RELEASE_BRANCH_NAME="release/${new_version}" - echo "${RELEASE_BRANCH_NAME}" - elif [ "$platform" == "extension" ]; then - RELEASE_BRANCH_NAME="Version-v${new_version}" - echo "${RELEASE_BRANCH_NAME}" - else - echo "Error: Unknown platform '$platform'. Must be 'mobile' or 'extension'." - exit 1 + # Use test branch if TEST_ONLY is true + if [ "$TEST_ONLY" == "true" ]; then + echo "release-testing/${new_version}" + return 0 fi -} + # Use consistent release branch naming for all platforms + echo "release/${new_version}" +} +# Main Script +# ---------- +# Initialize branch names RELEASE_BRANCH_NAME=$(get_release_branch_name $PLATFORM $NEW_VERSION) CHANGELOG_BRANCH_NAME="chore/${NEW_VERSION}-Changelog" +# Prepare release PR body with team sign-off checklist RELEASE_BODY="This is the release candidate for version ${NEW_VERSION}. The changelog will be found in another PR ${CHANGELOG_BRANCH_NAME}. # Team sign-off checklist @@ -91,6 +105,8 @@ RELEASE_BODY="This is the release candidate for version ${NEW_VERSION}. The chan # Reference - Testing plan sheet - https://docs.google.com/spreadsheets/d/1tsoodlAlyvEUpkkcNcbZ4PM9HuC9cEM80RZeoVv5OCQ/edit?gid=404070372#gid=404070372" +# Git Configuration +# ---------------- echo "Configuring git.." git config user.name metamaskbot git config user.email metamaskbot@users.noreply.github.com @@ -98,7 +114,8 @@ git config user.email metamaskbot@users.noreply.github.com echo "Fetching from remote..." git fetch -# Check out the existing release branch from the remote +# Release Branch Setup +# ------------------- echo "Checking out the release branch: ${RELEASE_BRANCH_NAME}" git checkout "${RELEASE_BRANCH_NAME}" @@ -107,18 +124,15 @@ echo "Release Branch Checked Out" echo "version : ${NEW_VERSION}" echo "platform : ${PLATFORM}" +# Version Updates +# -------------- echo "Running version update scripts.." -# Bump versions for the release ./github-tools/.github/scripts/set-semvar-version.sh "${NEW_VERSION}" ${PLATFORM} -if [[ "$PLATFORM" == "mobile" ]]; then - ./github-tools/.github/scripts/set-mobile-build-version.sh "${NEW_VERSION_NUMBER}" -fi - +# Commit Changes +# ------------- changed_files=$(get_expected_changed_files "$PLATFORM") - -# Echo the files to be added echo "Files to be staged for commit: $changed_files" echo "Adding and committing changes.." @@ -126,33 +140,62 @@ echo "Adding and committing changes.." # Track our changes git add $changed_files -# Generate a commit based on PLATFORM +# Generate commit message based on platform if [ "$PLATFORM" = "mobile" ]; then - git commit -m "bump semvar version to ${NEW_VERSION} && build version to ${NEW_VERSION_NUMBER}" + if ! git commit -m "bump semvar version to ${NEW_VERSION} && build version to ${NEW_VERSION_NUMBER}"; then + echo "No changes to commit for mobile version bump" + fi elif [ "$PLATFORM" = "extension" ]; then - git commit -m "bump semvar version to ${NEW_VERSION}" + if ! git commit -m "bump semvar version to ${NEW_VERSION}"; then + echo "No changes to commit for extension version bump" + fi fi - +# Push Changes and Create Release PR +# --------------------------------- echo "Pushing changes to the remote.." -git push --set-upstream origin "${RELEASE_BRANCH_NAME}" - -echo Creating release PR.. - -gh pr create \ - --draft \ - --title "feat: ${NEW_VERSION}" \ - --body "${RELEASE_BODY}" \ - --head "${RELEASE_BRANCH_NAME}"; +if ! git push --set-upstream origin "${RELEASE_BRANCH_NAME}"; then + echo "No changes to push to ${RELEASE_BRANCH_NAME}" + # Check if branch exists remotely + if git ls-remote --heads origin "${RELEASE_BRANCH_NAME}" | grep -q "${RELEASE_BRANCH_NAME}"; then + echo "Branch ${RELEASE_BRANCH_NAME} already exists remotely" + else + echo "Error: Failed to push and branch doesn't exist remotely" + exit 1 + fi +fi -echo "Release PR Created" +echo "Creating release PR.." +# Check if PR already exists +if gh pr list --head "${RELEASE_BRANCH_NAME}" --json number --jq 'length' | grep -q "1"; then + echo "PR for branch ${RELEASE_BRANCH_NAME} already exists" +else + gh pr create \ + --draft \ + --title "feat: ${NEW_VERSION}" \ + --body "${RELEASE_BODY}" \ + --head "${RELEASE_BRANCH_NAME}" + echo "Release PR Created" +fi -echo "Checking out ${CHANGELOG_BRANCH_NAME}" -git checkout -b "${CHANGELOG_BRANCH_NAME}" -echo "Changelog Branch Created" +# Changelog Branch Setup +# --------------------- +echo "Checking for existing changelog branch ${CHANGELOG_BRANCH_NAME}" + +# Check if branch exists locally or remotely +if git show-ref --verify --quiet refs/heads/"${CHANGELOG_BRANCH_NAME}" || git ls-remote --heads origin "${CHANGELOG_BRANCH_NAME}" | grep -q "${CHANGELOG_BRANCH_NAME}"; then + echo "Branch ${CHANGELOG_BRANCH_NAME} already exists, checking it out" + git fetch origin "${CHANGELOG_BRANCH_NAME}" + git checkout "${CHANGELOG_BRANCH_NAME}" +else + echo "Creating new branch ${CHANGELOG_BRANCH_NAME}" + git checkout -b "${CHANGELOG_BRANCH_NAME}" +fi +echo "Changelog Branch Ready" +# Generate Changelog and Test Plan +# ------------------------------ echo "Generating changelog via auto-changelog.." - npx @metamask/auto-changelog@4.1.0 update --rc --repo "${GITHUB_REPOSITORY_URL}" --currentVersion "${NEW_VERSION}" --autoCategorize # Need to run from .github-tools context to inherit it's dependencies/environment @@ -163,14 +206,19 @@ ls -ltra corepack prepare yarn@4.5.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 + echo "Generating test plan csv.." yarn run gen:commits "${PLATFORM}" "${PREVIOUS_VERSION}" "${RELEASE_BRANCH_NAME}" "${PROJECT_GIT_DIR}" -echo "Updating release sheet.." -# Create a new Release Sheet Page for the new version with our commits.csv content -yarn run update-release-sheet "${PLATFORM}" "${NEW_VERSION}" "${GOOGLE_DOCUMENT_ID}" "./commits.csv" "${PROJECT_GIT_DIR}" "${MOBILE_TEMPLATE_SHEET_ID}" "${EXTENSION_TEMPLATE_SHEET_ID}" +if [[ "${TEST_ONLY:-false}" == 'false' ]]; then + echo "Updating release sheet.." + # Create a new Release Sheet Page for the new version with our commits.csv content + yarn run update-release-sheet "${PLATFORM}" "${NEW_VERSION}" "${GOOGLE_DOCUMENT_ID}" "./commits.csv" "${PROJECT_GIT_DIR}" "${MOBILE_TEMPLATE_SHEET_ID}" "${EXTENSION_TEMPLATE_SHEET_ID}" +fi cd ../ +# Commit and Push Changelog Changes +# ------------------------------- echo "Adding and committing changes.." git add ./commits.csv @@ -186,12 +234,20 @@ PR_BODY="This PR updates the change log for ${NEW_VERSION} and generates the tes echo "Pushing changes to the remote.." git push --set-upstream origin "${CHANGELOG_BRANCH_NAME}" -echo Creating release PR.. -gh pr create \ - --draft \ - --title "chore: ${CHANGELOG_BRANCH_NAME}" \ - --body "${PR_BODY}" \ - --base "${RELEASE_BRANCH_NAME}" \ - --head "${CHANGELOG_BRANCH_NAME}"; +# Create Changelog PR +# ----------------- +echo "Creating changelog PR.." +# Check if PR already exists +if gh pr list --search "head:${CHANGELOG_BRANCH_NAME}" --json number --jq 'length' | grep -q "1"; then + echo "Changelog PR for branch ${CHANGELOG_BRANCH_NAME} already exists" +else + gh pr create \ + --draft \ + --title "chore: ${CHANGELOG_BRANCH_NAME}" \ + --body "${PR_BODY}" \ + --base "${RELEASE_BRANCH_NAME}" \ + --head "${CHANGELOG_BRANCH_NAME}" + echo "Changelog PR Created" +fi -echo "Changelog PR Created" +echo "Changelog PR Ready" diff --git a/.github/scripts/set-semvar-version.sh b/.github/scripts/set-semvar-version.sh index 5b6e0b06..8cea5a0e 100755 --- a/.github/scripts/set-semvar-version.sh +++ b/.github/scripts/set-semvar-version.sh @@ -1,81 +1,97 @@ #!/usr/bin/env bash +# Script to update semantic versioning across MetaMask platform files +# This script handles version updates for both mobile and extension platforms +# For mobile: Updates package.json, Android build.gradle, Bitrise config, and iOS project files +# For extension: Updates package.json only + set -e set -u set -o pipefail -# Check if exactly 2 arguments are provided +# Input validation if [[ $# -ne 2 ]]; then - echo "Usage: $0 " + echo "Usage: $0 " exit 1 fi +# Script parameters SEMVER_VERSION=$1 PLATFORM=$2 -NAT='0|[1-9][0-9]*' -ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*' -IDENT="$NAT|$ALPHANUM" -FIELD='[0-9A-Za-z-]+' +# Regular expression patterns for semantic version validation +NAT='0|[1-9][0-9]*' # Non-negative integer +ALPHANUM='[0-9]*[A-Za-z-][0-9A-Za-z-]*' # Alphanumeric identifier +IDENT="$NAT|$ALPHANUM" # Identifier (number or alphanumeric) +FIELD='[0-9A-Za-z-]+' # Field (alphanumeric with hyphens) +# Full semantic version regex pattern +# Matches: v1.2.3-alpha.1+build.123 SEMVER_REGEX="\ ^[vV]?\ ($NAT)\\.($NAT)\\.($NAT)\ (\\-(${IDENT})(\\.(${IDENT}))*)?\ (\\+${FIELD}(\\.${FIELD})*)?$" +# File paths for version updates PACKAGE_JSON_FILE=package.json ANDROID_BUILD_GRADLE_FILE=android/app/build.gradle BITRISE_YML_FILE=bitrise.yml IOS_PROJECT_FILE=ios/MetaMask.xcodeproj/project.pbxproj +# Helper Functions +# --------------- + +# Converts semantic version to numeric format (e.g., 1.2.3 -> 123) semver_to_nat () { echo "${1//./}" } +# Logs error message and exits with status code 1 log_and_exit () { echo "$1" && exit 1 } +# Updates version in package.json using jq package_json() { - - # update package.json + # Create temporary file for jq operation tmp="${PACKAGE_JSON_FILE}_temp" jq ".version = \"$SEMVER_VERSION\"" $PACKAGE_JSON_FILE > "$tmp" mv "$tmp" $PACKAGE_JSON_FILE echo "- $PACKAGE_JSON_FILE updated" - } +# Updates version in mobile-specific files +# Handles both macOS and Linux environments update_mobile_files () { - # Check operating system and execute platform-specific sed commands if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS - - # update android/app/build.gradle + # macOS specific updates + + # Update Android version in build.gradle echo "Updating Android build.gradle file..." sed -i '' 's/\(\s*versionName \)".*"/\1"'"$SEMVER_VERSION"'"/' "$ANDROID_BUILD_GRADLE_FILE" echo "- $ANDROID_BUILD_GRADLE_FILE successfully updated" - # update bitrise.yml + # Update version in Bitrise configuration echo "Updating Bitrise configuration files..." sed -i '' 's/\(\s*VERSION_NAME: \).*/\1'"$SEMVER_VERSION"'/' "$BITRISE_YML_FILE" echo "- $BITRISE_YML_FILE successfully updated" + # Update iOS marketing version echo "Updating iOS project settings..." sed -i '' 's/\(\s*MARKETING_VERSION = \).*/\1'"$SEMVER_VERSION;"'/' "$IOS_PROJECT_FILE" echo "- $IOS_PROJECT_FILE successfully updated" else - # Linux - - # update android/app/build.gradle + # Linux specific updates + + # Update Android version in build.gradle echo "Updating Android build.gradle file..." sed -i 's/\(\s*versionName \)".*"/\1"'"$SEMVER_VERSION"'"/' "$ANDROID_BUILD_GRADLE_FILE" echo "- $ANDROID_BUILD_GRADLE_FILE updated" - # update bitrise.yml + # Update version in Bitrise configuration echo "Updating Bitrise configuration files..." sed -i 's/\(\s*VERSION_NAME: \).*/\1'"$SEMVER_VERSION"'/' "$BITRISE_YML_FILE" echo "- $BITRISE_YML_FILE updated" @@ -84,9 +100,9 @@ update_mobile_files () { echo "Updating iOS project settings..." sed -i 's/\(\s*MARKETING_VERSION = \).*/\1'"$SEMVER_VERSION;"'/' "$IOS_PROJECT_FILE" echo "- $IOS_PROJECT_FILE updated" - fi + # Print summary of updates echo "- $ANDROID_BUILD_GRADLE_FILE updated" echo "- $BITRISE_YML_FILE updated" echo "- $IOS_PROJECT_FILE updated" @@ -96,16 +112,32 @@ update_mobile_files () { echo "SEMVER version: $SEMVER_VERSION" } -# abort if values are empty +# Main Script +# ---------- + +# Validate input parameters if [[ -z $SEMVER_VERSION ]]; then log_and_exit "SEMVER_VERSION not specified, aborting!" fi -# check if SEMVER_VERSION is not valid semver +# Validate semantic version format if ! [[ $SEMVER_VERSION =~ $SEMVER_REGEX ]]; then log_and_exit "$SEMVER_VERSION is invalid semver!" fi +# Validate inputs for mobile platform +if [[ $PLATFORM == "mobile" ]]; then + # Get current version numbers from bitrise.yml + CURRENT_VERSION_NUMBER=$(awk '/^\s+VERSION_NUMBER: /{print $2}' $BITRISE_YML_FILE); + CURRENT_FLASK_VERSION_NUMBER=$(awk '/^\s+FLASK_VERSION_NUMBER: /{print $2}' $BITRISE_YML_FILE); + + # Ensure version number of main variant and flask are aligned + if [[ "$CURRENT_VERSION_NUMBER" != "$CURRENT_FLASK_VERSION_NUMBER" ]]; then + echo "VERSION_NUMBER $CURRENT_VERSION_NUMBER and FLASK_VERSION_NUMBER $CURRENT_FLASK_VERSION_NUMBER should be the same" + log_and_exit "Check why they are different and fix it before proceeding" + fi +fi + echo "SEMVER_VERSION is valid." echo -e "-------------------" echo "Updating files:" @@ -114,6 +146,7 @@ echo "Updating files:" # Update the version in the package.json file package_json +# Platform-specific updates if [[ $PLATFORM == "mobile" ]]; then update_mobile_files fi diff --git a/.github/workflows/create-release-pr.yml b/.github/workflows/create-release-pr.yml index 0a3dcba5..fec86dcd 100644 --- a/.github/workflows/create-release-pr.yml +++ b/.github/workflows/create-release-pr.yml @@ -45,6 +45,10 @@ on: required: true type: string description: 'The platform for which the release PR is being created.' + github-tools-version: + type: string + description: 'The version of github-tools to use. Defaults to main.' + default: 'main' secrets: github-token: required: true @@ -55,6 +59,7 @@ on: jobs: create-release-pr: + name: Create Release Pull Request runs-on: ubuntu-latest permissions: contents: write @@ -73,7 +78,7 @@ jobs: uses: actions/checkout@v4 with: repository: MetaMask/github-tools - ref: main + ref: ${{ inputs.github-tools-version }} path: github-tools # Step 3: Setup environment from github-tools @@ -82,8 +87,27 @@ jobs: with: is-high-risk-environment: true - # Step 4: Execute the script with access to both repositories - - name: Create Release + # Step 4: Print Input Values + - name: Print Input Values + run: | + echo "Input Values:" + echo "-------------" + echo "Platform: ${{ inputs.platform }}" + echo "Base Branch: ${{ inputs.base-branch }}" + echo "Semver Version: ${{ inputs.semver-version }}" + echo "Previous Version Tag: ${{ inputs.previous-version-tag }}" + echo "Test Only Mode: ${{ inputs.test-only }}" + if [[ "${{ inputs.platform }}" == "mobile" ]]; then + echo "Mobile Build Version: ${{ inputs.mobile-build-version }}" + fi + echo "Mobile Template Sheet ID: ${{ inputs.mobile-template-sheet-id }}" + echo "Extension Template Sheet ID: ${{ inputs.extension-template-sheet-id }}" + echo "Release Sheet Google Document ID: ${{ inputs.release-sheet-google-document-id }}" + echo "GitHub Tools Version: ${{ inputs.github-tools-version }}" + echo "-------------" + + # Step 5: Create Release PR + - name: Create Release PR id: create-release-pr shell: bash env: