.github/workflows/promote-to-staging.yml #64
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Promote Dev to Staging | ||
| # Manual promotion workflow: dev → staging | ||
| # Validates dev branch and creates PR requiring manual approval | ||
| on: | ||
| workflow_dispatch: # Manual trigger only | ||
| inputs: | ||
| skip_checks: | ||
| description: 'Skip CI checks (emergency only)' | ||
| required: false | ||
| default: 'false' | ||
| type: choice | ||
| options: | ||
| - 'false' | ||
| - 'true' | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| issues: write | ||
| jobs: | ||
| # Validate dev branch before promotion | ||
| validate-dev: | ||
| name: "Validate Dev Branch" | ||
| runs-on: ubuntu-latest | ||
| if: github.repository == 'ScheierVentures/emburden' && inputs.skip_checks != 'true' | ||
| outputs: | ||
| dev_sha: ${{ steps.get_sha.outputs.dev_sha }} | ||
| dev_short_sha: ${{ steps.get_sha.outputs.dev_short_sha }} | ||
| steps: | ||
| - name: Checkout dev branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: dev | ||
| fetch-depth: 0 | ||
| - name: Get dev branch SHA | ||
| id: get_sha | ||
| run: | | ||
| DEV_SHA=$(git rev-parse HEAD) | ||
| DEV_SHORT_SHA=$(git rev-parse --short HEAD) | ||
| echo "dev_sha=$DEV_SHA" >> $GITHUB_OUTPUT | ||
| echo "dev_short_sha=$DEV_SHORT_SHA" >> $GITHUB_OUTPUT | ||
| echo "Dev branch SHA: $DEV_SHA" | ||
| - name: Setup R | ||
| uses: r-lib/actions/setup-r@v2 | ||
| with: | ||
| r-version: 'release' | ||
| use-public-rspm: true | ||
| - name: Setup R dependencies | ||
| uses: r-lib/actions/setup-r-dependencies@v2 | ||
| with: | ||
| extra-packages: any::rcmdcheck | ||
| needs: check | ||
| - name: Setup Pandoc | ||
| uses: r-lib/actions/setup-pandoc@v2 | ||
| - name: Run R CMD check on dev | ||
| run: | | ||
| echo "🔍 Running R CMD check on dev branch..." | ||
| Rscript -e "rcmdcheck::rcmdcheck( | ||
| args = c('--no-manual', '--as-cran'), | ||
| error_on = 'warning', | ||
| check_dir = 'check' | ||
| )" | ||
| - name: Upload check results | ||
| if: failure() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: dev-check-results | ||
| path: check/ | ||
| # Create promotion PR | ||
| create-promotion-pr: | ||
| name: "Create Promotion PR" | ||
| needs: [validate-dev] | ||
| if: | | ||
| always() && | ||
| (needs.validate-dev.result == 'success' || inputs.skip_checks == 'true') | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| pr_number: ${{ steps.create_pr.outputs.pr_number }} | ||
| steps: | ||
| - name: Checkout staging branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: staging | ||
| fetch-depth: 0 | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Configure git | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| - name: Create promotion branch | ||
| id: create_branch | ||
| run: | | ||
| TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S") | ||
| BRANCH_NAME="promote/dev-to-staging-${TIMESTAMP}" | ||
| echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT | ||
| # Create promotion branch from staging | ||
| git checkout -b "$BRANCH_NAME" | ||
| # Merge dev into promotion branch | ||
| git fetch origin dev:dev | ||
| git merge --no-ff dev -m "chore: Promote dev to staging (${TIMESTAMP})" | ||
| # Push promotion branch | ||
| git push origin "$BRANCH_NAME" | ||
| echo "Created promotion branch: $BRANCH_NAME" | ||
| - name: Create pull request | ||
| id: create_pr | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| BRANCH_NAME="${{ steps.create_branch.outputs.branch_name }}" | ||
| DEV_SHA="${{ needs.validate-dev.outputs.dev_sha || 'N/A' }}" | ||
| DEV_SHORT_SHA="${{ needs.validate-dev.outputs.dev_short_sha || 'N/A' }}" | ||
| TIMESTAMP=$(date -u +"%Y-%m-%d %H:%M:%S UTC") | ||
| # Get commits between staging and dev | ||
| COMMITS=$(git log staging..dev --oneline --no-merges | head -20) | ||
| COMMIT_COUNT=$(git rev-list --count staging..dev) | ||
| # Create PR body (without single quotes to allow variable expansion) | ||
| cat > pr_body.md << EOF | ||
| ## 🚀 Dev → Staging Promotion | ||
| + **Promotion timestamp:** $TIMESTAMP | ||
| + **Dev branch SHA:** \`$DEV_SHORT_SHA\` (\`$DEV_SHA\`) | ||
| + **Commits included:** $COMMIT_COUNT | ||
| ### Changes in this promotion | ||
| \`\`\` | ||
| $COMMITS | ||
| \`\`\` | ||
| ### Pre-promotion validation | ||
| - ✅ Dev branch R CMD check: **PASSED** | ||
| - ✅ All dev CI checks: **PASSED** | ||
| ### Required approvals | ||
| This promotion requires: | ||
| 1. ✅ Manual approval via \`staging-promotion\` environment | ||
| 2. ✅ All staging CI checks must pass | ||
| 3. ✅ Code review approval | ||
| ### What happens after approval? | ||
| 1. PR will auto-merge to staging | ||
| 2. Staging CI will run (multi-platform) | ||
| 3. Promotion tag \`staging/YYYYMMDD-HHMMSS\` will be created | ||
| 4. Staging is then ready for production release | ||
| --- | ||
| **⚠️ IMPORTANT:** Review all changes carefully before approving. Once merged, staging becomes the source of truth for the next production release. | ||
| EOF | ||
| # Substitute variables | ||
| sed -i "s/\$TIMESTAMP/$TIMESTAMP/g" pr_body.md | ||
| sed -i "s/\$DEV_SHA/$DEV_SHA/g" pr_body.md | ||
| sed -i "s/\$DEV_SHORT_SHA/$DEV_SHORT_SHA/g" pr_body.md | ||
| sed -i "s/\$COMMIT_COUNT/$COMMIT_COUNT/g" pr_body.md | ||
| sed -i "s/\$COMMITS/$COMMITS/g" pr_body.md | ||
| # Create PR | ||
| PR_URL=$(gh pr create \ | ||
| --base staging \ | ||
| --head "$BRANCH_NAME" \ | ||
| --title "🚀 Promote dev to staging ($(date -u +"%Y-%m-%d"))" \ | ||
| --body-file pr_body.md \ | ||
| --label "promotion" \ | ||
| --label "staging") | ||
| # Extract PR number | ||
| PR_NUMBER=$(echo "$PR_URL" | grep -o '[0-9]*$') | ||
| echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT | ||
| echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT | ||
| echo "Created promotion PR: $PR_URL" | ||
| echo "PR Number: $PR_NUMBER" | ||
| - name: Add promotion comment | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| PR_NUMBER="${{ steps.create_pr.outputs.pr_number }}" | ||
| gh pr comment "$PR_NUMBER" --body "$(cat <<'EOF' | ||
| ### 📋 Promotion Checklist | ||
| Before approving this promotion, verify: | ||
| - [ ] All changes in dev have been reviewed and tested | ||
| - [ ] No breaking changes unless documented | ||
| - [ ] All CI checks pass (ubuntu, windows, macos) | ||
| - [ ] Code coverage is maintained or improved | ||
| - [ ] Documentation is up to date | ||
| - [ ] CHANGELOG/NEWS is updated (if applicable) | ||
| **Approval process:** | ||
| 1. Review all changes in this PR | ||
| 2. Verify CI status checks are green | ||
| 3. Approve via GitHub PR review | ||
| 4. Approve via \`staging-promotion\` environment gate | ||
| 5. PR will auto-merge after approval | ||
| **Questions or issues?** Leave a comment and mark the checklist items that need attention. | ||
| EOF | ||
| )" | ||
| # Wait for approval and auto-merge | ||
| auto-merge: | ||
| name: "Auto-Merge After Approval" | ||
| needs: [create-promotion-pr] | ||
| runs-on: ubuntu-latest | ||
| environment: staging-promotion # Requires manual approval | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 | ||
| - name: Enable auto-merge | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| run: | | ||
| PR_NUMBER="${{ needs.create-promotion-pr.outputs.pr_number }}" | ||
| echo "🔄 Enabling auto-merge for PR #$PR_NUMBER..." | ||
| # Enable auto-merge with squash strategy | ||
| gh pr merge "$PR_NUMBER" --auto --squash --delete-branch | ||
| echo "✅ Auto-merge enabled. PR will merge automatically after:" | ||
| echo " - All status checks pass" | ||
| echo " - Required reviews are approved" | ||
| echo " - staging-promotion environment approval granted" | ||
| - name: Tag promotion | ||
| if: success() | ||
| run: | | ||
| TIMESTAMP=$(date -u +"%Y%m%d-%H%M%S") | ||
| TAG_NAME="staging/${TIMESTAMP}" | ||
| echo "📌 Creating promotion tag: $TAG_NAME" | ||
| # Wait a moment for merge to complete | ||
| sleep 10 | ||
| # Fetch latest staging | ||
| git fetch origin staging:staging | ||
| git checkout staging | ||
| # Create and push tag | ||
| git tag -a "$TAG_NAME" -m "Promotion from dev to staging at $TIMESTAMP" | ||
| git push origin "$TAG_NAME" | ||
| echo "✅ Created tag: $TAG_NAME" | ||
| - name: Promotion summary | ||
| if: always() | ||
| run: | | ||
| PR_NUMBER="${{ needs.create-promotion-pr.outputs.pr_number }}" | ||
| cat >> $GITHUB_STEP_SUMMARY << 'EOF' | ||
| ## ✅ Dev → Staging Promotion Complete | ||
| **PR Number:** #$PR_NUMBER | ||
| **Status:** Merged and tagged | ||
| **Next steps:** | ||
| - Staging CI is running | ||
| - Review staging deployment | ||
| - When ready, promote staging → main for production release | ||
| --- | ||
| **Branching flow:** | ||
| ``` | ||
| Feature branches → dev → [staging] → main → public repo → CRAN | ||
| ^^^^^^^^ | ||
| YOU ARE HERE | ||
| ``` | ||
| EOF | ||
| sed -i "s/\$PR_NUMBER/$PR_NUMBER/g" $GITHUB_STEP_SUMMARY | ||