From 3b50c0bb6ef181b85837f23e83ab5e22270de291 Mon Sep 17 00:00:00 2001 From: Sterling Dreyer Date: Tue, 10 Jun 2025 13:12:04 -0700 Subject: [PATCH] Add promotion workflow (#422) --- .github/workflows/promote.yml | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 .github/workflows/promote.yml diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml new file mode 100644 index 00000000..e2dc15fa --- /dev/null +++ b/.github/workflows/promote.yml @@ -0,0 +1,163 @@ +name: Promote to Production + +on: + workflow_dispatch: + inputs: + commit_sha: + description: 'Specific commit SHA to cherry-pick (leave empty to promote everything in main)' + type: string + required: false + default: '' + +jobs: + promote: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.PROMOTE_PAT_TOKEN }} + + - name: Configure Git + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Setup branches + run: | + # Ensure we have latest changes + git fetch origin + + # Create production branch if it doesn't exist + if ! git show-ref --verify --quiet refs/remotes/origin/production; then + echo "Creating production branch from main" + git checkout -b production origin/main + git push origin production + else + echo "Production branch already exists" + git checkout production + git pull origin production + fi + + # Ensure main is up to date + git checkout main + git pull origin main + + - name: Cherry-pick specific commit + if: ${{ inputs.commit_sha != '' }} + run: | + echo "Cherry-picking specific commit: ${{ inputs.commit_sha }}" + + # Validate commit exists in main + if ! git cat-file -e "${{ inputs.commit_sha }}^{commit}"; then + echo "Error: Commit ${{ inputs.commit_sha }} does not exist" + exit 1 + fi + + # Check if commit is in main branch + if ! git merge-base --is-ancestor "${{ inputs.commit_sha }}" main; then + echo "Error: Commit ${{ inputs.commit_sha }} is not in main branch" + exit 1 + fi + + # Switch to production and cherry-pick + git checkout production + + # Check if commit is already in production + if git merge-base --is-ancestor "${{ inputs.commit_sha }}" production; then + echo "Commit ${{ inputs.commit_sha }} is already in production branch" + exit 0 + fi + + # Cherry-pick the commit + COMMIT_MSG=$(git log -1 --pretty=format:"%s" "${{ inputs.commit_sha }}") + echo "Cherry-picking: $COMMIT_MSG" + + if git cherry-pick "${{ inputs.commit_sha }}"; then + echo "✅ Successfully cherry-picked commit" + git push origin production + echo "commit_promoted=true" >> $GITHUB_ENV + else + echo "❌ Cherry-pick failed - conflicts may need resolution" + git cherry-pick --abort + exit 1 + fi + + - name: Recreate production from main + if: ${{ inputs.commit_sha == '' }} + run: | + echo "Recreating production branch from main" + + # Get current commit on main for summary + MAIN_COMMIT=$(git rev-parse main) + MAIN_COMMIT_MSG=$(git log -1 --pretty=format:"%s" main) + echo "Main branch is at: $MAIN_COMMIT ($MAIN_COMMIT_MSG)" + + # Check if production branch exists remotely + if git show-ref --verify --quiet refs/remotes/origin/production; then + PROD_COMMIT=$(git rev-parse origin/production) + PROD_COMMIT_MSG=$(git log -1 --pretty=format:"%s" origin/production) + echo "Current production is at: $PROD_COMMIT ($PROD_COMMIT_MSG)" + + # Check if they're already the same + if [ "$MAIN_COMMIT" = "$PROD_COMMIT" ]; then + echo "Production is already up to date with main" + echo "production_updated=false" >> $GITHUB_ENV + exit 0 + fi + + echo "Deleting existing production branch" + git push origin --delete production + + # Also delete local production branch if it exists + if git show-ref --verify --quiet refs/heads/production; then + echo "Deleting local production branch" + git branch -D production + fi + else + echo "Production branch doesn't exist, will create new one" + fi + + # Create new production branch from main + echo "Creating new production branch from main" + git checkout main + git checkout -b production + git push origin production + + echo "✅ Successfully recreated production branch from main" + echo "production_updated=true" >> $GITHUB_ENV + echo "main_commit=$MAIN_COMMIT" >> $GITHUB_ENV + + - name: Create summary + if: always() + run: | + echo "## Promotion Summary" >> $GITHUB_STEP_SUMMARY + + if [ "${{ inputs.commit_sha }}" != "" ]; then + echo "### Single Commit Promotion" >> $GITHUB_STEP_SUMMARY + echo "**Target Commit:** \`${{ inputs.commit_sha }}\`" >> $GITHUB_STEP_SUMMARY + + if [ "${commit_promoted:-false}" = "true" ]; then + echo "✅ **Status:** Successfully promoted to production" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Status:** Promotion failed or commit already existed" >> $GITHUB_STEP_SUMMARY + fi + else + echo "### Bulk Promotion" >> $GITHUB_STEP_SUMMARY + + if [ "${production_updated:-false}" = "true" ]; then + echo "✅ **Status:** Successfully updated production branch" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ **Status:** No new commits to promote or production already up to date" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Branches" >> $GITHUB_STEP_SUMMARY + echo "- **Source:** main" >> $GITHUB_STEP_SUMMARY + echo "- **Target:** production" >> $GITHUB_STEP_SUMMARY