feat(db): apply pending migrations as a deploy step - #352
Merged
Merged
Conversation
0007 shipped without ever being applied to the database the academy site connects to, so the table was missing until the first visitor asked for a sample. A migration that has to be remembered is one that will be forgotten.
The migrations folder joins globalDependencies because a cached build is a build that did not run, and would serve a release from before the migration existed.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
kartikeya-27
approved these changes
Sep 19, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
0007_academy_samplesshipped in #350 and was never applied to the database the academy site connects to. Nothing noticed until somebody filed a sample request, at which pointPOST /api/ordersanswered 503 and the log said the database could not be reached — the database was fine, the table was missing.CI's
db:migrateonly seeds the test database. Applying a migration to production was a manual step, and a manual step is one that eventually gets skipped. This makes the deploy do it.How it works
packages/db/scripts/migrate.tsconnects, checks what the journal has that the database does not, takes a session-level advisory lock, applies the rest, and exits non-zero if it could not. With nothing pending it is one connection and two queries, and it never takes the lock. Every outcome is a logged line naming the database and the migrations, because the failure this is meant to prevent was an invisible one.It runs from the
buildscript of the three apps that read Postgres, so a release and its schema arrive together. Nothing in it is Vercel-specific — any platform that runs a build command can run it, andDATABASE_MIGRATE=forcemakes it usable from a release step on one that does not build at all.The decisions live in
packages/db/src/deploy.tsas pure functions of the environment, which is the part that is worth testing:DATABASE_URL. Recognising the platform is a table with one row in it today; adding another platform is adding a row. A build on something unrecognised — CI, a container, a laptop withDATABASE_URLexported — is trusted to have chosen its own database.client.tsrather than writing a second one.DATABASE_MIGRATE_URLcovers anything else that pools by transaction..envfile is loaded. The environment is the whole contract. A stray local env file naming a different project is exactly how the migration went to the wrong database in the first place.packages/db/migrations/**joinsglobalDependenciesinturbo.json: a cached build is a build that did not run, so without it a release carrying a new migration can be served from a cache taken before that migration existed.Verification
Against a throwaway Postgres 17:
up to date, exit 0DATABASE_URL→ skipped, with the reason loggedpnpm buildinapps/academy→ migrated, built,academy.sample_requestspresent51 tests pass in
@byteveda/db(20 new), typecheck and Biome are clean. The Lighthouse workflow builds these apps without aDATABASE_URLand takes the skip path, so it is unaffected.After merge
Production is still missing
0007until the next production deploy of academy, admin or flexiq applies it. Preview environments need no configuration — they are skipped by default.