name: Release Branch Create on: pull_request: types: [closed] branches: [main] paths: - 'package.json' jobs: create-release-branch: runs-on: ubuntu-latest if: > github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'Release') permissions: contents: write steps: - name: Checkout repository uses: actions/checkout@v5 with: fetch-depth: 0 token: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 'lts/*' - name: Check version bump type id: check_version run: | # Get current version from main CURRENT_VERSION=$(node -p "require('./package.json').version") # Remove 'v' prefix if present (shouldn't be in package.json, but defensive) CURRENT_VERSION=${CURRENT_VERSION#v} echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT # Validate version format if ! [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then echo "ERROR: Invalid version format: $CURRENT_VERSION" exit 1 fi # Extract major and minor versions MAJOR=$(echo $CURRENT_VERSION | cut -d. -f1) MINOR=$(echo $CURRENT_VERSION | cut -d. -f2) PATCH=$(echo $CURRENT_VERSION | cut -d. -f3 | cut -d- -f1) echo "major=$MAJOR" >> $GITHUB_OUTPUT echo "minor=$MINOR" >> $GITHUB_OUTPUT echo "patch=$PATCH" >> $GITHUB_OUTPUT # Get previous version from the commit before the merge git checkout HEAD^1 PREV_VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "0.0.0") # Remove 'v' prefix if present PREV_VERSION=${PREV_VERSION#v} # Validate previous version format if ! [[ "$PREV_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then echo "WARNING: Invalid previous version format: $PREV_VERSION, using 0.0.0" PREV_VERSION="0.0.0" fi PREV_MINOR=$(echo $PREV_VERSION | cut -d. -f2) echo "prev_version=$PREV_VERSION" >> $GITHUB_OUTPUT echo "prev_minor=$PREV_MINOR" >> $GITHUB_OUTPUT BASE_COMMIT=$(git rev-parse HEAD) echo "base_commit=$BASE_COMMIT" >> $GITHUB_OUTPUT # Get previous major version for comparison PREV_MAJOR=$(echo $PREV_VERSION | cut -d. -f1) # Check if current version is a pre-release if [[ "$CURRENT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+- ]]; then IS_PRERELEASE=true else IS_PRERELEASE=false fi # Check if this was a minor version bump or major version bump # But skip if it's a pre-release version if [[ "$IS_PRERELEASE" == "true" ]]; then echo "is_minor_bump=false" >> $GITHUB_OUTPUT echo "reason=prerelease version" >> $GITHUB_OUTPUT elif [[ "$MAJOR" -gt "$PREV_MAJOR" && "$MINOR" == "0" && "$PATCH" == "0" ]]; then # Major version bump (e.g., 1.99.x → 2.0.0) echo "is_minor_bump=true" >> $GITHUB_OUTPUT BRANCH_BASE="${PREV_MAJOR}.${PREV_MINOR}" echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT elif [[ "$MAJOR" == "$PREV_MAJOR" && "$MINOR" -gt "$PREV_MINOR" && "$PATCH" == "0" ]]; then # Minor version bump (e.g., 1.23.x → 1.24.0) echo "is_minor_bump=true" >> $GITHUB_OUTPUT BRANCH_BASE="${MAJOR}.${PREV_MINOR}" echo "branch_base=$BRANCH_BASE" >> $GITHUB_OUTPUT else echo "is_minor_bump=false" >> $GITHUB_OUTPUT fi # Return to main branch git checkout main - name: Create release branches id: create_branches if: steps.check_version.outputs.is_minor_bump == 'true' run: | BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}" PREV_VERSION="${{ steps.check_version.outputs.prev_version }}" if [[ -z "$BRANCH_BASE" ]]; then echo "::error::Branch base not set; unable to determine release branches" exit 1 fi BASE_COMMIT="${{ steps.check_version.outputs.base_commit }}" if [[ -z "$BASE_COMMIT" ]]; then echo "::error::Base commit not provided; cannot create release branches" exit 1 fi RESULTS_FILE=$(mktemp) trap 'rm -f "$RESULTS_FILE"' EXIT for PREFIX in core cloud; do BRANCH_NAME="${PREFIX}/${BRANCH_BASE}" if git ls-remote --exit-code --heads origin \ "$BRANCH_NAME" >/dev/null 2>&1; then echo "⚠️ Branch $BRANCH_NAME already exists" echo "ℹ️ Skipping creation for $BRANCH_NAME" STATUS="exists" else # Create branch from the commit BEFORE the version bump if ! git push origin "$BASE_COMMIT:refs/heads/$BRANCH_NAME"; then echo "::error::Failed to push release branch $BRANCH_NAME" exit 1 fi echo "✅ Created release branch: $BRANCH_NAME" STATUS="created" fi echo "$BRANCH_NAME|$STATUS|$PREV_VERSION" >> "$RESULTS_FILE" done { echo "results<> "$GITHUB_OUTPUT" - name: Ensure release labels if: steps.check_version.outputs.is_minor_bump == 'true' env: GH_TOKEN: ${{ secrets.PR_GH_TOKEN || secrets.GITHUB_TOKEN }} run: | set -euo pipefail BRANCH_BASE="${{ steps.check_version.outputs.branch_base }}" if [[ -z "$BRANCH_BASE" ]]; then echo "::error::Branch base not set; unable to manage labels" exit 1 fi declare -A COLORS=( [core]="4361ee" [cloud]="4f6ef5" ) for PREFIX in core cloud; do LABEL="${PREFIX}/${BRANCH_BASE}" COLOR="${COLORS[$PREFIX]}" DESCRIPTION="Backport PRs for ${PREFIX} ${BRANCH_BASE}" if gh label view "$LABEL" >/dev/null 2>&1; then gh label edit "$LABEL" \ --color "$COLOR" \ --description "$DESCRIPTION" echo "🔄 Updated label $LABEL" else gh label create "$LABEL" \ --color "$COLOR" \ --description "$DESCRIPTION" echo "✨ Created label $LABEL" fi done MIN_LABELS_TO_KEEP=3 MAX_LABELS_TO_FETCH=200 for PREFIX in core cloud; do mapfile -t LABELS < <( gh label list \ --json name \ --limit "$MAX_LABELS_TO_FETCH" \ --jq '.[].name' | grep -E "^${PREFIX}/[0-9]+\.[0-9]+$" | sort -t/ -k2,2V ) TOTAL=${#LABELS[@]} if (( TOTAL <= MIN_LABELS_TO_KEEP )); then echo "ℹ️ Nothing to prune for $PREFIX labels" continue fi REMOVE_COUNT=$((TOTAL - MIN_LABELS_TO_KEEP)) if (( REMOVE_COUNT > 1 )); then REMOVE_COUNT=1 fi for ((i=0; i> $GITHUB_STEP_SUMMARY << EOF ## 🌿 Release Branch Summary Release branch creation skipped; no eligible branches were found. EOF exit 0 fi cat >> $GITHUB_STEP_SUMMARY << EOF ## 🌿 Release Branch Summary - **Main branch**: \`$CURRENT_VERSION\` (active development) ### Branch Status EOF while IFS='|' read -r BRANCH STATUS PREV_VERSION; do if [[ "$STATUS" == "created" ]]; then cat >> $GITHUB_STEP_SUMMARY << EOF - \`$BRANCH\` created from version \`$PREV_VERSION\` EOF else cat >> $GITHUB_STEP_SUMMARY << EOF - \`$BRANCH\` already existed (based on version \`$PREV_VERSION\`) EOF fi done <<< "$RESULTS" cat >> $GITHUB_STEP_SUMMARY << EOF ### Branch Policy Release branches are feature-frozen and only accept: - 🐛 Bug fixes - 🔒 Security patches - 📚 Documentation updates All new features should continue to be developed against \`main\`. ### Backporting Changes To backport a fix: 1. Create your fix on \`main\` first 2. Cherry-pick to the target release branch 3. Create a PR targeting that branch 4. Apply the matching \`core/x.y\` or \`cloud/x.y\` label EOF