Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ jobs:
run: |
bun drizzle-kit check
bun drizzle-kit migrate
bun run src/verify-migrations-applied.ts

- name: Save database success status
run: |
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/deploy-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
PGOPTIONS: -c lock_timeout=5s -c statement_timeout=60s
# Losing a lock race rolls back cleanly, so only that is retried.
run: |
# GitHub's default shell is `bash -e` without pipefail, so without this
# the `| tee` below reports a failed migration as a success.
set -o pipefail
for attempt in 1 2 3 4 5 6 7 8; do
if bun drizzle-kit migrate 2>&1 | tee migrate.log; then exit 0; fi
grep -qE "deadlock detected|lock timeout" migrate.log || exit 1
Expand All @@ -54,6 +57,13 @@ jobs:
done
exit 1

- name: Verify migrations applied
working-directory: packages/db
env:
DATABASE_URL: ${{ secrets.DATABASE_URL_UNPOOLED }}
DATABASE_URL_UNPOOLED: ${{ secrets.DATABASE_URL_UNPOOLED }}
run: bun run src/verify-migrations-applied.ts

deploy-api:
name: Deploy API to Vercel
runs-on: ubuntu-latest
Expand Down
34 changes: 34 additions & 0 deletions packages/db/src/verify-migrations-applied.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { neon } from "@neondatabase/serverless";
import journal from "../drizzle/meta/_journal.json";
import { env } from "./env";

/**
* Fails unless the newest migration in the repo is the newest one recorded in
* the database. The deploy runs it after `drizzle-kit migrate` so a migration
* that did not apply cannot pass as one that did, whatever the migrate step's
* exit code said.
*
* Usage: bun run packages/db/src/verify-migrations-applied.ts
*/
async function verifyMigrationsApplied(): Promise<void> {
const expected = journal.entries.at(-1);
if (!expected) throw new Error("The migration journal has no entries");

const sql = neon(env.DATABASE_URL_UNPOOLED);
const [row] = await sql`
select max(created_at)::text as latest from drizzle.__drizzle_migrations
`;
const latest = row?.latest ? Number(row.latest) : null;

if (latest !== expected.when) {
throw new Error(
`Database is not at ${expected.tag}: newest applied migration is ${latest ?? "none"}, expected ${expected.when}`,
);
}
console.log(`Database is at ${expected.tag}`);
}

verifyMigrationsApplied().catch((error) => {
console.error(error instanceof Error ? error.message : error);
process.exit(1);
});
Loading