Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/scripts/verify-prisma-baseline.mjs
T

163 lines
7.0 KiB
JavaScript

#!/usr/bin/env node
import { createHash } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
export const BASELINE_MIGRATION_NAME = "00000000000000_hivegrid_baseline";
function sha256(value) {
return createHash("sha256").update(value).digest("hex");
}
function occurrences(value, pattern) {
return value.match(pattern)?.length ?? 0;
}
export function verifyPrismaBaseline(rootDirectory = process.cwd()) {
const passes = [];
const failures = [];
const migrationsDirectory = path.join(rootDirectory, "prisma/migrations");
const baselinePath = path.join(migrationsDirectory, BASELINE_MIGRATION_NAME, "migration.sql");
const lockPath = path.join(migrationsDirectory, "migration_lock.toml");
const manifestPath = path.join(rootDirectory, "prisma/baseline-legacy-manifest.json");
const legacyArchivePath = path.join(rootDirectory, "prisma/baseline-legacy-migrations.tar.gz");
if (
!fs.existsSync(migrationsDirectory) ||
!fs.existsSync(baselinePath) ||
!fs.existsSync(lockPath) ||
!fs.existsSync(manifestPath) ||
!fs.existsSync(legacyArchivePath)
) {
return {
passes,
failures: ["Prisma baseline requires its migration, migration_lock.toml, legacy manifest, and audit archive"],
};
}
const migrationDirectories = fs
.readdirSync(migrationsDirectory, { withFileTypes: true })
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort();
if (migrationDirectories[0] === BASELINE_MIGRATION_NAME) {
passes.push("active Prisma history starts with the consolidated baseline");
} else {
failures.push(`active Prisma history must start with ${BASELINE_MIGRATION_NAME}`);
}
let manifest;
try {
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
} catch {
failures.push("Prisma baseline legacy manifest must be valid JSON");
return { passes, failures };
}
const baselineSql = fs.readFileSync(baselinePath, "utf8");
const baselineChecksum = sha256(baselineSql);
const legacyArchiveChecksum = sha256(fs.readFileSync(legacyArchivePath));
if (
manifest.version === 1 &&
manifest.baselineMigration === BASELINE_MIGRATION_NAME &&
manifest.baselineChecksum === baselineChecksum &&
manifest.legacyArchiveChecksum === legacyArchiveChecksum &&
/^[0-9a-f]{40}$/.test(manifest.legacySourceCommit ?? "")
) {
passes.push("baseline, audit archive, and source revision match the immutable legacy manifest");
} else {
failures.push("baseline checksum, audit archive, migration name, or source revision differs from the legacy manifest");
}
const legacyMigrations = Array.isArray(manifest.migrations) ? manifest.migrations : [];
const names = legacyMigrations.map((migration) => migration?.name);
const uniqueNames = new Set(names);
const checksumsValid = legacyMigrations.every((migration) => typeof migration?.name === "string" && /^[0-9a-f]{64}$/.test(migration?.checksum ?? ""));
if (
legacyMigrations.length === manifest.legacyMigrationCount &&
legacyMigrations.length === 195 &&
uniqueNames.size === legacyMigrations.length &&
checksumsValid &&
names.every((name, index) => index === 0 || names[index - 1].localeCompare(name) < 0) &&
manifest.legacyHead === names.at(-1)
) {
passes.push("legacy adoption manifest pins all 195 ordered migration checksums");
} else {
failures.push("legacy adoption manifest must pin exactly 195 unique ordered migrations through its declared head");
}
const legacyChecksumVariants = Array.isArray(manifest.legacyChecksumVariants) ? manifest.legacyChecksumVariants : [];
const migrationChecksums = new Map(legacyMigrations.map((migration) => [migration?.name, migration?.checksum]));
const variantKeys = new Set();
const variantsValid = legacyChecksumVariants.every((variant) => {
const key = `${variant?.name}:${variant?.checksum}`;
const valid =
migrationChecksums.has(variant?.name) &&
/^[0-9a-f]{64}$/.test(variant?.checksum ?? "") &&
/^[0-9a-f]{40}$/.test(variant?.sourceCommit ?? "") &&
migrationChecksums.get(variant.name) !== variant.checksum &&
!variantKeys.has(key);
variantKeys.add(key);
return valid;
});
if (legacyChecksumVariants.length === 10 && variantsValid && variantKeys.size === legacyChecksumVariants.length) {
passes.push("legacy adoption manifest pins all 10 reviewed historical checksum variants with source revisions");
} else {
failures.push("legacy adoption manifest must pin exactly 10 unique reviewed checksum variants with source revisions");
}
const forbiddenMetaCommands = /^\\(?:restrict|unrestrict)\b/m.test(baselineSql);
const referencesMigrationLedger = /_prisma_migrations/.test(baselineSql);
if (!forbiddenMetaCommands && !referencesMigrationLedger) {
passes.push("baseline is non-interactive and does not manufacture Prisma ledger rows");
} else {
failures.push("baseline must not contain pg_dump session meta-commands or _prisma_migrations statements");
}
const surface = {
tables: occurrences(baselineSql, /^CREATE TABLE /gm),
policies: occurrences(baselineSql, /^CREATE POLICY /gm),
functions: occurrences(baselineSql, /^CREATE FUNCTION /gm),
rlsEnabled: occurrences(baselineSql, / ENABLE ROW LEVEL SECURITY;$/gm),
rlsForced: occurrences(baselineSql, / FORCE ROW LEVEL SECURITY;$/gm),
extensions: occurrences(baselineSql, /^CREATE EXTENSION /gm),
grantsAndRevokes: occurrences(baselineSql, /^(?:GRANT|REVOKE) /gm),
};
if (
surface.tables === 293 &&
surface.policies === 844 &&
surface.functions === 92 &&
surface.rlsEnabled === 293 &&
surface.rlsForced === 293 &&
surface.extensions === 2 &&
surface.grantsAndRevokes === 1010 &&
occurrences(baselineSql, /^INSERT INTO public\."Onboarding" /gm) === 1 &&
occurrences(baselineSql, /^INSERT INTO public\."OnboardingStep" /gm) === 1 &&
occurrences(baselineSql, /'shk-first-steps-(?:profile|contacts|projects|billing)-v1'/g) === 4 &&
occurrences(baselineSql, /^INSERT INTO public\."FeatureFlag" /gm) === 1 &&
occurrences(baselineSql, /^INSERT INTO public\."FeatureFlagAudit" /gm) === 1
) {
passes.push("baseline preserves the reviewed schema, RLS, function, extension, and grant surface");
} else {
failures.push(`baseline database surface changed unexpectedly: ${JSON.stringify(surface)}`);
}
if (/^provider = "postgresql"$/m.test(fs.readFileSync(lockPath, "utf8"))) {
passes.push("Prisma migration provider remains PostgreSQL");
} else {
failures.push("migration_lock.toml must retain the PostgreSQL provider");
}
return { passes, failures };
}
export function formatPrismaBaselineResult(result) {
return [...result.passes.map((message) => `PASS ${message}`), ...result.failures.map((message) => `FAIL ${message}`)].join("\n");
}
const isCli = process.argv[1] && path.join(import.meta.dirname, "verify-prisma-baseline.mjs") === path.resolve(process.argv[1]);
if (isCli) {
const result = verifyPrismaBaseline();
process.stdout.write(`${formatPrismaBaselineResult(result)}\n`);
process.exitCode = result.failures.length === 0 ? 0 : 1;
}