diff --git a/.github/scripts/check-dev-green.sh b/.github/scripts/check-dev-green.sh index 62bd5283..8df92228 100755 --- a/.github/scripts/check-dev-green.sh +++ b/.github/scripts/check-dev-green.sh @@ -4,7 +4,7 @@ # Reads check-runs on stdin — one # namestatusconclusiondetails_url # per line, fields separated by ASCII Unit Separator (0x1F) — so it is unit-testable -# WITHOUT GitHub. promote-dev-to-prod.yml pipes the live `gh api .../check-runs` +# WITHOUT GitHub. promote-to-main.yml pipes the live `gh api .../check-runs` # output in. 0x1F (not TAB) is used deliberately: TAB is IFS-whitespace, so an empty # conclusion (every in_progress check has a null conclusion) would collapse and shift # the columns — which would make the promote run fail to exclude itself. 0x1F is diff --git a/.github/workflows/promote-dev-to-prod.yml b/.github/workflows/promote-to-main.yml similarity index 53% rename from .github/workflows/promote-dev-to-prod.yml rename to .github/workflows/promote-to-main.yml index 8e5e0ebc..98c3e60e 100644 --- a/.github/workflows/promote-dev-to-prod.yml +++ b/.github/workflows/promote-to-main.yml @@ -1,11 +1,21 @@ -name: Promote dev to main (prod) +# Gated dev -> main promotion (the PROD deploy gate under managed LangGraph Cloud). +# +# PROD now runs on managed LangGraph Cloud (git-connected to `main`) + Vercel (UI). +# The platform AUTO-DEPLOYS prod on every push to `main`, so a fast-forward of `main` +# to `dev` IS the prod deploy trigger -- there is no separate AWS deploy step anymore +# (the bespoke S3/SSM/packer CD is retired). This workflow therefore carries the whole +# prod-promotion gate: +# 1. manual dispatch only (no scheduled auto-promote -- prod ships when a human asks), +# 2. the `prod` GitHub Environment approval (required reviewer: amoussa1229), +# 3. the dev-HEAD-fully-green precondition (check-dev-green.sh), +# 4. a fast-forward-only push of `main` -> `dev` via the seahaven-promotion App, the +# sole non-admin bypass actor on the `main` ruleset (id 18238334). +name: Promote to main (prod) permissions: contents: write on: - schedule: - - cron: "0 8 * * *" workflow_dispatch: concurrency: @@ -15,10 +25,29 @@ concurrency: jobs: promote: runs-on: ubuntu-latest + # The manual-approval gate. The `prod` Environment's required reviewer + # (amoussa1229) must approve before this job runs -- and because the FF push + # below auto-deploys managed prod, that approval IS the prod-deploy approval. + environment: prod permissions: contents: write checks: read steps: + # Defense-in-depth: the checkout below pins `ref: dev`, so this workflow only + # ever promotes dev's HEAD. But workflow_dispatch runs the workflow DEFINITION + # from whichever ref it was launched on, so a branch that edited this file could + # otherwise reach the privileged steps. Refuse any dispatch not from `dev`, + # before the App token is minted. (The `prod` Environment approval still gates + # everything after this regardless.) + - name: Guard — only promote from dev + env: + DISPATCH_REF: ${{ github.ref_name }} + run: | + if [ "${DISPATCH_REF}" != "dev" ]; then + echo "::error::promote-to-main must be dispatched from 'dev' (got '${DISPATCH_REF}')." + exit 1 + fi + echo "Dispatch ref OK: ${DISPATCH_REF}" # Mint a GitHub App installation token for the protected-branch push below. # A plain ref push by github-actions[bot] is REJECTED by the `main` ruleset # (PRs required + a required status check; the default token is not a bypass @@ -56,10 +85,11 @@ jobs: -q '.check_runs[] | [.name, .status, (.conclusion // ""), (.details_url // "")] | join("\u001f")' \ | bash .github/scripts/check-dev-green.sh - name: Fast-forward main (PROD) to dev - # main is the production branch. A direct ref push is normally rejected by the - # `main` ruleset (PRs required), so this succeeds only because the App minted - # above is a bypass actor. The push is fast-forward-only, so a diverged main - # fails loudly rather than force-updating. + # main is the production branch: managed LangGraph Cloud is git-connected to it + # and auto-deploys prod on every push, so THIS push is the prod deploy trigger. + # A direct ref push is normally rejected by the `main` ruleset (PRs required), + # so it succeeds only because the App minted above is a bypass actor. The push + # is fast-forward-only, so a diverged main fails loudly rather than force-updating. # # SECURITY: this is the LAST step on purpose. The App token persists as the # git credential after checkout — do not add steps after this push that run