Files
pezkuwi-wallet-utils/.github/workflows/promote-to-live.yml
T
pezkuwichain 6d96f82ceb ci: run the gate before the deploy, not after it (#70)
* ci: run the gate before the deploy, not after it

master is the live branch — every app fetches its config from
raw.githubusercontent.com/.../master/... — and the flow was arranged so that content
reached it first and was checked afterwards.

auto-pr.yml fired on a push to *master* and opened a master → main PR. Code Quality ran
on that PR, i.e. on the way into the mirror, long after the config was already being
served. A check that reports "the live config is broken" is not a gate.

The same inversion made master unwritable by the front door: five required status
checks, and no workflow triggering on a PR into master, so the only way to change the
live branch was to bypass its own protection. That is not a hypothetical — it was
bypassed twice on 2026-08-10, and the second time was to undo the first.

Now: work lands on main, Code Quality decides, and promote-to-live moves master to the
exact commit that passed. It refuses anything that is not an ancestor of main, and uses
--force-with-lease so a master that moved underneath it is a failure rather than a
silent overwrite. main already ran Code Quality on push, so no trigger change is needed.

Also: each daily sync opened a PR and nothing closed the previous one. Thirty-one
branches spanning 2026-02-10 to 2026-08-08 were cleared by hand on 2026-08-11, every one
superseded by the next day's run. A sync is a snapshot of upstream, so an older open
sync PR is never the right thing to merge — it is noise that hides whether anything is
genuinely waiting. The sync now closes what it supersedes.

Branch protection still needs moving in the same direction and cannot be done from a
commit: main requires no PR, no approvals and no checks, while master carries the five
checks that can never run there. The settings change is proposed separately.

* ci: require an admin's approval before anything reaches the live branch

main is where work lands and where Code Quality decides; promote-to-live then moves
master to whatever passed, and every app reads master directly. An approval on main is
therefore the last human judgement before a change is served to wallets in the field —
and until now main required no review at all.

One approval, from either admin. Requiring two would mean two of two, which stalls
whenever one of them is the author.
2026-08-11 05:39:28 -07:00

66 lines
2.6 KiB
YAML

name: Promote main → master
# master is the live branch: every app reads its config straight from
# raw.githubusercontent.com/.../master/... . Nothing should reach it that has not
# passed Code Quality first.
#
# The flow used to run the other way. auto-pr.yml fired on a push to *master* and
# opened a master → main PR, so content was already live before a single check ran —
# the gate sat downstream of the thing it was meant to gate. A check that reports
# "the live config is broken" is not a gate.
#
# It also made master unwritable by the front door: five checks were required there and
# no workflow triggered on a PR into master, so the only way to change the live branch
# was to bypass its own protection. A rule that can only be bypassed teaches people to
# bypass it.
#
# Now: work lands on main, Code Quality decides, and only then is master moved to match.
on:
workflow_run:
workflows: ["Code Quality"]
types: [completed]
permissions:
contents: write
jobs:
promote:
# Only a green run, only on main, and only for a real push — a pull_request run
# tests a merge commit that does not exist on main yet.
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.head_branch == 'main' &&
github.event.workflow_run.event == 'push'
runs-on: ubuntu-latest
steps:
- name: Checkout the exact commit that passed
uses: actions/checkout@v4
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0
- name: Refuse to promote a commit that is not on main
run: |
git fetch origin main
if ! git merge-base --is-ancestor "${{ github.event.workflow_run.head_sha }}" origin/main; then
echo "::error::${{ github.event.workflow_run.head_sha }} is not an ancestor of main — refusing"
exit 1
fi
echo "Commit is on main."
- name: Promote
run: |
SHA="${{ github.event.workflow_run.head_sha }}"
# --force-with-lease, not --force: if master has moved since this job read it,
# the push is refused rather than silently discarding whatever moved it.
git push --force-with-lease origin "$SHA":refs/heads/master
echo "master is now $SHA"
echo "### Promoted to live" >> "$GITHUB_STEP_SUMMARY"
echo "\`master\` → \`$SHA\`" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Apps read this branch directly; the change is live as soon as their config refreshes." >> "$GITHUB_STEP_SUMMARY"