From 54665a4f1947f76b097141f7d08fa44eeef42780 Mon Sep 17 00:00:00 2001 From: 7174Andy Date: Fri, 31 Jul 2026 21:34:39 -0700 Subject: [PATCH] ci: take main off Vercel's git trigger The release workflow applies migrations and only then deploys, but until now Vercel also deployed main off the same push with nothing ordering the two. The first release showed it concretely: two production deployments of b195da1, eighteen seconds apart. Harmless there because nothing was pending, a real race for any release carrying a migration - issue #6 made intermittent rather than fixed. Held back from #9 on purpose. Whether the CLI deploy still works with the git trigger disabled takes a real production deploy to find out, and keeping the git deploy as a fallback meant a broken deploy step could not strand the project. That release has since deployed successfully, so the remaining unknown is narrow: this change carries no migration, so if the deploy step fails, nothing is half-applied and reverting this file restores the git trigger. Also drops the interim wording it invalidates - three passages in the README and two comments in the workflow described a state that no longer exists once this lands. Refs #6 --- .github/workflows/release.yml | 32 +++++++++++------------ README.md | 48 +++++++++++++++-------------------- vercel.json | 7 +++++ 3 files changed, 43 insertions(+), 44 deletions(-) create mode 100644 vercel.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e60bf51..e084e92 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,5 @@ -# The only path to production that applies migrations - and once vercel.json -# lands (see below), the only one git can trigger at all. +# The only path to production that applies migrations, and the only one git can +# trigger at all - vercel.json disables Vercel's git trigger for main. # # Migrations used to run inside the Vercel build, in a script deleted with this # change. Ordering was correct there - Vercel promotes only after a successful @@ -11,14 +11,14 @@ # failed step ends the job, the deploy step simply never runs if it fails. # Vercel keeps serving the previous deployment, which is the safe direction. # -# A vercel.json disabling Vercel's git trigger for main is NOT in the repo yet. -# Until it is, Vercel also deploys main off the same push, in parallel with this -# workflow, and the two race - the failure mode in issue #6, made intermittent. -# That is deliberate and temporary: it leaves the git-triggered deploy as a -# fallback while `vercel deploy --prod` from this workflow is still unproven, so -# a first release that cannot deploy does not strand the project with no way to -# ship. It must be added promptly, and no schema-changing merge should happen -# before it is. +# vercel.json is load-bearing, not cosmetic. Without it Vercel deploys main off +# the same push, in parallel with this workflow, and the two race - the failure +# mode in issue #6, made intermittent. Deleting it silently reopens that race, +# and nothing in CI would catch it. +# +# It is also what makes this workflow the only way to deploy: if the deploy step +# below stops working, there is no git-triggered fallback. Revert vercel.json to +# get one back. name: Release on: @@ -133,13 +133,11 @@ jobs: echo "A migration that failed part-way is recorded in" echo "_prisma_migrations with finished_at NULL, and every later" echo "'migrate deploy' aborts with P3009 until that is resolved -" - echo "including releases that touch no schema at all. Once" - echo "vercel.json lands and this is the only deploy path git can" - echo "trigger, one bad migration freezes every deploy. Until then it" - echo "is the worse way round: migrations stall here while Vercel keeps" - echo "deploying main off the same push, so code ships without them -" - echo "issue #6 exactly. Either way, clear this before the next merge." - echo "A cancelled or timed-out run mid-migration leaves the same state." + echo "including releases that touch no schema at all. Since this is" + echo "the only deploy path git can trigger, one bad migration freezes" + echo "every deploy until it is cleared - so clear it before the next" + echo "merge. A cancelled or timed-out run mid-migration leaves the" + echo "same state." echo echo "So check whether the DDL actually landed in the database, then" echo "tell Prisma which of the two happened:" diff --git a/README.md b/README.md index b756ffd..d4f403d 100644 --- a/README.md +++ b/README.md @@ -309,8 +309,8 @@ is why the workflow's failure output points at `gh run list ### Deploying schema changes `.github/workflows/release.yml` is the only path to production that applies -migrations, and once `vercel.json` lands (below) the only path to production that -git can trigger. It starts when CI completes successfully on `main` — chained +migrations, and the only path to production that git can trigger. It starts when +CI completes successfully on `main` — chained off CI's completion rather than off the merge push, so a release cannot begin until the migration gate, tests, and lint are green on that exact commit, which it then checks out by SHA. It runs `prisma migrate deploy` first, and only if that @@ -323,20 +323,18 @@ like a build failure, and every retried or concurrent build re-ran it. `concurrency: release` now runs one release at a time, so migrations cannot interleave. -**`vercel.json` is not in the repo yet.** When it lands it will disable Vercel's -git trigger for `main`, which is what stops Vercel deploying off the same push in -parallel with this workflow. Until then a merge to `main` produces *both* a -Vercel git deploy and this workflow's deploy, and the two race — harmless for a -release with nothing pending, a real race for any release that has a migration. -That is deliberate and temporary: whether `vercel deploy --prod` still works with -the git trigger disabled takes one real production deploy to find out, and -holding `vercel.json` back leaves the git deploy as a fallback if it does not, so -a broken deploy step cannot strand the project with no way to ship at all. It -follows in a one-file pull request as soon as the first release has proven the -CLI path, and no schema-changing merge should happen before it is in. Preview -deployments for other branches are unaffected either way. - -Even with `vercel.json` in place, only *git-triggered* deploys are disabled. Each +**`vercel.json` is load-bearing.** It disables Vercel's git trigger for `main`, +which is what stops Vercel deploying off the same push in parallel with this +workflow. Without it, a merge produces *both* a Vercel git deploy and this +workflow's deploy with nothing ordering them — harmless for a release with +nothing pending, a real race for any release carrying a migration, which is +issue #6 made intermittent. Deleting the file silently reopens that race and no +check would catch it. Preview deployments for other branches are unaffected. + +The cost of that is no git-triggered fallback: if the deploy step breaks, there +is no other way to ship until it is fixed or `vercel.json` is reverted. + +`vercel.json` covers only *git-triggered* deploys. Each of these still ships code whose migrations were never applied, and the daily drift check cannot detect it — that check compares production's database to `main`'s schema, not to whatever code is deployed. So don't: @@ -376,13 +374,10 @@ gh workflow run release.yml Fixing the migration and merging again is not enough on its own. A migration that failed part-way is recorded in `_prisma_migrations` with `finished_at` NULL, and **every later `prisma migrate deploy` aborts with `P3009`** until that record -is resolved — including releases that touch no schema at all. Once `vercel.json` -lands and this is the only deploy path git can trigger, one bad migration freezes -every deploy until it is cleared. Until then it is the worse way round: the -migration stalls here while Vercel keeps deploying `main` off the same push, so -code ships without its migrations — issue #6 exactly. Either way, clear the -`P3009` before the next merge. A release cancelled or timed out mid-migration -leaves the same state. +is resolved — including releases that touch no schema at all. Since this is the +only deploy path git can trigger, one bad migration freezes every deploy until it +is cleared, so clear the `P3009` before the next merge. A release cancelled or +timed out mid-migration leaves the same state. So look at the database, decide whether the failed migration's DDL actually landed, and tell Prisma which of the two happened: @@ -506,10 +501,9 @@ Step 4 is the last deploy you trigger from the Vercel side. After that a release is `.github/workflows/release.yml`: merge to `main`, CI passes, and the workflow applies migrations and then deploys — or `gh workflow run release.yml` to release by hand. Watch that workflow rather than the Vercel dashboard, because it is the -thing that applies migrations. Vercel does still deploy `main` off its own git -trigger for now, and will stop once `vercel.json` lands; see [Deploying schema -changes](#deploying-schema-changes) for why it is not in the repo yet and what -races until it is. +thing that applies migrations. `vercel.json` disables Vercel's own git trigger for +`main`, so a push no longer deploys on its own — see [Deploying schema +changes](#deploying-schema-changes). `DATABASE_URL` is needed by the running app, not by the Vercel build — `next build` never touches the database. It is needed separately by GitHub Actions, in diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..540ffc6 --- /dev/null +++ b/vercel.json @@ -0,0 +1,7 @@ +{ + "git": { + "deploymentEnabled": { + "main": false + } + } +}