115 lines
4.4 KiB
JavaScript
115 lines
4.4 KiB
JavaScript
#!/usr/bin/env node
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const DB_MANIFEST_PATH = "packages/db/package.json";
|
|
const PRODUCTION_SEED_SOURCE_PATH = "prisma/seed.ts";
|
|
const PRODUCTION_SEED_ARTIFACT_PATH = "packages/db/dist/production-seed.server.js";
|
|
const REQUIRED_EXTERNALS = ["@prisma/client", "@prisma/adapter-pg", "pg", "pino", "bcryptjs"];
|
|
|
|
function readText(rootDir, relativePath) {
|
|
return fs.readFileSync(path.join(rootDir, relativePath), "utf8");
|
|
}
|
|
|
|
function readJson(rootDir, relativePath) {
|
|
return JSON.parse(readText(rootDir, relativePath));
|
|
}
|
|
|
|
function getProductionSeedBuildSegment(buildScript) {
|
|
return (
|
|
buildScript
|
|
.split(/\s+&&\s+/)
|
|
.find((segment) => /production-seed\.server\.js/.test(segment)) ?? ""
|
|
);
|
|
}
|
|
|
|
export function verifyProductionSeedArtifact(rootDir = process.cwd()) {
|
|
const passes = [];
|
|
const failures = [];
|
|
const observations = [];
|
|
|
|
const manifestPath = path.join(rootDir, DB_MANIFEST_PATH);
|
|
if (!fs.existsSync(manifestPath)) {
|
|
failures.push(`${DB_MANIFEST_PATH}: missing DB package manifest`);
|
|
return { passes, failures, observations };
|
|
}
|
|
|
|
const manifest = readJson(rootDir, DB_MANIFEST_PATH);
|
|
const buildScript = manifest.scripts?.build ?? "";
|
|
const productionSeedBuild = getProductionSeedBuildSegment(buildScript);
|
|
const sourcePath = path.join(rootDir, PRODUCTION_SEED_SOURCE_PATH);
|
|
|
|
if (!fs.existsSync(sourcePath)) {
|
|
failures.push(`${PRODUCTION_SEED_SOURCE_PATH}: missing production seed source`);
|
|
} else {
|
|
const source = readText(rootDir, PRODUCTION_SEED_SOURCE_PATH);
|
|
if (/db\.\$disconnect\s*\(/.test(source) && /finally\s*\{[\s\S]*db\.\$disconnect\s*\(/.test(source)) {
|
|
passes.push(`${PRODUCTION_SEED_SOURCE_PATH}: disconnects the shared Prisma client in a finally block`);
|
|
} else {
|
|
failures.push(`${PRODUCTION_SEED_SOURCE_PATH}: production seed must disconnect the shared Prisma client in a finally block`);
|
|
}
|
|
}
|
|
|
|
if (!productionSeedBuild) {
|
|
failures.push(`${DB_MANIFEST_PATH}: build script must emit ${PRODUCTION_SEED_ARTIFACT_PATH}`);
|
|
} else {
|
|
passes.push(`${DB_MANIFEST_PATH}: build script emits ${PRODUCTION_SEED_ARTIFACT_PATH}`);
|
|
|
|
if (/\s--packages=external(?:\s|$)/.test(productionSeedBuild)) {
|
|
failures.push(`${DB_MANIFEST_PATH}: production seed build must not externalize all packages`);
|
|
} else {
|
|
passes.push(`${DB_MANIFEST_PATH}: production seed build bundles workspace packages`);
|
|
}
|
|
|
|
for (const dependency of REQUIRED_EXTERNALS) {
|
|
if (new RegExp(`--external:${dependency.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:\\s|$)`).test(productionSeedBuild)) {
|
|
passes.push(`${DB_MANIFEST_PATH}: production seed build keeps ${dependency} runtime external`);
|
|
} else {
|
|
failures.push(`${DB_MANIFEST_PATH}: production seed build must explicitly externalize ${dependency}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const artifactPath = path.join(rootDir, PRODUCTION_SEED_ARTIFACT_PATH);
|
|
if (!fs.existsSync(artifactPath)) {
|
|
failures.push(`${PRODUCTION_SEED_ARTIFACT_PATH}: artifact not present; run pnpm build before artifact content verification`);
|
|
return { passes, failures, observations };
|
|
}
|
|
|
|
const artifact = readText(rootDir, PRODUCTION_SEED_ARTIFACT_PATH);
|
|
if (/(?:from\s+["']|import\(\s*["'])@toolctrl\//.test(artifact)) {
|
|
failures.push(`${PRODUCTION_SEED_ARTIFACT_PATH}: must not contain bare @toolctrl/* imports`);
|
|
} else {
|
|
passes.push(`${PRODUCTION_SEED_ARTIFACT_PATH}: contains no bare @toolctrl/* imports`);
|
|
}
|
|
|
|
return { passes, failures, observations };
|
|
}
|
|
|
|
export function formatProductionSeedArtifactResult(result) {
|
|
const status = result.failures.length > 0 ? "FAIL" : "PASS";
|
|
return [
|
|
`${status} Production seed artifact verification`,
|
|
...result.passes.map((pass) => `PASS ${pass}`),
|
|
...result.observations.map((observation) => `OBSERVE ${observation}`),
|
|
...result.failures.map((failure) => `FAIL ${failure}`),
|
|
`Summary: ${result.passes.length} passed, ${result.observations.length} observed, ${result.failures.length} failed`,
|
|
"",
|
|
].join("\n");
|
|
}
|
|
|
|
const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
|
|
|
|
if (isCli) {
|
|
const result = verifyProductionSeedArtifact(process.cwd());
|
|
const output = formatProductionSeedArtifactResult(result);
|
|
|
|
if (result.failures.length > 0) {
|
|
process.stderr.write(output);
|
|
process.exitCode = 1;
|
|
} else {
|
|
process.stdout.write(output);
|
|
}
|
|
}
|