|
1 | 1 | name: Promote SDKs |
2 | 2 |
|
3 | | -# Promote staging to production by fast-forwarding production main up to staging, |
4 | | -# preserving SHAs so the trunks stay identical and linear (nothing to heal on the |
5 | | -# next stlc build). Manual dispatch by design: a maintainer promotes the accumulated |
6 | | -# batch to cut a release; release-please then opens its version PR on production. |
| 3 | +# Promote staging to production by opening a pull request on the production repo |
| 4 | +# whose head is staging main (pushed to the `stlc/promote` branch). Production `main` |
| 5 | +# stays fully protected; the PR is the human review gate before publishing. It must |
| 6 | +# be merged with a MERGE COMMIT (never squash/rebase) so the trunks keep shared |
| 7 | +# history; back-sync then fast-forwards staging to the merge commit. Manual dispatch |
| 8 | +# by design: a maintainer promotes the accumulated batch to cut a release; |
| 9 | +# release-please then opens its version PR on production. |
7 | 10 | on: |
8 | 11 | workflow_dispatch: {} |
9 | 12 |
|
@@ -47,17 +50,40 @@ jobs: |
47 | 50 | echo "synced=false" >> "$GITHUB_OUTPUT" |
48 | 51 | fi |
49 | 52 |
|
50 | | - - name: Promote staging to production (fast-forward) |
| 53 | + - name: Promote staging to production (pull request) |
51 | 54 | if: steps.diff.outputs.synced == 'false' |
| 55 | + env: |
| 56 | + PROMOTE_BRANCH: stlc/promote |
52 | 57 | run: | |
53 | 58 | # Refuse unless production is an ancestor of staging: otherwise the trunks |
54 | | - # have forked (production advanced without a back-sync) and FF is unsafe. |
| 59 | + # have forked (production advanced without a back-sync) and the PR would be |
| 60 | + # a real merge. Back-sync production into staging first. |
55 | 61 | if ! git merge-base --is-ancestor production/main origin/main; then |
56 | 62 | echo "::error title=Promote blocked::production/main is not an ancestor of staging main. Back-sync production into staging first." |
57 | 63 | exit 1 |
58 | 64 | fi |
59 | | - git push production origin/main:refs/heads/main |
60 | | - echo "Fast-forwarded production/main to staging/main." |
| 65 | + # Publish staging's tip as a branch on production and open (or refresh) the |
| 66 | + # promote PR. `main` is protected on production; the PR is the review gate. |
| 67 | + git push --force production "origin/main:refs/heads/${PROMOTE_BRANCH}" |
| 68 | + tip=$(git rev-parse --short origin/main) |
| 69 | + { |
| 70 | + echo "Promotes \`${PRODUCTION_REPO}\` \`main\` to the staging trunk (\`${tip}\`)." |
| 71 | + echo |
| 72 | + echo "**Merge with a merge commit — do not squash or rebase.** Squashing rewrites the commits and forks the trunks; the next back-sync and promote refuse until repaired by hand." |
| 73 | + echo |
| 74 | + echo "After merging, release-please opens the version PR on this repo; merging that one tags and publishes." |
| 75 | + echo |
| 76 | + echo "### Commits" |
| 77 | + git log --format='- %h %s' production/main..origin/main |
| 78 | + } > /tmp/promote-body.md |
| 79 | + existing=$(gh pr list -R "$PRODUCTION_REPO" --head "$PROMOTE_BRANCH" --state open --json number --jq '.[0].number // empty') |
| 80 | + if [ -n "$existing" ]; then |
| 81 | + gh pr edit "$existing" -R "$PRODUCTION_REPO" --title "Promote staging to production (${tip})" --body-file /tmp/promote-body.md |
| 82 | + echo "Refreshed promote PR #${existing} on ${PRODUCTION_REPO}." |
| 83 | + else |
| 84 | + gh pr create -R "$PRODUCTION_REPO" --base main --head "$PROMOTE_BRANCH" \ |
| 85 | + --title "Promote staging to production (${tip})" --body-file /tmp/promote-body.md |
| 86 | + fi |
61 | 87 |
|
62 | 88 | - name: Alert on failure |
63 | 89 | if: failure() |
|
0 commit comments