Skip to content

Commit 20802fb

Browse files
committed
feat: add payload migration checks
1 parent 8479b29 commit 20802fb

6 files changed

Lines changed: 3726 additions & 1 deletion

File tree

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ COPY .next .next
77
COPY scripts/ scripts/
88
COPY src/ src/
99
COPY tsconfig.json tsconfig.json
10+
RUN npm run migration:check
1011

1112
EXPOSE 3000
1213

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
"type": "module",
66
"scripts": {
77
"dev": "next dev",
8+
"prebuild": "npm run migration:check",
89
"build": "next build",
910
"postbuild": "next-sitemap --config next-sitemap.config.cjs && node scripts/prepare-standalone.mjs",
1011
"start": "next start",
1112
"standalone": "node .next/standalone/server.js",
1213
"migrate": "payload migrate",
1314
"migrate:create": "payload migrate:create",
1415
"migrate:status": "payload migrate:status",
16+
"migration:check": "node scripts/check-migrations.mjs",
17+
"migration:snapshot": "payload generate:db-schema --log=false --prettify=false",
1518
"generate:importmap": "payload generate:importmap",
1619
"lint": "npm run typecheck",
1720
"typecheck": "tsc --noEmit",
@@ -72,7 +75,8 @@
7275
},
7376
"knip": {
7477
"ignoreFiles": [
75-
"next-sitemap.config.cjs"
78+
"next-sitemap.config.cjs",
79+
"src/payload-schema.snapshot"
7680
],
7781
"ignoreDependencies": [
7882
"@swc-node/register",

scripts/check-migrations.mjs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"
2+
import { tmpdir } from "node:os"
3+
import { delimiter, join, resolve } from "node:path"
4+
import { spawnSync } from "node:child_process"
5+
6+
const projectRoot = resolve(import.meta.dirname, "..")
7+
const baselinePath = process.env.PAYLOAD_SCHEMA_BASELINE_FILE ? resolve(projectRoot, process.env.PAYLOAD_SCHEMA_BASELINE_FILE) : resolve(projectRoot, "src/payload-schema.snapshot")
8+
const temporaryDirectory = mkdtempSync(join(tmpdir(), "cygnus-payload-schema-"))
9+
const generatedPath = join(temporaryDirectory, "payload-schema.snapshot")
10+
const payloadBin = resolve(projectRoot, "node_modules/payload/bin.js")
11+
12+
function normalizedFile(path) {
13+
return readFileSync(path, "utf8").replaceAll("\r\n", "\n").trim()
14+
}
15+
16+
try {
17+
const result = spawnSync(process.execPath, [payloadBin, "generate:db-schema", "--log=false", "--prettify=false"], {
18+
cwd: projectRoot,
19+
encoding: "utf8",
20+
env: {
21+
...process.env,
22+
PATH: `${resolve(projectRoot, "node_modules/.bin")}${delimiter}${process.env.PATH ?? ""}`,
23+
PAYLOAD_SCHEMA_OUTPUT_FILE: generatedPath,
24+
},
25+
timeout: 120_000,
26+
})
27+
28+
if (result.error) throw result.error
29+
30+
if (result.status !== 0) {
31+
process.stderr.write(result.stdout ?? "")
32+
process.stderr.write(result.stderr ?? "")
33+
throw new Error(`Payload schema generation failed with exit code ${result.status ?? "unknown"}.`)
34+
}
35+
36+
if (!existsSync(generatedPath)) {
37+
process.stderr.write(result.stdout ?? "")
38+
process.stderr.write(result.stderr ?? "")
39+
throw new Error(
40+
[
41+
`Payload did not create the temporary schema at ${generatedPath}.`,
42+
"Ensure postgresAdapter.generateSchemaOutputFile uses PAYLOAD_SCHEMA_OUTPUT_FILE when it is set.",
43+
].join("\n")
44+
)
45+
}
46+
47+
let baseline
48+
try {
49+
baseline = normalizedFile(baselinePath)
50+
} catch {
51+
throw new Error(`Missing schema baseline at ${baselinePath}. Create the required migration and refresh the schema baseline.`)
52+
}
53+
54+
if (normalizedFile(generatedPath) !== baseline) {
55+
console.error(
56+
[
57+
"",
58+
"ERROR: Payload schema differs from the latest committed migration baseline.",
59+
"A Payload field may have been changed without creating a migration.",
60+
"",
61+
"Create and verify a migration:",
62+
" npm run migrate:create -- <migration-name>",
63+
"",
64+
"Then refresh the baseline:",
65+
" npm run migration:snapshot",
66+
"",
67+
].join("\n")
68+
)
69+
process.exitCode = 1
70+
} else {
71+
console.log("Payload migration check passed: schema and migration baseline match.")
72+
}
73+
} finally {
74+
rmSync(temporaryDirectory, { recursive: true, force: true })
75+
}

scripts/docker-entrypoint.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
#!/bin/sh
2+
set -eu
3+
14
npm run migrate
25
exec npm run standalone

0 commit comments

Comments
 (0)