squad-promote.yml (4399B)
1 name: Squad Promote 2 3 on: 4 workflow_dispatch: 5 inputs: 6 dry_run: 7 description: 'Dry run — show what would happen without pushing' 8 required: false 9 default: 'false' 10 type: choice 11 options: ['false', 'true'] 12 13 permissions: 14 contents: write 15 16 jobs: 17 dev-to-preview: 18 name: Promote dev → preview 19 runs-on: ubuntu-latest 20 steps: 21 - uses: actions/checkout@v4 22 with: 23 fetch-depth: 0 24 token: ${{ secrets.GITHUB_TOKEN }} 25 26 - name: Configure git 27 run: | 28 git config user.name "github-actions[bot]" 29 git config user.email "github-actions[bot]@users.noreply.github.com" 30 31 - name: Fetch all branches 32 run: git fetch --all 33 34 - name: Show current state (dry run info) 35 run: | 36 echo "=== dev HEAD ===" && git log origin/dev -1 --oneline 37 echo "=== preview HEAD ===" && git log origin/preview -1 --oneline 38 echo "=== Files that would be stripped ===" 39 git diff origin/preview..origin/dev --name-only | grep -E "^(\.(ai-team|squad|ai-team-templates)|team-docs/|docs/proposals/)" || echo "(none)" 40 41 - name: Merge dev → preview (strip forbidden paths) 42 if: ${{ inputs.dry_run == 'false' }} 43 run: | 44 git checkout preview 45 git merge origin/dev --no-commit --no-ff -X theirs || true 46 47 # Strip forbidden paths from merge commit 48 git rm -rf --cached --ignore-unmatch \ 49 .ai-team/ \ 50 .squad/ \ 51 .ai-team-templates/ \ 52 team-docs/ \ 53 "docs/proposals/" || true 54 55 # Commit if there are staged changes 56 if ! git diff --cached --quiet; then 57 git commit -m "chore: promote dev → preview (v$(node -e "console.log(require('./package.json').version)"))" 58 git push origin preview 59 echo "✅ Pushed preview branch" 60 else 61 echo "ℹ️ Nothing to commit — preview is already up to date" 62 fi 63 64 - name: Dry run complete 65 if: ${{ inputs.dry_run == 'true' }} 66 run: echo "🔍 Dry run complete — no changes pushed." 67 68 preview-to-main: 69 name: Promote preview → main (release) 70 needs: dev-to-preview 71 runs-on: ubuntu-latest 72 steps: 73 - uses: actions/checkout@v4 74 with: 75 fetch-depth: 0 76 token: ${{ secrets.GITHUB_TOKEN }} 77 78 - name: Configure git 79 run: | 80 git config user.name "github-actions[bot]" 81 git config user.email "github-actions[bot]@users.noreply.github.com" 82 83 - name: Fetch all branches 84 run: git fetch --all 85 86 - name: Show current state 87 run: | 88 echo "=== preview HEAD ===" && git log origin/preview -1 --oneline 89 echo "=== main HEAD ===" && git log origin/main -1 --oneline 90 echo "=== Version ===" && node -e "console.log('v' + require('./package.json').version)" 91 92 - name: Validate preview is release-ready 93 run: | 94 git checkout preview 95 VERSION=$(node -e "console.log(require('./package.json').version)") 96 if ! grep -q "## \[$VERSION\]" CHANGELOG.md 2>/dev/null; then 97 echo "::error::Version $VERSION not found in CHANGELOG.md — update before releasing" 98 exit 1 99 fi 100 echo "✅ Version $VERSION has CHANGELOG entry" 101 102 # Verify no forbidden files on preview 103 FORBIDDEN=$(git ls-files | grep -E "^(\.(ai-team|squad|ai-team-templates)/|team-docs/|docs/proposals/)" || true) 104 if [ -n "$FORBIDDEN" ]; then 105 echo "::error::Forbidden files found on preview: $FORBIDDEN" 106 exit 1 107 fi 108 echo "✅ No forbidden files on preview" 109 110 - name: Merge preview → main 111 if: ${{ inputs.dry_run == 'false' }} 112 run: | 113 git checkout main 114 git merge origin/preview --no-ff -m "chore: promote preview → main (v$(node -e "console.log(require('./package.json').version)"))" 115 git push origin main 116 echo "✅ Pushed main — squad-release.yml will tag and publish the release" 117 118 - name: Dry run complete 119 if: ${{ inputs.dry_run == 'true' }} 120 run: echo "🔍 Dry run complete — no changes pushed."