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

1974 lines
94 KiB
JavaScript

#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const REQUIRED_FILES = [
"Dockerfile",
"docker-compose.yml",
"docker-compose.staging.yml",
".dockerignore",
"start.sh",
"package.json",
"pnpm-workspace.yaml",
"apps/web/package.json",
"apps/api/package.json",
"apps/hocuspocus/Dockerfile",
"apps/portal-domain-controller/package.json",
"apps/worker/package.json",
"packages/db/package.json",
"packages/db/scripts/build-runtime-artifacts.mjs",
"runtime/api/package.json",
"runtime/hocuspocus/package.json",
"runtime/mcp/package.json",
"runtime/portal-domain-controller/package.json",
"runtime/web/package.json",
"runtime/worker/package.json",
"scripts/ci/deploy-production-closures.sh",
];
const ROOT_REQUIRED_SCRIPTS = ["build", "lint", "test", "typecheck"];
const API_REQUIRED_SCRIPTS = ["build"];
const REQUIRED_WORKER_GROUPS = [
{ service: "worker-general", group: "general", healthPort: "9101", adminPort: "9201", concurrencyEnv: "WORKER_CONCURRENCY_GENERAL" },
{ service: "worker-mail", group: "mail", healthPort: "9102", adminPort: "9202", concurrencyEnv: "WORKER_CONCURRENCY_MAIL" },
{ service: "worker-import", group: "import", healthPort: "9103", adminPort: "9203", concurrencyEnv: "WORKER_CONCURRENCY_IMPORT" },
{ service: "worker-webhook", group: "webhook", healthPort: "9104", adminPort: "9204", concurrencyEnv: "WORKER_CONCURRENCY_WEBHOOK" },
{ service: "worker-plugin", group: "plugin", healthPort: "9105", adminPort: "9205", concurrencyEnv: "WORKER_CONCURRENCY_PLUGIN" },
];
const REQUIRED_WORKER_GENERAL_WORKFLOW_ENV = [
"APP_URL",
"OUTBOX_DISPATCHER_ENABLED",
"OUTBOX_DISPATCHER_REQUIRED",
"WORKFLOW_TRIGGER_EXECUTOR_MODE",
"WORKFLOW_TRIGGER_EXECUTOR_URL",
"API_ACCESS_TOKEN",
"WORKFLOW_TRIGGER_EXECUTOR_TIMEOUT_MS",
"WORKFLOW_TRIGGER_OUTBOX_ENABLED",
"WORKFLOW_TRIGGER_OUTBOX_CONCURRENCY",
"WORKFLOW_TRIGGER_OUTBOX_REQUIRED",
];
const REQUIRED_STAGING_WORKER_GENERAL_ENV = [
["OUTBOX_DISPATCHER_ENABLED", "true"],
["OUTBOX_DISPATCHER_REQUIRED", "true"],
["WORKFLOW_TRIGGER_OUTBOX_ENABLED", "true"],
["WORKFLOW_TRIGGER_OUTBOX_REQUIRED", "true"],
];
const REQUIRED_WORKER_GENERAL_BILLING_ENV = [
"LAGO_API_URL",
"LAGO_API_KEY",
"LAGO_TIMEOUT_MS",
"MOLLIE_API_KEY",
"MOLLIE_PROFILE_ID",
"MOLLIE_TIMEOUT_MS",
"BILLING_MOLLIE_WEBHOOK_URL",
];
const REQUIRED_WORKER_GENERAL_SEARCH_ENV = ["MEILI_URL", "MEILI_API_KEY"];
const REQUIRED_WORKER_SCAN_ENV = [
"STORAGE_ENDPOINT",
"STORAGE_REGION",
"STORAGE_BUCKET",
"STORAGE_ACCESS_KEY_ID",
"STORAGE_SECRET_ACCESS_KEY",
"STORAGE_SESSION_TOKEN",
"STORAGE_FORCE_PATH_STYLE",
"STORAGE_QUARANTINE_BUCKET",
];
const REQUIRED_WEBHOOK_VAPID_ENV = ["WEB_PUSH_VAPID_SUBJECT", "WEB_PUSH_VAPID_PUBLIC_KEY", "WEB_PUSH_VAPID_PRIVATE_KEY"];
const REQUIRED_STAGING_WEB_AIRCALL_ENV = ["AIRCALL_CLIENT_ID", "AIRCALL_CLIENT_SECRET", "AIRCALL_REDIRECT_URI", "AIRCALL_WEBHOOK_URL"];
const REQUIRED_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV = [
"STORAGE_ENDPOINT",
"STORAGE_REGION",
"STORAGE_BUCKET",
"STORAGE_ACCESS_KEY_ID",
"STORAGE_SECRET_ACCESS_KEY",
"GDPR_EXPORT_SSE_C_KEY_BASE64",
];
const OPTIONAL_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV = ["STORAGE_SESSION_TOKEN", "STORAGE_FORCE_PATH_STYLE"];
const STAGING_STORAGE_CREDENTIAL_SOURCES = Object.freeze({
web: Object.freeze({
STORAGE_ACCESS_KEY_ID: "WEB_STORAGE_ACCESS_KEY_ID",
STORAGE_SECRET_ACCESS_KEY: "WEB_STORAGE_SECRET_ACCESS_KEY",
STORAGE_SESSION_TOKEN: "WEB_STORAGE_SESSION_TOKEN",
}),
"worker-general": Object.freeze({
STORAGE_ACCESS_KEY_ID: "WORKER_GENERAL_STORAGE_ACCESS_KEY_ID",
STORAGE_SECRET_ACCESS_KEY: "WORKER_GENERAL_STORAGE_SECRET_ACCESS_KEY",
STORAGE_SESSION_TOKEN: "WORKER_GENERAL_STORAGE_SESSION_TOKEN",
}),
"worker-import": Object.freeze({
STORAGE_ACCESS_KEY_ID: "WORKER_IMPORT_STORAGE_ACCESS_KEY_ID",
STORAGE_SECRET_ACCESS_KEY: "WORKER_IMPORT_STORAGE_SECRET_ACCESS_KEY",
STORAGE_SESSION_TOKEN: "WORKER_IMPORT_STORAGE_SESSION_TOKEN",
}),
});
const REQUIRED_WORKER_IMPORT_DEDICATED_GATE_ENV = [
["IMPORT_QUEUE_ENABLED", "false"],
["IMPORT_QUEUE_RUNTIME_READY", "false"],
["PORTAL_GDPR_EXPORT_QUEUE_ENABLED", "true"],
];
const SCAN_WORKER_SERVICE = "worker-general";
const NON_SCAN_WORKER_SERVICES = REQUIRED_WORKER_GROUPS.map((worker) => worker.service).filter((service) => service !== SCAN_WORKER_SERVICE);
function stagingStorageSource(service, envName) {
return STAGING_STORAGE_CREDENTIAL_SOURCES[service]?.[envName] ?? envName;
}
const WEB_TARGET_PATTERN = /^FROM\s+base\s+AS\s+web\s*$/im;
const API_TARGET_PATTERN = /^FROM\s+base\s+AS\s+api\s*$/im;
const MCP_TARGET_PATTERN = /^FROM\s+base\s+AS\s+mcp\s*$/im;
const WORKER_TARGET_PATTERN = /^FROM\s+base\s+AS\s+worker\s*$/im;
const WEB_BUILD_TARGET_PATTERN = /^FROM\s+build\s+AS\s+web-build\s*$/im;
const API_BUILD_TARGET_PATTERN = /^FROM\s+build\s+AS\s+api-build\s*$/im;
const MCP_BUILD_TARGET_PATTERN = /^FROM\s+build\s+AS\s+mcp-build\s*$/im;
const PORTAL_CONTROLLER_BUILD_TARGET_PATTERN = /^FROM\s+build\s+AS\s+portal-domain-controller-build\s*$/im;
const WORKER_BUILD_TARGET_PATTERN = /^FROM\s+build\s+AS\s+worker-build\s*$/im;
const BUILD_STAGE_FORBIDDEN_APP_BUILD_PATTERN =
/\bpnpm\s+(?:run\s+)?build\b|\bpnpm\s+--filter\s+@hivegrid\/web\s+build\b|\bpnpm\s+--filter\s+@hivegrid\/api\s+build\b|\bpnpm\s+--filter\s+@hivegrid\/worker\s+build\b/;
const SHARED_BUILD_FORBIDDEN_VITE_PATTERN = /\b(?:ARG|ENV)\s+VITE_[A-Z0-9_]+/;
const WEB_BUILD_COMMAND_PATTERN = /^RUN\s+node\s+scripts\/build-web-surfaces\.mjs\s+&&\s+node\s+scripts\/build-web-server\.mjs\s*$/im;
const WEB_HOCUSPOCUS_BUILD_ARG_PATTERN = /^ARG\s+VITE_HOCUSPOCUS_URL=""\s*$/im;
const WEB_HOCUSPOCUS_BUILD_ENV_PATTERN = /^ENV\s+VITE_HOCUSPOCUS_URL="\$\{VITE_HOCUSPOCUS_URL\}"\s*$/im;
const API_BUILD_COMMAND_PATTERN = /^RUN\s+node\s+apps\/api\/scripts\/build\.mjs\s*$/im;
const MCP_BUILD_COMMAND_PATTERN = /^RUN\s+node\s+apps\/mcp\/scripts\/build\.mjs\s*$/im;
const PORTAL_CONTROLLER_BUILD_COMMAND_PATTERN = /^RUN\s+cd\s+apps\/portal-domain-controller\s+&&\s+node\s+scripts\/build\.mjs\s*$/im;
const WORKER_BUILD_COMMAND_PATTERN = /^RUN\s+cd\s+apps\/worker\s+&&\s+node\s+scripts\/build\.mjs\s*$/im;
const WEB_RUNTIME_ARTIFACTS_TARGET_PATTERN = /^FROM\s+web-build\s+AS\s+web-runtime-artifacts\s*$/im;
const API_RUNTIME_ARTIFACTS_TARGET_PATTERN = /^FROM\s+api-build\s+AS\s+api-runtime-artifacts\s*$/im;
const WORKER_RUNTIME_ARTIFACTS_TARGET_PATTERN = /^FROM\s+worker-build\s+AS\s+worker-runtime-artifacts\s*$/im;
const WEB_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN =
/^RUN\s+find\s+\/myapp\/build\s+\/myapp\/apps\/web\/build\s+\/myapp\/apps\/web\/build-portal\s+-type\s+f\s+-name\s+['"]\*\.map['"]\s+-delete\s*$/im;
const API_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN = /^RUN\s+find\s+\/myapp\/apps\/api\/dist\s+-type\s+f\s+-name\s+['"]\*\.map['"]\s+-delete\s*$/im;
const WORKER_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN = /^RUN\s+find\s+\/myapp\/apps\/worker\/dist\s+-type\s+f\s+-name\s+['"]\*\.map['"]\s+-delete\s*$/im;
const RUNTIME_DEPLOY_TARGETS = [
{ target: "web", stage: "web-production-deps", packageName: "@hivegrid/runtime-web", helperFunction: "deploy_web", prunePrisma: true },
{
target: "api",
stage: "api-production-deps",
packageName: "@hivegrid/runtime-api",
helperFunction: "deploy_api",
omitOptional: true,
prunePrisma: true,
},
{
target: "mcp",
stage: "mcp-production-deps",
packageName: "@hivegrid/runtime-mcp",
helperFunction: "deploy_mcp",
omitOptional: true,
prunePrisma: true,
},
{
target: "portal-domain-controller",
stage: "portal-domain-controller-production-deps",
packageName: "@hivegrid/runtime-portal-domain-controller",
helperFunction: "deploy_portal_controller",
omitOptional: true,
prunePrisma: false,
},
{ target: "worker", stage: "worker-production-deps", packageName: "@hivegrid/runtime-worker", helperFunction: "deploy_worker", prunePrisma: true },
];
const PRODUCTION_DEPS_HELPER_PATH = "scripts/ci/deploy-production-closures.sh";
const PRODUCTION_DEPS_BUNDLE_TARGET_PATTERN = /^FROM\s+build\s+AS\s+production-deps-bundle\s*$/im;
const SERVER_RUNTIME_COPY_PATTERN = /^COPY\s+--from=web-runtime-artifacts\s+\/myapp\/build\/server-runtime\s+\/myapp\/build\/server-runtime\s*$/im;
const START_SH_ENTRYPOINT_PATTERN = /^ENTRYPOINT\s+\[\s*["']\.\/start\.sh["']\s*\]\s*$/im;
const APP_REVISION_ARG_PATTERN = /\bARG\s+APP_REVISION(?:=unknown)?\b/;
const APP_REVISION_ENV_PATTERN = /\bENV\s+APP_REVISION=["']?\$\{APP_REVISION\}["']?/;
const APP_REVISION_LABEL_PATTERN = /\bLABEL\s+org\.opencontainers\.image\.revision=["']?\$\{APP_REVISION\}["']?/;
const WEB_HEALTHCHECK_PATTERN =
/\bHEALTHCHECK[\s\S]*?(?:(?:\bcurl\b|\bnode\s+-e\b)[\s\S]*?127\.0\.0\.1:\$\{PORT\}\/healthz|hostname\s*:\s*["']127\.0\.0\.1["'][\s\S]*?port\s*:\s*process\.env\.PORT[\s\S]*?path\s*:\s*["']\/healthz["'])/;
const WEB_HEALTHCHECK_HOST_PATTERN = /headers\s*:\s*\{\s*host\s*:\s*process\.env\.WEB_APP_HOST/;
const API_HEALTHCHECK_PATTERN = /\bHEALTHCHECK[\s\S]*?(?:\bcurl\b|\bnode\s+-e\b)[\s\S]*?127\.0\.0\.1:\$\{API_PORT:-\$\{PORT\}\}\/healthz/;
const MCP_HEALTHCHECK_PATTERN = /\bHEALTHCHECK[\s\S]*?\bnode\s+-e\b[\s\S]*?127\.0\.0\.1:\$\{MCP_PORT:-\$\{PORT\}\}\/healthz/;
const IMAGE_REVISION_FILE_PATTERN = /^RUN\s+printf\s+'%s\\n'\s+"\$APP_REVISION"\s+>\s+\/myapp\/\.image-revision\s*$/im;
const RECURSIVE_CHOWN_PATTERN = /\bchown\s+-R\b/;
const EXPOSE_8080_PATTERN = /^EXPOSE\s+8080\s*$/im;
const API_RUNTIME_PATTERN = /\bnode\b[\s\S]*?(?:\/myapp\/)?apps\/api\/dist\/server\.js\b/;
const MCP_RUNTIME_PATTERN = /\bnode\b[\s\S]*?(?:\/myapp\/)?apps\/mcp\/dist\/server\.js\b/;
const WORKER_RUNTIME_PATTERN = /\bnode\b[\s\S]*?(?:\/myapp\/)?apps\/worker\/dist\/index\.js\b/;
const WORKER_HEALTH_PATTERN = /\bnode\b[\s\S]*?(?:\/myapp\/)?apps\/worker\/dist\/health\.js\b/;
const FAST_START_HEALTHCHECK_CADENCE_PATTERN = /\bHEALTHCHECK\s+--interval=30s\b[^\n]*--start-interval=2s\b[^\n]*--retries=3\b/;
const DIRECT_PRISMA_GENERATE_PATTERN = /^RUN\s+\.\/node_modules\/\.bin\/prisma\s+generate\s+--config\s+prisma\.config\.ts\s*$/im;
const DIRECT_DB_RUNTIME_BUILD_PATTERN = /^RUN\s+node\s+packages\/db\/scripts\/build-runtime-artifacts\.mjs\s*$/im;
const DB_RUNTIME_BUILD_SCRIPT_PATH = "packages/db/scripts/build-runtime-artifacts.mjs";
const DB_RUNTIME_ARTIFACTS = [
["packages/db/src/permissions-seed.server.ts", "packages/db/dist/permissions-seed.server.js"],
["packages/db/src/deploy-permission-catalog-sync.server.ts", "packages/db/dist/deploy-permission-catalog-sync.server.js"],
["prisma/seed.ts", "packages/db/dist/production-seed.server.js"],
["scripts/seed-e2e-readiness.ts", "packages/db/dist/e2e-readiness-seed.server.js"],
];
const FORBIDDEN_DOCKERIGNORE_PATTERNS = [
["build", "**/build"],
["dist", "**/dist"],
["cache", "**/cache"],
["report", "**/report"],
];
const REQUIRED_DOCKERIGNORE_PATTERNS = ["public/build", "/build", "/apps/*/build", "apps/*/dist", "packages/*/dist"];
const REQUIRED_DOCKERIGNORE_ARTIFACT_PATTERNS = [
"/docs",
"/secrets",
"/.gitea",
"/.github",
"/tests",
"/e2e",
"**/*.test.ts",
"**/*.test.tsx",
"**/*.test.mjs",
"**/*.test.js",
"**/*.spec.ts",
"/scripts/ci/deploy-buildkitd.toml",
"/scripts/ci/verify-deploy-buildkit-config.mjs",
"/scripts/verify-gitea-ci-workflow.mjs",
"/scripts/verify-gitea-deploy-workflow.mjs",
".cache",
"**/.cache",
".eslintcache",
"**/.eslintcache",
"coverage",
"**/coverage",
".nyc_output",
"**/.nyc_output",
"reports",
"**/reports",
"playwright-report",
"**/playwright-report",
"test-results",
"**/test-results",
"blob-report",
"**/blob-report",
];
const API_REQUIRED_COPY_PATTERNS = [
[
"target-specific node_modules",
/^COPY\s+--exclude=\*\*\/\*\.map\s+--exclude=\*\*\/\*\.d\.ts\s+--from=api-production-deps\s+\/prod\/api\/node_modules\s+\/myapp\/node_modules\s*$/im,
],
["apps/api/dist", /^COPY\s+--from=api-runtime-artifacts\s+\/myapp\/apps\/api\/dist\s+\/myapp\/apps\/api\/dist\s*$/im],
];
const WORKER_REQUIRED_COPY_PATTERNS = [
[
"target-specific node_modules",
/^COPY\s+--exclude=\*\*\/\*\.map\s+--exclude=\*\*\/\*\.d\.ts\s+--from=worker-production-deps\s+\/prod\/worker\/node_modules\s+\/myapp\/node_modules\s*$/im,
],
["apps/worker/dist", /^COPY\s+--from=worker-runtime-artifacts\s+\/myapp\/apps\/worker\/dist\s+\/myapp\/apps\/worker\/dist\s*$/im],
];
const WEB_REQUIRED_COPY_PATTERNS = [
[
"target-specific node_modules",
/^COPY\s+--exclude=\*\*\/\*\.map\s+--exclude=\*\*\/\*\.d\.ts\s+--from=web-production-deps\s+\/prod\/web\/node_modules\s+\/myapp\/node_modules\s*$/im,
],
["Core React Router build", /^COPY\s+--from=web-runtime-artifacts\s+\/myapp\/apps\/web\/build\s+\/myapp\/apps\/web\/build\s*$/im],
["Customer Portal React Router build", /^COPY\s+--from=web-runtime-artifacts\s+\/myapp\/apps\/web\/build-portal\s+\/myapp\/apps\/web\/build-portal\s*$/im],
["migrator role verifier", /^COPY\s+--from=build\s+\/myapp\/scripts\/verify-migrator-role\.mjs\s+\/myapp\/scripts\/verify-migrator-role\.mjs\s*$/im],
["Prisma baseline adopter", /^COPY\s+--from=build\s+\/myapp\/scripts\/adopt-prisma-baseline\.mjs\s+\/myapp\/scripts\/adopt-prisma-baseline\.mjs\s*$/im],
["prisma.config.ts", /^COPY\s+--from=build\s+\/myapp\/prisma\.config\.ts\s+\/myapp\/prisma\.config\.ts\s*$/im],
["packages/db production seed artifact", /^COPY\s+--from=build\s+\/myapp\/packages\/db\/dist\s+\/myapp\/packages\/db\/dist\s*$/im],
["prisma", /^COPY\s+--from=build\s+\/myapp\/prisma\s+\/myapp\/prisma\s*$/im],
];
const START_SCRIPT_EXEC_PATTERN = /^exec\s+node\s+build\/server-runtime\/server\.js\s*$/m;
const START_SCRIPT_MIGRATE_PATTERN = /^\s*node\s+\/myapp\/node_modules\/prisma\/build\/index\.js\s+migrate\s+deploy\s+--config\s+prisma\.config\.ts\s*$/m;
const START_SCRIPT_MIGRATOR_ROLE_VERIFY_PATTERN = /^\s*node\s+\/myapp\/scripts\/verify-migrator-role\.mjs\s*$/m;
const START_SCRIPT_BASELINE_ADOPTION_PATTERN = /^\s*node\s+\/myapp\/scripts\/adopt-prisma-baseline\.mjs\s+--adopt\s*$/m;
const START_SCRIPT_CURRENT_SCHEMA_VERIFY_PATTERN =
/^\s*node\s+\/myapp\/scripts\/adopt-prisma-baseline\.mjs\s+--verify-current-schema\s*$/m;
const START_SCRIPT_PERMISSION_CATALOG_SYNC_PATTERN = /^\s*node\s+\/myapp\/packages\/db\/dist\/deploy-permission-catalog-sync\.server\.js\s*$/m;
const FULL_WORKSPACE_SOURCE_COPY_PATTERN = /^(?:ADD|COPY)\s+(apps|packages)\s+\1\s*$/im;
const FROZEN_FETCH_PATTERN = /^RUN\s+--mount=type=cache,[^\n]*target=\/pnpm\/store[^\n]*\s+\\?\s*\n\s*pnpm\s+fetch\s+--frozen-lockfile\s*$/im;
const FROZEN_DEV_INSTALL_PATTERN =
/^RUN\s+(?:--mount=type=cache,[^\n]*\s+)?(?:\\\s*\n\s*)?pnpm\s+install\s+--frozen-lockfile\s+--prod=false(?:\s+--offline)?\s*$/im;
const PNPM_BUILDKIT_CACHE_PATTERN = /^RUN\s+--mount=type=cache,[^\n]*target=\/pnpm\/store[^\n]*\s+\\?\s*\n\s*pnpm\s+install\b/im;
const GENERIC_PRODUCTION_NODE_MODULES_COPY_PATTERN = /^COPY\s+--from=production-deps\s+\/myapp\/node_modules\s+\/myapp\/node_modules\s*$/im;
function manifestPath(relativePath) {
return relativePath.split(path.sep).join("/");
}
function escapeRegExp(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function readJsonFile(rootDir, relativePath) {
const absolutePath = path.join(rootDir, relativePath);
return JSON.parse(fs.readFileSync(absolutePath, "utf8"));
}
function hasScript(manifest, scriptName) {
return typeof manifest.scripts?.[scriptName] === "string" && manifest.scripts[scriptName].trim().length > 0;
}
function getComposeServiceBlock(content, serviceName) {
const escaped = escapeRegExp(serviceName);
return (
content.match(new RegExp(`^ ${escaped}:[^\\n]*\\n([\\s\\S]*?)(?=^ [A-Za-z0-9_-]+:[^\\n]*\\n|^volumes:\\n|^networks:\\n|(?![\\s\\S]))`, "m"))?.[1] ?? ""
);
}
function listWorkspacePackageManifests(rootDir) {
const manifests = [];
for (const workspaceRoot of ["apps", "packages", "runtime"]) {
const absoluteWorkspaceRoot = path.join(rootDir, workspaceRoot);
if (!fs.existsSync(absoluteWorkspaceRoot)) {
continue;
}
for (const entry of fs.readdirSync(absoluteWorkspaceRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) {
continue;
}
const relativePath = manifestPath(path.join(workspaceRoot, entry.name, "package.json"));
if (fs.existsSync(path.join(rootDir, relativePath))) {
manifests.push(relativePath);
}
}
}
return manifests.sort();
}
export function checkRequiredFiles(rootDir) {
const passes = [];
const failures = [];
for (const relativePath of REQUIRED_FILES) {
if (fs.existsSync(path.join(rootDir, relativePath))) {
passes.push(`${relativePath}: exists`);
} else {
failures.push(`${relativePath}: missing required Docker baseline file`);
}
}
return { passes, failures, observations: [] };
}
export function checkDockerignoreContract(rootDir) {
const relativePath = ".dockerignore";
const passes = [];
const failures = [];
const content = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const patterns = content
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0 && !line.startsWith("#"));
for (const [sourceName, pattern] of FORBIDDEN_DOCKERIGNORE_PATTERNS) {
if (patterns.includes(pattern)) {
failures.push(`${relativePath}: must not use broad ${pattern}; it can exclude source directories named ${sourceName}`);
} else {
passes.push(`${relativePath}: does not exclude all ${sourceName} directories recursively`);
}
}
for (const pattern of REQUIRED_DOCKERIGNORE_PATTERNS) {
if (patterns.includes(pattern)) {
passes.push(`${relativePath}: ignores generated ${pattern}`);
} else {
failures.push(`${relativePath}: must ignore generated ${pattern} without broad recursive source exclusions`);
}
}
for (const pattern of REQUIRED_DOCKERIGNORE_ARTIFACT_PATTERNS) {
if (patterns.includes(pattern)) {
passes.push(`${relativePath}: ignores Docker context artifact ${pattern}`);
} else {
failures.push(`${relativePath}: must ignore Docker context artifact ${pattern} without broad recursive source exclusions`);
}
}
return { passes, failures, observations: [] };
}
export function checkStartScript(rootDir) {
const relativePath = "start.sh";
const passes = [];
const failures = [];
const content = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
if (/^#!\/bin\/sh\s*$/m.test(content)) {
passes.push(`${relativePath}: uses POSIX shell shebang`);
} else {
failures.push(`${relativePath}: must use #!/bin/sh`);
}
if (START_SCRIPT_EXEC_PATTERN.test(content)) {
passes.push(`${relativePath}: execs the production start process for container signal handling`);
} else {
failures.push(`${relativePath}: must use "exec node build/server-runtime/server.js" so container signals reach the web process without pnpm indirection`);
}
if (/^set -eu\s*$/m.test(content)) {
passes.push(`${relativePath}: fails closed on command errors and unset variables (set -eu)`);
} else {
failures.push(`${relativePath}: must set -eu so migration and runtime credential mistakes fail closed`);
}
if (
/if \[ "\$\{1:-\}" = "migrate" \]; then/.test(content) &&
/DIRECT_URL is required for the one-shot migrator/.test(content) &&
/DATABASE_URL must not be forwarded to the one-shot migrator/.test(content) &&
/AUTHENTIK_ADMIN_JIT_DATABASE_URL belongs only to the web runtime/.test(content) &&
/MIGRATOR_\* aliases must not be forwarded to the one-shot migrator/.test(content) &&
START_SCRIPT_MIGRATOR_ROLE_VERIFY_PATTERN.test(content) &&
START_SCRIPT_BASELINE_ADOPTION_PATTERN.test(content) &&
content.indexOf("node /myapp/scripts/verify-migrator-role.mjs") < content.indexOf("node /myapp/scripts/adopt-prisma-baseline.mjs --adopt") &&
content.indexOf("node /myapp/scripts/adopt-prisma-baseline.mjs --adopt") < content.indexOf('export DATABASE_URL="$DIRECT_URL"') &&
/export DATABASE_URL="\$DIRECT_URL"/.test(content) &&
START_SCRIPT_MIGRATE_PATTERN.test(content) &&
START_SCRIPT_CURRENT_SCHEMA_VERIFY_PATTERN.test(content) &&
content.indexOf("node /myapp/node_modules/prisma/build/index.js migrate deploy --config prisma.config.ts") <
content.indexOf("node /myapp/scripts/adopt-prisma-baseline.mjs --verify-current-schema")
) {
passes.push(`${relativePath}: isolates migrate deploy behind the DIRECT_URL-only one-shot mode`);
} else {
failures.push(
`${relativePath}: must verify the exact non-superuser migrator role and safely adopt the reviewed baseline before running the installed Prisma CLI in a DIRECT_URL-only "migrate" mode without package-manager indirection`
);
}
const migrateModeStartMarker = 'if [ "${1:-}" = "migrate" ]; then';
const migrateModeEndMarker = " exit 0\nfi";
const permissionCatalogSyncCommand = "node /myapp/packages/db/dist/deploy-permission-catalog-sync.server.js";
const migrateModeStartIndex = content.indexOf(migrateModeStartMarker);
const migrateModeEndIndex = migrateModeStartIndex >= 0 ? content.indexOf(migrateModeEndMarker, migrateModeStartIndex) : -1;
const migrateModeBlock =
migrateModeStartIndex >= 0 && migrateModeEndIndex >= 0 ? content.slice(migrateModeStartIndex, migrateModeEndIndex + migrateModeEndMarker.length) : "";
const migratorRoleProbeIndex = migrateModeBlock.indexOf("node /myapp/scripts/verify-migrator-role.mjs");
const baselineAdoptionIndex = migrateModeBlock.indexOf("node /myapp/scripts/adopt-prisma-baseline.mjs --adopt");
const databaseAliasIndex = migrateModeBlock.indexOf('export DATABASE_URL="$DIRECT_URL"');
const migrateIndex = migrateModeBlock.indexOf("node /myapp/node_modules/prisma/build/index.js migrate deploy --config prisma.config.ts");
const currentSchemaVerifyIndex = migrateModeBlock.indexOf("node /myapp/scripts/adopt-prisma-baseline.mjs --verify-current-schema");
const permissionCatalogSyncIndex = migrateModeBlock.indexOf(permissionCatalogSyncCommand);
const migrateModeExitIndex = migrateModeBlock.indexOf(" exit 0");
const permissionCatalogSyncOccurrences = content.split(permissionCatalogSyncCommand).length - 1;
if (
permissionCatalogSyncOccurrences === 1 &&
START_SCRIPT_PERMISSION_CATALOG_SYNC_PATTERN.test(migrateModeBlock) &&
migratorRoleProbeIndex >= 0 &&
migratorRoleProbeIndex < baselineAdoptionIndex &&
baselineAdoptionIndex < databaseAliasIndex &&
databaseAliasIndex < migrateIndex &&
migrateIndex < currentSchemaVerifyIndex &&
currentSchemaVerifyIndex < permissionCatalogSyncIndex &&
permissionCatalogSyncIndex < migrateModeExitIndex
) {
passes.push(`${relativePath}: syncs the permission catalog inside one-shot migrate mode only after successful migrations`);
} else {
failures.push(
`${relativePath}: must run the bundled permission catalog sync exactly once inside one-shot migrate mode after baseline adoption, prisma migrate deploy, and current schema verification`
);
}
if (
/seed credentials and flags must not be forwarded to the one-shot migrator/.test(content) &&
!/\bnode packages\/db\/dist\/production-seed\.server\.js\b/.test(content)
) {
passes.push(`${relativePath}: keeps seeding outside the non-superuser migration authority`);
} else {
failures.push(`${relativePath}: one-shot migrator must reject SEED_* and must not execute the cross-tenant production seed`);
}
if (
/RLS_RUNTIME_MODE=required is mandatory for the web runtime/.test(content) &&
/DATABASE_RUNTIME_ROLE=hivegrid_app is mandatory for the web runtime/.test(content) &&
/DIRECT_URL must not be forwarded to the web runtime/.test(content) &&
/MIGRATOR_\* credentials must not be forwarded to the web runtime/.test(content) &&
/AUTHENTIK_ADMIN_JIT_DATABASE_URL must use the dedicated hivegrid_authentik_jit login/.test(content) &&
/MIGRATE_ON_BOOT is forbidden/.test(content) &&
/SEED_ON_BOOT is forbidden/.test(content) &&
!/RLS_RUNTIME_MODE=off/.test(content)
) {
passes.push(`${relativePath}: rejects privileged credentials and RLS bypasses in the long-lived web runtime`);
} else {
failures.push(`${relativePath}: web runtime must require hivegrid_app with RLS required and reject direct, migrate, and seed env`);
}
if (
/forbidden core session, OAuth, billing, ingress, or domain-controller credential in the portal web runtime/.test(content) &&
/AUTHENTIK_/.test(content) &&
/OAUTH/.test(content) &&
/BILLING/.test(content) &&
/COOLIFY/.test(content) &&
/SSH/.test(content) &&
/TRAEFIK/.test(content) &&
/CLOUDFLARE/.test(content) &&
/DOMAIN_CONTROLLER/.test(content)
) {
passes.push(`${relativePath}: rejects Core and operator credentials inside the portal process itself`);
} else {
failures.push(`${relativePath}: portal runtime must reject Core session, OAuth, billing, ingress, and domain-controller credentials before Node starts`);
}
if (
/WEB_RUNTIME_SURFACE must be explicitly set to core, admin, or portal/.test(content) &&
/HIVEGRID_RUNTIME_SURFACE must match WEB_RUNTIME_SURFACE/.test(content) &&
/export HIVEGRID_RUNTIME_SURFACE="\$WEB_RUNTIME_SURFACE"/.test(content)
) {
passes.push(`${relativePath}: binds the selected runtime to the matching isolated React Router build`);
} else {
failures.push(`${relativePath}: must validate WEB_RUNTIME_SURFACE and export the matching HIVEGRID_RUNTIME_SURFACE`);
}
return { passes, failures, observations: [] };
}
export function checkDockerComposeWorkerTopology(rootDir) {
const passes = [];
const failures = [];
const composePath = "docker-compose.yml";
const stagingPath = "docker-compose.staging.yml";
const compose = fs.readFileSync(path.join(rootDir, composePath), "utf8");
const staging = fs.readFileSync(path.join(rootDir, stagingPath), "utf8");
for (const [service, port] of [
["redis", "6379"],
["clamav", "3310"],
]) {
const block = getComposeServiceBlock(compose, service);
if (block.includes(`127.0.0.1:${port}:${port}`)) {
passes.push(`${composePath}: ${service} host port ${port} is loopback-only`);
} else {
failures.push(`${composePath}: ${service} host port ${port} must bind to 127.0.0.1 or remain unpublished`);
}
}
for (const { service, group, healthPort, adminPort, concurrencyEnv } of REQUIRED_WORKER_GROUPS) {
const block = getComposeServiceBlock(compose, service);
if (!block) {
failures.push(`${composePath}: missing ${service} service for WORKER_QUEUE_GROUP=${group}`);
} else if (block.includes(`WORKER_QUEUE_GROUP=${group}`)) {
passes.push(`${composePath}: ${service} sets WORKER_QUEUE_GROUP=${group}`);
} else {
failures.push(`${composePath}: ${service} must set WORKER_QUEUE_GROUP=${group}`);
}
if (!block) {
// Already reported above.
} else if (block.includes(concurrencyEnv)) {
passes.push(`${composePath}: ${service} exposes ${concurrencyEnv}`);
} else {
failures.push(`${composePath}: ${service} must expose ${concurrencyEnv}`);
}
if (!block) {
// Already reported above.
} else if (block.includes(`WORKER_HEALTH_PORT=${healthPort}`) && /test:\s*\["CMD",\s*"node",\s*"apps\/worker\/dist\/health\.js"\]/.test(block)) {
passes.push(`${composePath}: ${service} uses the dependency-free health probe on port ${healthPort}`);
} else {
failures.push(`${composePath}: ${service} must use WORKER_HEALTH_PORT=${healthPort} and the node apps/worker/dist/health.js probe`);
}
if (!block) {
// Already reported above.
} else if (block.includes(`WORKER_ADMIN_PORT=${adminPort}`) && adminPort !== healthPort) {
passes.push(`${composePath}: ${service} uses distinct WORKER_ADMIN_PORT=${adminPort}`);
} else {
failures.push(`${composePath}: ${service} must set WORKER_ADMIN_PORT=${adminPort} distinct from WORKER_HEALTH_PORT=${healthPort}`);
}
const stagingBlock = getComposeServiceBlock(staging, service);
if (!stagingBlock) {
failures.push(`${stagingPath}: missing ${service} staging overlay`);
continue;
}
if (
stagingBlock.includes("WORKER_DATABASE_URL") &&
stagingBlock.includes("DATABASE_RUNTIME_ROLE=hivegrid_worker") &&
stagingBlock.includes("RLS_RUNTIME_MODE=required") &&
!stagingBlock.includes("DIRECT_URL=") &&
!stagingBlock.includes("AUTHENTIK_ADMIN_JIT_DATABASE_URL") &&
stagingBlock.includes("condition: service_healthy")
) {
passes.push(`${stagingPath}: ${service} receives only the RLS-enforced worker database credential`);
} else {
failures.push(`${stagingPath}: ${service} must use WORKER_DATABASE_URL with hivegrid_worker, RLS required, no DIRECT_URL, and a PgBouncer dependency`);
}
}
for (const service of ["api", "web", "hocuspocus"]) {
const block = getComposeServiceBlock(staging, service);
const hasExpectedJitCredential =
service === "web"
? block.includes("AUTHENTIK_ADMIN_JIT_DATABASE_URL=${AUTHENTIK_ADMIN_JIT_DATABASE_URL:-}")
: !block.includes("AUTHENTIK_ADMIN_JIT_DATABASE_URL");
if (
block.includes("APP_DATABASE_URL") &&
block.includes("DATABASE_RUNTIME_ROLE=hivegrid_app") &&
block.includes("RLS_RUNTIME_MODE=required") &&
!block.includes("DIRECT_URL=") &&
hasExpectedJitCredential
) {
passes.push(`${stagingPath}: ${service} receives only its RLS-enforced database capability`);
} else {
failures.push(`${stagingPath}: ${service} must use hivegrid_app with RLS required while the Authentik JIT credential remains web-only`);
}
}
const migratorBlock = getComposeServiceBlock(staging, "migrator");
if (
migratorBlock.includes('entrypoint: ["./start.sh", "migrate"]') &&
migratorBlock.includes("DIRECT_URL=${MIGRATOR_DIRECT_URL") &&
!migratorBlock.includes("DATABASE_URL=") &&
!migratorBlock.includes("AUTHENTIK_ADMIN_JIT_DATABASE_URL")
) {
passes.push(`${stagingPath}: migrator is a DIRECT_URL-only one-shot service`);
} else {
failures.push(`${stagingPath}: migrator must be a DIRECT_URL-only ./start.sh migrate service`);
}
if (!staging.includes("RLS_RUNTIME_MODE=off")) {
passes.push(`${stagingPath}: contains no RLS runtime bypass`);
} else {
failures.push(`${stagingPath}: must not set RLS_RUNTIME_MODE=off`);
}
const generalBlock = getComposeServiceBlock(compose, "worker-general");
const stagingGeneralBlock = getComposeServiceBlock(staging, "worker-general");
for (const envName of REQUIRED_WORKER_GENERAL_WORKFLOW_ENV) {
if (generalBlock.includes(`${envName}=`)) {
passes.push(`${composePath}: worker-general passes through ${envName}`);
} else {
failures.push(`${composePath}: worker-general must pass through ${envName} so outbox.workflow is explicitly configured`);
}
}
for (const [envName, expectedValue] of REQUIRED_STAGING_WORKER_GENERAL_ENV) {
if (stagingGeneralBlock.includes(`${envName}=${expectedValue}`)) {
passes.push(`${stagingPath}: worker-general forces ${envName}=${expectedValue}`);
} else {
failures.push(`${stagingPath}: worker-general must force ${envName}=${expectedValue} so staging/prod outbox.workflow cannot silently idle`);
}
}
for (const envName of REQUIRED_WORKER_GENERAL_BILLING_ENV) {
if (generalBlock.includes(`${envName}=`)) {
passes.push(`${composePath}: worker-general passes through ${envName} for billing reconciliation`);
} else {
failures.push(`${composePath}: worker-general must pass through ${envName} for billing reconciliation`);
}
if (stagingGeneralBlock.includes(`${envName}=`)) {
passes.push(`${stagingPath}: worker-general configures ${envName} for required billing reconciliation`);
} else {
failures.push(`${stagingPath}: worker-general must configure ${envName} for required billing reconciliation`);
}
}
for (const envName of REQUIRED_WORKER_GENERAL_SEARCH_ENV) {
if (generalBlock.includes(`${envName}=\${${envName}:-}`)) {
passes.push(`${composePath}: worker-general passes through ${envName} for tenant hard-delete search cleanup`);
} else {
failures.push(`${composePath}: worker-general must pass through ${envName} for tenant hard-delete search cleanup`);
}
if (stagingGeneralBlock.includes(`${envName}=\${${envName}:?`)) {
passes.push(`${stagingPath}: worker-general requires ${envName} for tenant hard-delete search cleanup`);
} else {
failures.push(`${stagingPath}: worker-general must require ${envName} for tenant hard-delete search cleanup`);
}
}
if (generalBlock.includes("CLAMAV_HOST=") && generalBlock.includes("CLAMAV_PORT=") && generalBlock.includes("clamav:")) {
passes.push(`${composePath}: worker-general owns ClamAV dependency for file scanning`);
} else {
failures.push(`${composePath}: worker-general must keep CLAMAV_* env and depend on clamav for file scanning`);
}
if (generalBlock.includes("SCAN_QUEUE_ENABLED=${SCAN_QUEUE_ENABLED:-true}")) {
passes.push(`${composePath}: worker-general enables the fail-closed scan queue by default`);
} else {
failures.push(`${composePath}: worker-general must default SCAN_QUEUE_ENABLED to true`);
}
for (const envName of REQUIRED_WORKER_SCAN_ENV) {
if (generalBlock.includes(`${envName}=`)) {
passes.push(`${composePath}: worker-general passes through ${envName} for scan and preview storage`);
} else {
failures.push(`${composePath}: worker-general must pass through ${envName} for scan and preview storage`);
}
}
if (stagingGeneralBlock.includes("SCAN_QUEUE_ENABLED=true")) {
passes.push(`${stagingPath}: worker-general forces SCAN_QUEUE_ENABLED=true`);
} else {
failures.push(`${stagingPath}: worker-general must force SCAN_QUEUE_ENABLED=true`);
}
if (generalBlock.includes("EXPORT_RETENTION_QUEUE_ENABLED=${EXPORT_RETENTION_QUEUE_ENABLED:-false}")) {
passes.push(`${composePath}: worker-general passes through EXPORT_RETENTION_QUEUE_ENABLED with a fail-closed default`);
} else {
failures.push(`${composePath}: worker-general must pass through EXPORT_RETENTION_QUEUE_ENABLED with a fail-closed false default`);
}
if (stagingGeneralBlock.includes("EXPORT_RETENTION_QUEUE_ENABLED=true")) {
passes.push(`${stagingPath}: worker-general forces EXPORT_RETENTION_QUEUE_ENABLED=true for GDPR export hard-delete`);
} else {
failures.push(`${stagingPath}: worker-general must force EXPORT_RETENTION_QUEUE_ENABLED=true for GDPR export hard-delete`);
}
for (const envName of REQUIRED_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV.filter((name) => name !== "GDPR_EXPORT_SSE_C_KEY_BASE64")) {
const sourceName = stagingStorageSource("worker-general", envName);
if (stagingGeneralBlock.includes(`${envName}=\${${sourceName}:?`)) {
passes.push(`${stagingPath}: worker-general requires ${envName} for GDPR export retention`);
} else {
failures.push(`${stagingPath}: worker-general must require ${envName} for GDPR export retention`);
}
}
if (stagingGeneralBlock.includes(`STORAGE_SESSION_TOKEN=\${${stagingStorageSource("worker-general", "STORAGE_SESSION_TOKEN")}:-`)) {
passes.push(`${stagingPath}: worker-general uses its dedicated optional storage session-token source`);
} else {
failures.push(`${stagingPath}: worker-general must use its dedicated optional storage session-token source`);
}
for (const service of NON_SCAN_WORKER_SERVICES) {
const block = getComposeServiceBlock(compose, service);
if (!block) {
continue;
}
if (!block.includes("depends_on:")) {
failures.push(`${composePath}: ${service} must override worker-base depends_on so it does not inherit clamav`);
continue;
}
if (block.includes("clamav:") || block.includes("CLAMAV_HOST=") || block.includes("CLAMAV_PORT=")) {
failures.push(`${composePath}: ${service} must not depend on clamav or receive CLAMAV_* scan env`);
} else if (block.includes("redis:") && block.includes("condition: service_healthy")) {
passes.push(`${composePath}: ${service} depends on redis only and does not wait for ClamAV`);
} else {
failures.push(`${composePath}: ${service} must depend on redis with condition: service_healthy after overriding worker-base depends_on`);
}
}
const importBlock = getComposeServiceBlock(compose, "worker-import");
const stagingImportBlock = getComposeServiceBlock(staging, "worker-import");
for (const [envName] of REQUIRED_WORKER_IMPORT_DEDICATED_GATE_ENV) {
if (importBlock.includes(`${envName}=\${${envName}:-false}`)) {
passes.push(`${composePath}: worker-import passes through ${envName}`);
} else {
failures.push(`${composePath}: worker-import must pass through ${envName} with a fail-closed false default`);
}
}
for (const envName of [...REQUIRED_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV, ...OPTIONAL_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV]) {
if (importBlock.includes(`${envName}=\${${envName}:-`)) {
passes.push(`${composePath}: worker-import passes through ${envName} for encrypted GDPR exports`);
} else {
failures.push(`${composePath}: worker-import must pass through ${envName} for encrypted GDPR exports`);
}
}
for (const [envName, expectedValue] of REQUIRED_WORKER_IMPORT_DEDICATED_GATE_ENV) {
if (stagingImportBlock.includes(`${envName}=${expectedValue}`)) {
passes.push(`${stagingPath}: worker-import forces ${envName}=${expectedValue}`);
} else {
failures.push(`${stagingPath}: worker-import must force ${envName}=${expectedValue} for dedicated encrypted GDPR exports`);
}
}
for (const envName of REQUIRED_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV) {
const sourceName = stagingStorageSource("worker-import", envName);
if (stagingImportBlock.includes(`${envName}=\${${sourceName}:?`)) {
passes.push(`${stagingPath}: worker-import requires ${envName} for encrypted GDPR exports`);
} else {
failures.push(`${stagingPath}: worker-import must require ${envName} for encrypted GDPR exports`);
}
}
for (const envName of OPTIONAL_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV) {
const sourceName = stagingStorageSource("worker-import", envName);
if (stagingImportBlock.includes(`${envName}=\${${sourceName}:-`)) {
passes.push(`${stagingPath}: worker-import passes through optional ${envName} for encrypted GDPR exports`);
} else {
failures.push(`${stagingPath}: worker-import must pass through optional ${envName} for encrypted GDPR exports`);
}
}
const webhookBlock = getComposeServiceBlock(compose, "worker-webhook");
const stagingWebhookBlock = getComposeServiceBlock(staging, "worker-webhook");
const stagingWebBlock = getComposeServiceBlock(staging, "web");
if (stagingWebBlock.includes("REDIS_URL=${REDIS_URL:?")) {
passes.push(`${stagingPath}: web requires REDIS_URL for distributed GDPR export download gates`);
} else {
failures.push(`${stagingPath}: web must require REDIS_URL for distributed GDPR export download gates`);
}
for (const envName of REQUIRED_WEBHOOK_VAPID_ENV) {
const optionalSource = `${envName}=\${${envName}:-}`;
const requiredSource = `${envName}=\${${envName}:?`;
if (webhookBlock.includes(optionalSource) && stagingWebhookBlock.includes(requiredSource)) {
passes.push(`${composePath}: worker-webhook and staging overlay pass through ${envName}`);
} else {
failures.push(`${composePath}: worker-webhook and staging overlay must pass through ${envName}`);
}
}
const requiredPublicKeySource = "WEB_PUSH_VAPID_PUBLIC_KEY=${WEB_PUSH_VAPID_PUBLIC_KEY:?";
if (stagingWebBlock.includes(requiredPublicKeySource) && stagingWebhookBlock.includes(requiredPublicKeySource)) {
passes.push(`${stagingPath}: web and worker-webhook share the VAPID public key source`);
} else {
failures.push(`${stagingPath}: web must receive WEB_PUSH_VAPID_PUBLIC_KEY from the shared deployment source`);
}
for (const envName of REQUIRED_STAGING_WEB_AIRCALL_ENV) {
if (stagingWebBlock.includes(`${envName}=`)) {
passes.push(`${stagingPath}: web passes through ${envName}`);
} else {
failures.push(`${stagingPath}: web must pass through ${envName} for Aircall OAuth`);
}
}
for (const envName of REQUIRED_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV) {
const sourceName = stagingStorageSource("web", envName);
if (stagingWebBlock.includes(`${envName}=\${${sourceName}:?`)) {
passes.push(`${stagingPath}: web requires ${envName} for encrypted GDPR export downloads`);
} else {
failures.push(`${stagingPath}: web must require ${envName} for encrypted GDPR export downloads`);
}
}
for (const envName of OPTIONAL_STAGING_WEB_GDPR_EXPORT_STORAGE_ENV) {
const sourceName = stagingStorageSource("web", envName);
if (stagingWebBlock.includes(`${envName}=\${${sourceName}:-`)) {
passes.push(`${stagingPath}: web passes through optional ${envName} for encrypted GDPR export downloads`);
} else {
failures.push(`${stagingPath}: web must pass through optional ${envName} for encrypted GDPR export downloads`);
}
}
const missingCommandServices = REQUIRED_WORKER_GROUPS.map((worker) => worker.service).filter((service) => !staging.includes(service));
if (missingCommandServices.length === 0) {
passes.push(`${stagingPath}: reference compose command includes all worker group services`);
} else {
failures.push(`${stagingPath}: reference compose command must include ${missingCommandServices.join(", ")}`);
}
return { passes, failures, observations: [] };
}
export function checkRootManifest(rootDir) {
const relativePath = "package.json";
const passes = [];
const failures = [];
const manifest = readJsonFile(rootDir, relativePath);
for (const scriptName of ROOT_REQUIRED_SCRIPTS) {
if (hasScript(manifest, scriptName)) {
passes.push(`${relativePath}: script "${scriptName}" is defined`);
} else {
failures.push(`${relativePath}: missing required script "${scriptName}"`);
}
}
if (!hasScript(manifest, "start")) {
failures.push(`${relativePath}: missing required script "start"`);
} else {
const startCommand = manifest.scripts.start;
if (startCommand === "pnpm --filter @hivegrid/web start") {
passes.push(`${relativePath}: production start script delegates to @hivegrid/web`);
} else {
failures.push(`${relativePath}: script "start" must delegate to @hivegrid/web`);
}
if (/\btsx\b/.test(startCommand)) {
failures.push(`${relativePath}: script "start" must not use tsx for web production runtime`);
} else {
passes.push(`${relativePath}: production start script does not use tsx`);
}
}
return { passes, failures, observations: [] };
}
export function checkDbManifest(rootDir) {
const relativePath = "packages/db/package.json";
const passes = [];
const failures = [];
const manifest = readJsonFile(rootDir, relativePath);
const buildCommand = manifest.scripts?.build;
const bundleCommand =
typeof buildCommand === "string"
? buildCommand
.split("&&")
.map((command) => command.trim())
.find((command) => command.includes("packages/db/src/deploy-permission-catalog-sync.server.ts"))
: undefined;
if (
bundleCommand?.includes("--bundle") &&
bundleCommand.includes("--platform=node") &&
bundleCommand.includes("--format=esm") &&
bundleCommand.includes("--alias:@hivegrid/logger=./packages/logger/src/index.ts") &&
bundleCommand.includes("--outfile=packages/db/dist/deploy-permission-catalog-sync.server.js")
) {
passes.push(`${relativePath}: builds the self-contained deploy permission catalog sync artifact`);
} else {
failures.push(
`${relativePath}: build must bundle deploy-permission-catalog-sync.server.ts for Node and inline the workspace logger into dist/deploy-permission-catalog-sync.server.js`
);
}
return { passes, failures, observations: [] };
}
export function checkWebManifest(rootDir) {
const relativePath = "apps/web/package.json";
const passes = [];
const failures = [];
const manifest = readJsonFile(rootDir, relativePath);
if (!hasScript(manifest, "start")) {
failures.push(`${relativePath}: missing required script "start"`);
} else {
const startCommand = manifest.scripts.start;
if (startCommand === "node ../../build/server-runtime/server.js") {
passes.push(`${relativePath}: owns production web server start command`);
} else {
failures.push(`${relativePath}: script "start" must run node ../../build/server-runtime/server.js`);
}
if (/\btsx\b/.test(startCommand)) {
failures.push(`${relativePath}: script "start" must not use tsx for web production runtime`);
} else {
passes.push(`${relativePath}: production start script does not use tsx`);
}
}
if (manifest.dependencies?.["@hivegrid/api"]) {
failures.push(`${relativePath}: web must not depend on the separately deployed @hivegrid/api app`);
} else {
passes.push(`${relativePath}: does not depend on the separate API application`);
}
return { passes, failures, observations: [] };
}
export function checkApiManifest(rootDir) {
const relativePath = "apps/api/package.json";
const passes = [];
const failures = [];
const manifest = readJsonFile(rootDir, relativePath);
for (const scriptName of API_REQUIRED_SCRIPTS) {
if (hasScript(manifest, scriptName)) {
passes.push(`${relativePath}: script "${scriptName}" is defined`);
} else {
failures.push(`${relativePath}: missing required script "${scriptName}"`);
}
}
if (!hasScript(manifest, "start")) {
failures.push(`${relativePath}: missing required script "start"`);
} else if (/\bnode\b/.test(manifest.scripts.start)) {
passes.push(`${relativePath}: production start script uses node`);
} else {
failures.push(`${relativePath}: script "start" must contain node for production runtime`);
}
return { passes, failures, observations: [] };
}
export function checkWorkerManifest(rootDir) {
const relativePath = "apps/worker/package.json";
const passes = [];
const failures = [];
const manifest = readJsonFile(rootDir, relativePath);
const requiredScripts = [
["start", /\bdist\/index\.js\b/],
["health", /\bdist\/health\.js\b/],
];
for (const [scriptName, distPattern] of requiredScripts) {
if (!hasScript(manifest, scriptName)) {
failures.push(`${relativePath}: missing required script "${scriptName}"`);
continue;
}
const command = manifest.scripts[scriptName];
if (/\bnode\b/.test(command)) {
passes.push(`${relativePath}: script "${scriptName}" uses node`);
} else {
failures.push(`${relativePath}: script "${scriptName}" must contain node for worker production runtime`);
}
if (distPattern.test(command)) {
passes.push(`${relativePath}: script "${scriptName}" targets ${scriptName === "start" ? "dist/index.js" : "dist/health.js"}`);
} else {
failures.push(`${relativePath}: script "${scriptName}" must target ${scriptName === "start" ? "dist/index.js" : "dist/health.js"}`);
}
if (/\btsx\b/.test(command)) {
failures.push(`${relativePath}: script "${scriptName}" must not use tsx for worker production runtime`);
} else {
passes.push(`${relativePath}: script "${scriptName}" does not use tsx`);
}
}
return { passes, failures, observations: [] };
}
export function checkDockerfileWebContract(rootDir) {
const relativePath = "Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const buildBody = getDockerfileTargetBody(dockerfile, "build");
const webBuildBody = getDockerfileTargetBody(dockerfile, "web-build");
const apiBuildBody = getDockerfileTargetBody(dockerfile, "api-build");
const mcpBuildBody = getDockerfileTargetBody(dockerfile, "mcp-build");
const portalControllerBuildBody = getDockerfileTargetBody(dockerfile, "portal-domain-controller-build");
const workerBuildBody = getDockerfileTargetBody(dockerfile, "worker-build");
const webBody = getDockerfileTargetBody(dockerfile, "web");
const webRuntimeArtifactsBody = getDockerfileTargetBody(dockerfile, "web-runtime-artifacts");
const apiRuntimeArtifactsBody = getDockerfileTargetBody(dockerfile, "api-runtime-artifacts");
const workerRuntimeArtifactsBody = getDockerfileTargetBody(dockerfile, "worker-runtime-artifacts");
const dbRuntimeBuildScriptPath = path.join(rootDir, DB_RUNTIME_BUILD_SCRIPT_PATH);
const dbRuntimeBuildSource = fs.existsSync(dbRuntimeBuildScriptPath) ? fs.readFileSync(dbRuntimeBuildScriptPath, "utf8") : "";
if (WEB_TARGET_PATTERN.test(dockerfile)) {
passes.push(`${relativePath}: defines named web target from base`);
} else {
failures.push(`${relativePath}: must define web target with "FROM base AS web"`);
}
if (webBody && SERVER_RUNTIME_COPY_PATTERN.test(webBody)) {
passes.push(`${relativePath}: copies build/server-runtime into web image`);
} else {
failures.push(`${relativePath}: web target must copy /myapp/build/server-runtime from web-runtime-artifacts stage`);
}
if (buildBody && !BUILD_STAGE_FORBIDDEN_APP_BUILD_PATTERN.test(buildBody)) {
passes.push(`${relativePath}: build target leaves web, api, and worker app builds to split stages`);
} else {
failures.push(`${relativePath}: build target must not run root/web/api/worker app builds; use web-build, api-build, and worker-build stages`);
}
if (buildBody && !SHARED_BUILD_FORBIDDEN_VITE_PATTERN.test(buildBody)) {
passes.push(`${relativePath}: build target keeps VITE build args out of api/worker cache paths`);
} else {
failures.push(`${relativePath}: build target must not declare VITE_* ARG/ENV values; keep them in web-build only`);
}
if (
buildBody &&
DIRECT_PRISMA_GENERATE_PATTERN.test(buildBody) &&
!/pnpm\s+(?:--filter\s+@hivegrid\/db\s+)?(?:run\s+)?prisma(?::generate|\s+generate)/u.test(buildBody)
) {
passes.push(`${relativePath}: shared build invokes the pinned Prisma CLI directly`);
} else {
failures.push(`${relativePath}: shared build must invoke ./node_modules/.bin/prisma generate --config prisma.config.ts directly`);
}
if (buildBody && DIRECT_DB_RUNTIME_BUILD_PATTERN.test(buildBody) && !/pnpm\s+--filter\s+@hivegrid\/db\s+build/u.test(buildBody)) {
passes.push(`${relativePath}: shared build invokes the DB runtime artifact builder directly`);
} else {
failures.push(`${relativePath}: shared build must invoke node ${DB_RUNTIME_BUILD_SCRIPT_PATH} directly`);
}
for (const [entryPoint, outfile] of DB_RUNTIME_ARTIFACTS) {
const artifactPattern = new RegExp(`entryPoints:\\s*\\["${escapeRegExp(entryPoint)}"\\][\\s\\S]{0,500}?outfile:\\s*"${escapeRegExp(outfile)}"`, "u");
if (artifactPattern.test(dbRuntimeBuildSource)) {
passes.push(`${DB_RUNTIME_BUILD_SCRIPT_PATH}: builds ${outfile} from ${entryPoint}`);
} else {
failures.push(`${DB_RUNTIME_BUILD_SCRIPT_PATH}: must build ${outfile} from ${entryPoint}`);
}
}
if (
/import\s+\{\s*build\s*\}\s+from\s+"esbuild"/u.test(dbRuntimeBuildSource) &&
/await\s+Promise\.all/u.test(dbRuntimeBuildSource) &&
/packages:\s*"external"/u.test(dbRuntimeBuildSource) &&
/"@hivegrid\/logger":\s*"\.\/packages\/logger\/src\/index\.ts"/u.test(dbRuntimeBuildSource) &&
/"~":\s*"\.\/apps\/web\/app"/u.test(dbRuntimeBuildSource) &&
/createRequire\(import\.meta\.url\)/u.test(dbRuntimeBuildSource) &&
!/child_process|\bpnpm\b/u.test(dbRuntimeBuildSource)
) {
passes.push(`${DB_RUNTIME_BUILD_SCRIPT_PATH}: preserves the four bundled runtime contracts without package-manager subprocesses`);
} else {
failures.push(
`${DB_RUNTIME_BUILD_SCRIPT_PATH}: must use the esbuild API in parallel and preserve external packages, logger/web aliases, and the seed createRequire banner`
);
}
if (WEB_BUILD_TARGET_PATTERN.test(dockerfile) && webBuildBody && WEB_BUILD_COMMAND_PATTERN.test(webBuildBody)) {
passes.push(`${relativePath}: web-build target invokes both direct web build entry points`);
} else {
failures.push(`${relativePath}: web-build must run node scripts/build-web-surfaces.mjs && node scripts/build-web-server.mjs directly`);
}
if (webBuildBody && WEB_HOCUSPOCUS_BUILD_ARG_PATTERN.test(webBuildBody) && WEB_HOCUSPOCUS_BUILD_ENV_PATTERN.test(webBuildBody)) {
passes.push(`${relativePath}: web-build target forwards VITE_HOCUSPOCUS_URL into the Vite and CSP builds`);
} else {
failures.push(`${relativePath}: web-build target must declare ARG VITE_HOCUSPOCUS_URL and forward it with ENV before the web build`);
}
if (API_BUILD_TARGET_PATTERN.test(dockerfile) && apiBuildBody && API_BUILD_COMMAND_PATTERN.test(apiBuildBody)) {
passes.push(`${relativePath}: api-build target invokes its direct build entry point`);
} else {
failures.push(`${relativePath}: api-build must run node apps/api/scripts/build.mjs directly`);
}
if (MCP_BUILD_TARGET_PATTERN.test(dockerfile) && mcpBuildBody && MCP_BUILD_COMMAND_PATTERN.test(mcpBuildBody)) {
passes.push(`${relativePath}: mcp-build target invokes its direct build entry point`);
} else {
failures.push(`${relativePath}: mcp-build must run node apps/mcp/scripts/build.mjs directly`);
}
if (
PORTAL_CONTROLLER_BUILD_TARGET_PATTERN.test(dockerfile) &&
portalControllerBuildBody &&
PORTAL_CONTROLLER_BUILD_COMMAND_PATTERN.test(portalControllerBuildBody)
) {
passes.push(`${relativePath}: portal controller build target invokes its direct cwd-aware entry point`);
} else {
failures.push(`${relativePath}: portal controller build must cd into its app and run node scripts/build.mjs directly`);
}
if (WORKER_BUILD_TARGET_PATTERN.test(dockerfile) && workerBuildBody && WORKER_BUILD_COMMAND_PATTERN.test(workerBuildBody)) {
passes.push(`${relativePath}: worker-build target invokes its direct cwd-aware build entry point`);
} else {
failures.push(`${relativePath}: worker-build must cd into apps/worker and run node scripts/build.mjs directly`);
}
if (
WEB_RUNTIME_ARTIFACTS_TARGET_PATTERN.test(dockerfile) &&
API_RUNTIME_ARTIFACTS_TARGET_PATTERN.test(dockerfile) &&
WORKER_RUNTIME_ARTIFACTS_TARGET_PATTERN.test(dockerfile) &&
webRuntimeArtifactsBody &&
apiRuntimeArtifactsBody &&
workerRuntimeArtifactsBody &&
WEB_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN.test(webRuntimeArtifactsBody) &&
API_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN.test(apiRuntimeArtifactsBody) &&
WORKER_RUNTIME_ARTIFACTS_SOURCEMAP_DELETE_PATTERN.test(workerRuntimeArtifactsBody)
) {
passes.push(`${relativePath}: target-specific runtime artifacts remove web, api, and worker sourcemaps before runtime COPY`);
} else {
failures.push(`${relativePath}: web/api/worker runtime artifact targets must delete their own *.map files before runtime COPY`);
}
if (webBody && (START_SH_ENTRYPOINT_PATTERN.test(webBody) || /\bpnpm\s+start\b|\bnpm\s+start\b|\bnode\s+build\/server-runtime\/server\.js\b/.test(webBody))) {
passes.push(`${relativePath}: web runtime invokes production start path`);
} else {
failures.push(`${relativePath}: web target must keep ENTRYPOINT [ "./start.sh" ] or invoke the root production start path`);
}
if (webBody && RECURSIVE_CHOWN_PATTERN.test(webBody)) {
failures.push(`${relativePath}: web target must not use recursive chown in the runtime image`);
} else if (webBody) {
passes.push(`${relativePath}: web target avoids recursive runtime chown`);
}
if (webBody && WEB_HEALTHCHECK_PATTERN.test(webBody)) {
passes.push(`${relativePath}: web target contains HEALTHCHECK using /healthz`);
} else {
failures.push(`${relativePath}: web target HEALTHCHECK must probe http://127.0.0.1:${"${PORT}"}/healthz`);
}
if (webBody && WEB_HEALTHCHECK_HOST_PATTERN.test(webBody)) {
passes.push(`${relativePath}: web target HEALTHCHECK uses the canonical WEB_APP_HOST`);
} else {
failures.push(`${relativePath}: web target HEALTHCHECK must send WEB_APP_HOST for host-aware routing`);
}
checkFastHealthcheckCadence(webBody, relativePath, "web", passes, failures);
if (webBody && EXPOSE_8080_PATTERN.test(webBody)) {
passes.push(`${relativePath}: web target exposes port 8080`);
} else {
failures.push(`${relativePath}: web target must declare EXPOSE 8080 for Coolify port discovery`);
}
if (webBody && APP_REVISION_ARG_PATTERN.test(webBody) && APP_REVISION_ENV_PATTERN.test(webBody) && APP_REVISION_LABEL_PATTERN.test(webBody)) {
passes.push(`${relativePath}: web target persists APP_REVISION in env and OCI label`);
} else {
failures.push(`${relativePath}: web target must persist APP_REVISION in ENV and org.opencontainers.image.revision label`);
}
for (const [copyName, copyPattern] of WEB_REQUIRED_COPY_PATTERNS) {
if (webBody && copyPattern.test(webBody)) {
passes.push(`${relativePath}: web target copies ${copyName}`);
} else {
failures.push(`${relativePath}: web target must copy /myapp/${copyName}`);
}
}
if (webBody && /\/myapp\/apps\/api(?:\/|\s)/.test(webBody)) {
failures.push(`${relativePath}: web target must not copy API source, manifests, or artifacts`);
} else if (webBody) {
passes.push(`${relativePath}: web target excludes the separate API application`);
}
return { passes, failures, observations: [] };
}
function getDockerfileTargetBody(dockerfile, targetName) {
const targetPattern = new RegExp(`^FROM\\s+\\S+(?:\\s+AS\\s+${targetName})\\s*$`, "im");
const targetMatch = targetPattern.exec(dockerfile);
if (!targetMatch) {
return null;
}
const bodyStart = targetMatch.index + targetMatch[0].length;
const nextTargetMatch = /^FROM\s+/gim.exec(dockerfile.slice(bodyStart));
const bodyEnd = nextTargetMatch ? bodyStart + nextTargetMatch.index : dockerfile.length;
return dockerfile.slice(bodyStart, bodyEnd);
}
function normalizeContinuedCommand(value) {
return value
.replace(/\\\s*\n\s*/g, " ")
.replace(/\s+/g, " ")
.trim();
}
function getDockerfileInstructions(targetBody) {
if (!targetBody) {
return [];
}
const withoutComments = targetBody
.split(/\r?\n/)
.filter((line) => !line.trimStart().startsWith("#"))
.join("\n");
return withoutComments
.split(/\n(?=[A-Z][A-Z0-9_]*\s)/)
.map(normalizeContinuedCommand)
.filter(Boolean);
}
function getShellFunctionBody(source, functionName) {
const functionPattern = new RegExp(`^${escapeRegExp(functionName)}\\(\\)\\s*\\{\\s*\\n([\\s\\S]*?)^\\}`, "m");
return functionPattern.exec(source)?.[1] ?? null;
}
function getShellArrayItems(source, variableName) {
const arrayPattern = new RegExp(`^${escapeRegExp(variableName)}=\\(([^)]*)\\)\\s*$`, "m");
return arrayPattern.exec(source)?.[1]?.trim().split(/\s+/).filter(Boolean) ?? [];
}
function checkFastHealthcheckCadence(targetBody, relativePath, target, passes, failures) {
if (targetBody && FAST_START_HEALTHCHECK_CADENCE_PATTERN.test(targetBody)) {
passes.push(`${relativePath}: ${target} HEALTHCHECK starts at 2s and keeps the 30s/3 steady-state budget`);
} else {
failures.push(`${relativePath}: ${target} HEALTHCHECK must use --start-interval=2s with steady --interval=30s and --retries=3`);
}
}
function supportsCopyExcludeSyntax(dockerfile) {
const match = /^#\s*syntax=docker\/dockerfile:(\d+)\.(\d+)\s*$/mu.exec(dockerfile);
if (!match) return false;
const major = Number(match[1]);
const minor = Number(match[2]);
return major > 1 || (major === 1 && minor >= 19);
}
function expectedProductionDepsInstructions({ target, prunePrisma }) {
const instructions = [];
if (target === "worker") {
instructions.push("RUN node /myapp/scripts/prune-pnpm-runtime-closure.mjs /prod/worker/node_modules prisma typescript");
}
if (prunePrisma) {
instructions.push(
`RUN node /myapp/scripts/prune-prisma-client-runtime.mjs /prod/${target}/node_modules /myapp/packages/db/generated/prisma/client/internal/class.ts`
);
}
instructions.push(
`RUN SBOM_TARGET=${target} SBOM_ROOT_PATH=/prod/${target} SBOM_NODE_MODULES_PATH=/prod/${target}/node_modules SBOM_OUTPUT_PATH=/prod/${target}/sbom-pnpm.json node /myapp/scripts/generate-sbom.mjs`
);
return instructions;
}
export function checkDockerfileDepsManifestContract(rootDir) {
const relativePath = "Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const depsBody = getDockerfileTargetBody(dockerfile, "deps");
const workspaceManifests = listWorkspacePackageManifests(rootDir);
if (supportsCopyExcludeSyntax(dockerfile)) {
passes.push(`${relativePath}: Dockerfile syntax supports COPY --exclude`);
} else {
failures.push(`${relativePath}: must declare docker/dockerfile syntax 1.19 or newer for COPY --exclude`);
}
if (!depsBody) {
failures.push(`${relativePath}: must define deps target with "FROM base AS deps"`);
} else {
if (FULL_WORKSPACE_SOURCE_COPY_PATTERN.test(depsBody)) {
failures.push(`${relativePath}: deps target must copy workspace package manifests, not full apps or packages source, before pnpm install`);
} else {
passes.push(`${relativePath}: deps target does not copy full workspace source before pnpm install`);
}
for (const workspaceManifest of workspaceManifests) {
const copyPattern = new RegExp(`^COPY\\s+${escapeRegExp(workspaceManifest)}\\s+${escapeRegExp(workspaceManifest)}\\s*$`, "m");
if (copyPattern.test(depsBody)) {
passes.push(`${relativePath}: deps target copies ${workspaceManifest}`);
} else {
failures.push(`${relativePath}: deps target must copy ${workspaceManifest} before pnpm install`);
}
}
if (FROZEN_FETCH_PATTERN.test(depsBody)) {
passes.push(`${relativePath}: deps target fetches lockfile dependencies before offline install`);
} else {
failures.push(`${relativePath}: deps target must run pnpm fetch --frozen-lockfile with BuildKit cache before install`);
}
if (FROZEN_DEV_INSTALL_PATTERN.test(depsBody) && /\bpnpm\s+install\s+--frozen-lockfile\s+--prod=false\s+--offline\b/.test(depsBody)) {
passes.push(`${relativePath}: deps target uses frozen offline dev dependency install`);
} else {
failures.push(`${relativePath}: deps target must run pnpm install --frozen-lockfile --prod=false --offline`);
}
if (PNPM_BUILDKIT_CACHE_PATTERN.test(depsBody)) {
passes.push(`${relativePath}: deps target uses BuildKit pnpm store cache`);
} else {
failures.push(`${relativePath}: deps target must use BuildKit cache mount for /pnpm/store during pnpm install`);
}
if (
workspaceManifests.length > 0 &&
workspaceManifests.every((workspaceManifest) => {
const copyPattern = new RegExp(`^COPY\\s+${escapeRegExp(workspaceManifest)}\\s+${escapeRegExp(workspaceManifest)}\\s*$`, "m");
return copyPattern.test(depsBody);
})
) {
passes.push(`${relativePath}: deps stage copies all workspace package manifests before install`);
}
}
const productionDepsBundleBody = getDockerfileTargetBody(dockerfile, "production-deps-bundle");
const expectedBundleInstruction =
"RUN --mount=type=cache,id=hivegrid-pnpm-store-v2,target=/pnpm/store,sharing=locked bash scripts/ci/deploy-production-closures.sh";
const productionDepsBundleInstructions = getDockerfileInstructions(productionDepsBundleBody);
const productionDepsBundleStageCount = (dockerfile.match(/^FROM\s+\S+\s+AS\s+production-deps-bundle\s*$/gim) ?? []).length;
if (
productionDepsBundleStageCount === 1 &&
PRODUCTION_DEPS_BUNDLE_TARGET_PATTERN.test(dockerfile) &&
productionDepsBundleInstructions.length === 1 &&
productionDepsBundleInstructions[0] === expectedBundleInstruction
) {
passes.push(`${relativePath}: production-deps-bundle delegates one locked warm v2 store directly to the closure helper`);
} else {
failures.push(
`${relativePath}: production-deps-bundle must derive from build and contain only one locked hivegrid-pnpm-store-v2 invocation of scripts/ci/deploy-production-closures.sh`
);
}
const helperPath = path.join(rootDir, PRODUCTION_DEPS_HELPER_PATH);
const helperSource = fs.existsSync(helperPath) ? fs.readFileSync(helperPath, "utf8") : "";
const normalizedHelper = normalizeContinuedCommand(helperSource);
if (helperSource && /^set\s+-euo\s+pipefail\s*$/m.test(helperSource)) {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: fails closed on command errors, unset variables, and pipeline failures`);
} else {
failures.push(`${PRODUCTION_DEPS_HELPER_PATH}: must enable set -euo pipefail`);
}
const expectedTasks = RUNTIME_DEPLOY_TARGETS.map(({ helperFunction }) => helperFunction).join(" ");
const expectedTargets = RUNTIME_DEPLOY_TARGETS.map(({ target }) => target).join(" ");
const helperTasks = getShellArrayItems(helperSource, "tasks");
const helperTargets = getShellArrayItems(helperSource, "targets");
const runWaveBody = getShellFunctionBody(helperSource, "run_wave") ?? "";
const normalizedRunWave = normalizeContinuedCommand(runWaveBody);
const hasParallelLaunch = new RegExp(
`local -a pids=\\(\\)[\\s\\S]*for task in "\\$\\{tasks\\[@\\]\\}"; do[\\s\\S]*"\\$task" &[\\s\\S]*pids\\+=\\("\\$!"\\)[\\s\\S]*done`
).test(normalizedRunWave);
const hasCompleteWaitAndFailurePropagation =
/local status=0[\s\S]*for index in "\$\{!pids\[@\]\}"; do[\s\S]*if wait "\$\{pids\[\$index\]\}"; then[\s\S]*else child_status=\$\?;? fi[\s\S]*if \[\[ "\$status" -eq 0 \]\]; then status="\$child_status";? fi[\s\S]*done[\s\S]*return "\$status"/.test(
normalizedRunWave
) && (normalizedRunWave.match(/\bwait\b/g) ?? []).length === 1;
if (helperTasks.join(" ") === expectedTasks && helperTargets.join(" ") === expectedTargets && hasParallelLaunch && hasCompleteWaitAndFailurePropagation) {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: each wave starts all five closures in parallel, reaps every PID, and returns the first failure`);
} else {
failures.push(
`${PRODUCTION_DEPS_HELPER_PATH}: tasks and targets must list the exact five closures and run_wave must collect all five background PIDs, wait for every child, preserve the first non-zero status, and return after all children are reaped`
);
}
const initialWaveCall = "run_wave offline-first || initial_status=$?";
const cleanupLoop = 'for target in "${targets[@]}"; do rm -rf -- "/prod/${target}" done';
const fetchCommand = "pnpm fetch --frozen-lockfile";
const retryWaveCall = "run_wave after-fetch || retry_status=$?";
const initialWaveIndex = normalizedHelper.indexOf(initialWaveCall);
const cleanupIndex = normalizedHelper.indexOf(cleanupLoop);
const fetchIndex = normalizedHelper.indexOf(`if ${fetchCommand}; then`);
const retryWaveIndex = normalizedHelper.indexOf(retryWaveCall);
const hasExactFallbackSequence =
initialWaveIndex >= 0 &&
cleanupIndex > initialWaveIndex &&
fetchIndex > cleanupIndex &&
retryWaveIndex > fetchIndex &&
(normalizedHelper.match(/\brun_wave\s+(?:offline-first|after-fetch)\b/g) ?? []).length === 2 &&
(normalizedHelper.match(/\bpnpm\s+fetch\s+--frozen-lockfile\b/g) ?? []).length === 1 &&
(normalizedHelper.match(/\brm\s+-rf\b/g) ?? []).length === 1 &&
/initial_status=0 run_wave offline-first \|\| initial_status=\$\? if \[\[ "\$initial_status" -eq 0 \]\]; then[\s\S]*exit 0[\s\S]*fi/.test(
normalizedHelper
) &&
/if pnpm fetch --frozen-lockfile; then[\s\S]*else fetch_status=\$\?[\s\S]*exit "\$fetch_status"[\s\S]*fi/.test(normalizedHelper) &&
/retry_status=0 run_wave after-fetch \|\| retry_status=\$\? if \[\[ "\$retry_status" -ne 0 \]\]; then[\s\S]*exit "\$retry_status"[\s\S]*fi/.test(
normalizedHelper
);
const hasForbiddenFallbackShortcut =
/sentinel|sha256sum|lockfile[_-]?hash/i.test(helperSource) ||
/\|\|\s*(?:true|:|exit\s+0)\b/.test(helperSource) ||
/\brm\s+-rf\s+(?:--\s+)?["']?\/prod["']?(?:\s|;|$)/.test(helperSource) ||
/\brm\s+-rf\s+(?:--\s+)?["']?\/prod\/\*/.test(helperSource);
if (hasExactFallbackSequence && !hasForbiddenFallbackShortcut) {
passes.push(
`${PRODUCTION_DEPS_HELPER_PATH}: retries one complete wave only after exact-target cleanup and one frozen fetch, with no sentinel or fail-open bypass`
);
} else {
failures.push(
`${PRODUCTION_DEPS_HELPER_PATH}: must try one offline-first wave, exit on success, clean exactly the five /prod targets on failure, fetch once, retry one complete wave, and propagate fetch or retry failure without a sentinel`
);
}
if ((normalizedHelper.match(/\bpnpm\s+--offline\b/g) ?? []).length !== RUNTIME_DEPLOY_TARGETS.length) {
failures.push(`${PRODUCTION_DEPS_HELPER_PATH}: must contain exactly five offline pnpm deployment commands`);
} else {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: contains exactly five offline pnpm deployment commands`);
}
const hasLockedMetadataWalk = /prune_runtime_metadata/u.test(helperSource) || /find\s+[^\n]*node_modules[^\n]*(?:\*\.map|\*\.d\.ts)/u.test(helperSource);
if (!hasLockedMetadataWalk) {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: releases the pnpm store lock without recursive metadata walks`);
} else {
failures.push(`${PRODUCTION_DEPS_HELPER_PATH}: must not run metadata find/delete work while the locked production closure RUN is active`);
}
for (const { target, stage, packageName, helperFunction, omitOptional, prunePrisma } of RUNTIME_DEPLOY_TARGETS) {
const productionDepsBody = getDockerfileTargetBody(dockerfile, stage);
const helperFunctionBody = getShellFunctionBody(helperSource, helperFunction);
const expectedDeployCommand = `pnpm --offline --config.inject-workspace-packages=true --filter ${packageName} deploy --prod${
omitOptional ? " --no-optional" : ""
} /prod/${target}`;
const helperFunctionCount = (helperSource.match(new RegExp(`^${escapeRegExp(helperFunction)}\\(\\)\\s*\\{`, "gm")) ?? []).length;
const hasExactDeployCommand = helperFunctionCount === 1 && helperFunctionBody && normalizeContinuedCommand(helperFunctionBody) === expectedDeployCommand;
if (hasExactDeployCommand) {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: ${helperFunction} deploys exactly the isolated ${packageName} production closure`);
} else {
failures.push(`${PRODUCTION_DEPS_HELPER_PATH}: ${helperFunction} must run exactly "${expectedDeployCommand}" with no extra or missing flags`);
}
const stagePattern = new RegExp(`^FROM\\s+production-deps-bundle\\s+AS\\s+${escapeRegExp(stage)}\\s*$`, "im");
const stageDeclarationCount = (dockerfile.match(new RegExp(`^FROM\\s+\\S+\\s+AS\\s+${escapeRegExp(stage)}\\s*$`, "gim")) ?? []).length;
const expectedStageInstructions = expectedProductionDepsInstructions({ target, prunePrisma });
const actualStageInstructions = getDockerfileInstructions(productionDepsBody);
if (
stageDeclarationCount === 1 &&
stagePattern.test(dockerfile) &&
actualStageInstructions.length === expectedStageInstructions.length &&
actualStageInstructions.every((instruction, index) => instruction === expectedStageInstructions[index])
) {
passes.push(`${relativePath}: ${stage} derives from production-deps-bundle and performs only required reachability pruning and SBOM generation`);
} else {
failures.push(`${relativePath}: ${stage} must derive from production-deps-bundle and contain only its required post-deploy prune and SBOM instructions`);
}
if (omitOptional && hasExactDeployCommand) {
passes.push(`${PRODUCTION_DEPS_HELPER_PATH}: ${helperFunction} omits optional peer toolchains from the runtime closure`);
}
const normalizedProductionDepsBody = normalizeContinuedCommand(productionDepsBody ?? "");
if (prunePrisma) {
const prismaRuntimePruneCommand = `node /myapp/scripts/prune-prisma-client-runtime.mjs /prod/${target}/node_modules /myapp/packages/db/generated/prisma/client/internal/class.ts`;
if (normalizedProductionDepsBody.includes(prismaRuntimePruneCommand)) {
passes.push(`${relativePath}: ${stage} keeps only the generated PostgreSQL Prisma runtime`);
} else {
failures.push(`${relativePath}: ${stage} must prune unused Prisma query compilers before SBOM generation`);
}
}
const targetSbomPattern = new RegExp(
`SBOM_TARGET=${target}[\\s\\S]*SBOM_ROOT_PATH=\\/prod\\/${target}[\\s\\S]*SBOM_NODE_MODULES_PATH=\\/prod\\/${target}\\/node_modules[\\s\\S]*SBOM_OUTPUT_PATH=\\/prod\\/${target}\\/sbom-pnpm\\.json[\\s\\S]*generate-sbom\\.mjs`
);
if (productionDepsBody && targetSbomPattern.test(productionDepsBody)) {
passes.push(`${relativePath}: ${stage} generates an SBOM from its pruned runtime tree`);
} else {
failures.push(`${relativePath}: ${stage} must generate its SBOM from /prod/${target}/node_modules after pruning`);
}
if (target === "worker") {
const removesOptionalToolchains = normalizedProductionDepsBody.includes(
"node /myapp/scripts/prune-pnpm-runtime-closure.mjs /prod/worker/node_modules prisma typescript"
);
if (removesOptionalToolchains) {
passes.push(`${relativePath}: worker-production-deps removes optional peer toolchains and their unreachable dependencies`);
} else {
failures.push(`${relativePath}: worker-production-deps must prune optional Prisma and TypeScript peer closures by reachability`);
}
}
const runtimeBody = getDockerfileTargetBody(dockerfile, target);
const expectedDependencyCopy = `COPY --exclude=**/*.map --exclude=**/*.d.ts --from=${stage} /prod/${target}/node_modules /myapp/node_modules`;
const dependencyCopyInstructions = getDockerfileInstructions(runtimeBody).filter((instruction) =>
instruction.endsWith(`/prod/${target}/node_modules /myapp/node_modules`)
);
if (dependencyCopyInstructions.length === 1 && dependencyCopyInstructions[0] === expectedDependencyCopy) {
passes.push(`${relativePath}: ${target} target copies only its deployed dependency closure without maps or declarations`);
} else {
failures.push(`${relativePath}: ${target} target dependency COPY must use both --exclude=**/*.map and --exclude=**/*.d.ts from ${stage}`);
}
const sbomCopyPattern = new RegExp(
`^COPY\\s+--from=${escapeRegExp(stage)}\\s+\\/prod\\/${target}\\/sbom-pnpm\\.json\\s+\\/myapp\\/sbom-pnpm\\.json\\s*$`,
"im"
);
if (runtimeBody && sbomCopyPattern.test(runtimeBody)) {
passes.push(`${relativePath}: ${target} target ships its target-specific runtime SBOM`);
} else {
failures.push(`${relativePath}: ${target} target must copy its SBOM from ${stage}`);
}
if (target === "web" || target === "api" || target === "mcp" || target === "portal-domain-controller" || target === "worker") {
const removesPackageManagers =
runtimeBody &&
/rm\s+-rf[\s\S]*\/usr\/local\/lib\/node_modules\/corepack/.test(runtimeBody) &&
/\/usr\/local\/bin\/pnpm/.test(runtimeBody) &&
/\/usr\/local\/bin\/yarn/.test(runtimeBody);
if (removesPackageManagers) {
passes.push(`${relativePath}: ${target} target removes inherited package-manager executables`);
} else {
failures.push(`${relativePath}: ${target} target must remove inherited Corepack, pnpm, and Yarn executables`);
}
}
}
if (GENERIC_PRODUCTION_NODE_MODULES_COPY_PATTERN.test(dockerfile) || /^FROM\s+base\s+AS\s+production-deps\s*$/im.test(dockerfile)) {
failures.push(`${relativePath}: must not build or copy a generic whole-workspace production node_modules closure`);
} else {
passes.push(`${relativePath}: excludes generic whole-workspace production node_modules from runtime images`);
}
if (/^COPY\s+--from=build\s+\/myapp\/sbom-pnpm\.json\s+\/myapp\/sbom-pnpm\.json\s*$/im.test(dockerfile)) {
failures.push(`${relativePath}: runtime targets must not copy the full-workspace build SBOM`);
} else {
passes.push(`${relativePath}: runtime targets do not ship a full-workspace build SBOM`);
}
return { passes, failures, observations: [] };
}
export function checkDockerfileWorkerContract(rootDir) {
const relativePath = "Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const workerBody = getDockerfileTargetBody(dockerfile, "worker");
if (WORKER_TARGET_PATTERN.test(dockerfile)) {
passes.push(`${relativePath}: defines named worker target from base`);
} else {
failures.push(`${relativePath}: must define worker target with "FROM base AS worker"`);
}
if (workerBody && WORKER_RUNTIME_PATTERN.test(workerBody)) {
passes.push(`${relativePath}: worker runtime invokes @hivegrid/worker production start path`);
} else {
failures.push(`${relativePath}: worker target must invoke node apps/worker/dist/index.js directly`);
}
if (workerBody && WORKER_HEALTH_PATTERN.test(workerBody)) {
passes.push(`${relativePath}: worker target invokes worker health path directly`);
} else {
failures.push(`${relativePath}: worker target must invoke node apps/worker/dist/health.js directly`);
}
checkFastHealthcheckCadence(workerBody, relativePath, "worker", passes, failures);
if (workerBody && APP_REVISION_ARG_PATTERN.test(workerBody) && APP_REVISION_ENV_PATTERN.test(workerBody) && APP_REVISION_LABEL_PATTERN.test(workerBody)) {
passes.push(`${relativePath}: worker target persists APP_REVISION in env and OCI label`);
} else {
failures.push(`${relativePath}: worker target must persist APP_REVISION in ENV and org.opencontainers.image.revision label`);
}
if (workerBody && IMAGE_REVISION_FILE_PATTERN.test(workerBody)) {
passes.push(`${relativePath}: worker target writes the immutable image revision file before dropping privileges`);
} else {
failures.push(`${relativePath}: worker target must write root-owned /myapp/.image-revision from APP_REVISION`);
}
if (workerBody && /\btsx\b/.test(workerBody)) {
failures.push(`${relativePath}: worker target must not use tsx at runtime`);
} else if (workerBody) {
passes.push(`${relativePath}: worker target runtime does not use tsx`);
}
if (workerBody && RECURSIVE_CHOWN_PATTERN.test(workerBody)) {
failures.push(`${relativePath}: worker target must not use recursive chown in the runtime image`);
} else if (workerBody) {
passes.push(`${relativePath}: worker target avoids recursive runtime chown`);
}
for (const [copyName, copyPattern] of WORKER_REQUIRED_COPY_PATTERNS) {
if (workerBody && copyPattern.test(workerBody)) {
passes.push(`${relativePath}: worker target copies ${copyName}`);
} else {
failures.push(`${relativePath}: worker target must copy /myapp/${copyName}`);
}
}
return { passes, failures, observations: [] };
}
export function checkDockerfileMcpContract(rootDir) {
const relativePath = "Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const mcpBody = getDockerfileTargetBody(dockerfile, "mcp");
if (MCP_TARGET_PATTERN.test(dockerfile)) {
passes.push(`${relativePath}: defines named MCP target from base`);
} else {
failures.push(`${relativePath}: must define MCP target with "FROM base AS mcp"`);
}
if (mcpBody && MCP_RUNTIME_PATTERN.test(mcpBody)) {
passes.push(`${relativePath}: MCP target invokes node apps/mcp/dist/server.js directly`);
} else {
failures.push(`${relativePath}: MCP target must invoke node apps/mcp/dist/server.js directly`);
}
if (mcpBody && MCP_HEALTHCHECK_PATTERN.test(mcpBody)) {
passes.push(`${relativePath}: MCP target contains dependency-free Node HEALTHCHECK using /healthz`);
} else {
failures.push(`${relativePath}: MCP target HEALTHCHECK must probe http://127.0.0.1:\${MCP_PORT:-\${PORT}}/healthz with Node`);
}
checkFastHealthcheckCadence(mcpBody, relativePath, "MCP", passes, failures);
if (mcpBody && APP_REVISION_ARG_PATTERN.test(mcpBody) && APP_REVISION_ENV_PATTERN.test(mcpBody) && APP_REVISION_LABEL_PATTERN.test(mcpBody)) {
passes.push(`${relativePath}: MCP target persists APP_REVISION in env and OCI label`);
} else {
failures.push(`${relativePath}: MCP target must persist APP_REVISION in ENV and org.opencontainers.image.revision label`);
}
if (mcpBody && IMAGE_REVISION_FILE_PATTERN.test(mcpBody)) {
passes.push(`${relativePath}: MCP target writes the immutable image revision file before dropping privileges`);
} else {
failures.push(`${relativePath}: MCP target must write root-owned /myapp/.image-revision from APP_REVISION`);
}
if (mcpBody && /^COPY\s+--from=mcp-runtime-artifacts\s+\/myapp\/apps\/mcp\/dist\s+\/myapp\/apps\/mcp\/dist\s*$/im.test(mcpBody)) {
passes.push(`${relativePath}: MCP target copies the compiled runtime including deploy health`);
} else {
failures.push(`${relativePath}: MCP target must copy /myapp/apps/mcp/dist from mcp-runtime-artifacts`);
}
if (mcpBody && /^USER\s+node\s*$/im.test(mcpBody)) {
passes.push(`${relativePath}: MCP target runs as the non-root node user`);
} else {
failures.push(`${relativePath}: MCP target must run as USER node`);
}
if (mcpBody && RECURSIVE_CHOWN_PATTERN.test(mcpBody)) {
failures.push(`${relativePath}: MCP target must not use recursive chown in the runtime image`);
} else if (mcpBody) {
passes.push(`${relativePath}: MCP target avoids recursive runtime chown`);
}
if (mcpBody && /\btsx\b/.test(mcpBody)) {
failures.push(`${relativePath}: MCP target must not use tsx at runtime`);
} else if (mcpBody) {
passes.push(`${relativePath}: MCP target runtime does not use tsx`);
}
if (mcpBody && EXPOSE_8080_PATTERN.test(mcpBody)) {
passes.push(`${relativePath}: MCP target exposes port 8080`);
} else {
failures.push(`${relativePath}: MCP target must declare EXPOSE 8080 for Coolify port discovery`);
}
return { passes, failures, observations: [] };
}
export function checkDockerfileApiContract(rootDir) {
const relativePath = "Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const apiBody = getDockerfileTargetBody(dockerfile, "api");
if (API_TARGET_PATTERN.test(dockerfile)) {
passes.push(`${relativePath}: defines named api target from base`);
} else {
failures.push(`${relativePath}: must define api target with "FROM base AS api"`);
}
if (apiBody && API_RUNTIME_PATTERN.test(apiBody)) {
passes.push(`${relativePath}: api runtime invokes @hivegrid/api production start path`);
} else {
failures.push(`${relativePath}: api target must invoke node apps/api/dist/server.js directly`);
}
if (apiBody && /\btsx\b/.test(apiBody)) {
failures.push(`${relativePath}: api target must not use tsx at runtime`);
} else if (apiBody) {
passes.push(`${relativePath}: api target runtime does not use tsx`);
}
if (apiBody && RECURSIVE_CHOWN_PATTERN.test(apiBody)) {
failures.push(`${relativePath}: api target must not use recursive chown in the runtime image`);
} else if (apiBody) {
passes.push(`${relativePath}: api target avoids recursive runtime chown`);
}
if (apiBody && API_HEALTHCHECK_PATTERN.test(apiBody)) {
passes.push(`${relativePath}: api target contains HEALTHCHECK using /healthz`);
} else {
failures.push(`${relativePath}: api target HEALTHCHECK must probe http://127.0.0.1:\${API_PORT:-\${PORT}}/healthz`);
}
checkFastHealthcheckCadence(apiBody, relativePath, "api", passes, failures);
if (apiBody && EXPOSE_8080_PATTERN.test(apiBody)) {
passes.push(`${relativePath}: api target exposes port 8080`);
} else {
failures.push(`${relativePath}: api target must declare EXPOSE 8080 for Coolify port discovery`);
}
if (apiBody && APP_REVISION_ARG_PATTERN.test(apiBody) && APP_REVISION_ENV_PATTERN.test(apiBody) && APP_REVISION_LABEL_PATTERN.test(apiBody)) {
passes.push(`${relativePath}: api target persists APP_REVISION in env and OCI label`);
} else {
failures.push(`${relativePath}: api target must persist APP_REVISION in ENV and org.opencontainers.image.revision label`);
}
for (const [copyName, copyPattern] of API_REQUIRED_COPY_PATTERNS) {
if (apiBody && copyPattern.test(apiBody)) {
passes.push(`${relativePath}: api target copies ${copyName}`);
} else {
failures.push(`${relativePath}: api target must copy /myapp/${copyName}`);
}
}
return { passes, failures, observations: [] };
}
export function checkHocuspocusDockerfileContract(rootDir) {
const relativePath = "apps/hocuspocus/Dockerfile";
const dockerfile = fs.readFileSync(path.join(rootDir, relativePath), "utf8");
const passes = [];
const failures = [];
const depsBody = getDockerfileTargetBody(dockerfile, "deps");
const buildBody = getDockerfileTargetBody(dockerfile, "build");
const runtimeBody = getDockerfileTargetBody(dockerfile, "hocuspocus");
const productionDepsBody = getDockerfileTargetBody(dockerfile, "hocuspocus-production-deps");
if (supportsCopyExcludeSyntax(dockerfile)) {
passes.push(`${relativePath}: Dockerfile syntax supports COPY --exclude`);
} else {
failures.push(`${relativePath}: must declare docker/dockerfile syntax 1.19 or newer for COPY --exclude`);
}
if (/^FROM\s+deps\s+AS\s+hocuspocus-production-deps\s*$/imu.test(dockerfile)) {
passes.push(`${relativePath}: production dependency closure is cached independently from application source`);
} else {
failures.push(`${relativePath}: hocuspocus-production-deps must derive from the manifest-only deps stage`);
}
if (!depsBody) {
failures.push(`${relativePath}: must define a manifest-only deps target`);
} else {
const allowedCopyPatterns = [
/^ADD\s+package\.json\s+pnpm-lock\.yaml\s+pnpm-workspace\.yaml\s+\.\/$/u,
/^COPY\s+patches\s+\.\/patches$/u,
/^COPY\s+(apps|packages|runtime)\/[^/]+\/package\.json\s+\1\/[^/]+\/package\.json$/u,
];
const invalidDependencyInputs = depsBody
.split(/\r?\n/u)
.map((line) => line.trim())
.filter((line) => /^(?:ADD|COPY)\s+/u.test(line))
.filter((line) => !allowedCopyPatterns.some((pattern) => pattern.test(line)));
if (invalidDependencyInputs.length === 0) {
passes.push(`${relativePath}: dependency cache is invalidated only by manifests, lockfiles, workspace config, and patches`);
} else {
failures.push(`${relativePath}: deps target must not copy source inputs before install (${invalidDependencyInputs.join(", ")})`);
}
const workspaceManifests = listWorkspacePackageManifests(rootDir);
const missingManifests = workspaceManifests.filter(
(workspaceManifest) => !new RegExp(`^COPY\\s+${escapeRegExp(workspaceManifest)}\\s+${escapeRegExp(workspaceManifest)}\\s*$`, "m").test(depsBody)
);
if (missingManifests.length === 0) {
passes.push(`${relativePath}: deps target copies every required workspace package manifest before install`);
} else {
failures.push(`${relativePath}: deps target must copy workspace manifests before install (${missingManifests.join(", ")})`);
}
if (
/\bpnpm\s+fetch\s+--frozen-lockfile\b/u.test(depsBody) &&
/\bpnpm\s+install\s+--frozen-lockfile\s+--prod=false\s+--offline\b/u.test(depsBody) &&
(depsBody.match(/target=\/pnpm\/store/gu)?.length ?? 0) >= 2
) {
passes.push(`${relativePath}: deps target uses a frozen fetched dependency set and offline install`);
} else {
failures.push(`${relativePath}: deps target must use pnpm fetch --frozen-lockfile followed by frozen offline install`);
}
}
if (
buildBody &&
/^RUN\s+\.\/node_modules\/\.bin\/prisma\s+generate\s+--config\s+prisma\.config\.ts\s*$/imu.test(buildBody) &&
/^RUN\s+node\s+apps\/hocuspocus\/scripts\/build\.mjs\s*$/imu.test(buildBody) &&
!/pnpm\s+--filter\s+@hivegrid\/(?:db|hocuspocus)\s+build/u.test(buildBody)
) {
passes.push(`${relativePath}: source-only rebuilds skip workspace resolution and unused database seed builds`);
} else {
failures.push(`${relativePath}: build target must invoke pinned Prisma and Hocuspocus build scripts directly and skip unused database seed artifacts`);
}
const hocusDeployCommand =
"pnpm --offline --config.inject-workspace-packages=true --filter @hivegrid/runtime-hocuspocus deploy --prod --no-optional /prod/hocuspocus";
const hocusClosureInstruction = getDockerfileInstructions(productionDepsBody).find((instruction) => instruction.includes(hocusDeployCommand));
const hasLockedMetadataWalk = /find\s+[^\n]*node_modules[^\n]*(?:\*\.map|\*\.d\.ts)/u.test(productionDepsBody ?? "");
if (hocusClosureInstruction?.endsWith(hocusDeployCommand) && !hasLockedMetadataWalk) {
passes.push(`${relativePath}: deploys the Hocuspocus closure without extending the store lock for metadata walks`);
} else {
failures.push(`${relativePath}: must deploy @hivegrid/runtime-hocuspocus without metadata find/delete work in the locked RUN`);
}
if (
productionDepsBody &&
/COPY\s+--from=build\s+\/myapp\/packages\/db\/generated\/prisma\/client\/internal\/class\.ts\s+\/myapp\/packages\/db\/generated\/prisma\/client\/internal\/class\.ts/u.test(
productionDepsBody.replace(/\\\r?\n\s*/gu, " ")
) &&
/COPY\s+scripts\/prune-prisma-client-runtime\.mjs\s+\/myapp\/scripts\/prune-prisma-client-runtime\.mjs/u.test(productionDepsBody) &&
/node\s+\/myapp\/scripts\/prune-prisma-client-runtime\.mjs\s+\/prod\/hocuspocus\/node_modules\s+\/myapp\/packages\/db\/generated\/prisma\/client\/internal\/class\.ts/u.test(
productionDepsBody.replace(/\\\r?\n\s*/gu, " ")
)
) {
passes.push(`${relativePath}: fail-closed Prisma runtime pruning follows the generated client contract`);
} else {
failures.push(`${relativePath}: must use the generated-client-aware Prisma runtime pruner`);
}
if (
productionDepsBody &&
/SBOM_TARGET=hocuspocus[\s\S]*SBOM_NODE_MODULES_PATH=\/prod\/hocuspocus\/node_modules[\s\S]*SBOM_OUTPUT_PATH=\/prod\/hocuspocus\/sbom-pnpm\.json[\s\S]*generate-sbom\.mjs/.test(
productionDepsBody
)
) {
passes.push(`${relativePath}: generates a target-specific SBOM from the pruned runtime tree`);
} else {
failures.push(`${relativePath}: must generate its SBOM from the pruned Hocuspocus runtime tree`);
}
const expectedHocusDependencyCopy =
"COPY --exclude=**/*.map --exclude=**/*.d.ts --from=hocuspocus-production-deps /prod/hocuspocus/node_modules /myapp/node_modules";
const hocusDependencyCopyInstructions = getDockerfileInstructions(runtimeBody).filter((instruction) =>
instruction.endsWith("/prod/hocuspocus/node_modules /myapp/node_modules")
);
if (hocusDependencyCopyInstructions.length === 1 && hocusDependencyCopyInstructions[0] === expectedHocusDependencyCopy) {
passes.push(`${relativePath}: final image copies production node_modules without maps or declarations`);
} else {
failures.push(`${relativePath}: final dependency COPY must use both --exclude=**/*.map and --exclude=**/*.d.ts from hocuspocus-production-deps`);
}
if (runtimeBody && /^COPY\s+--from=hocuspocus-production-deps\s+\/prod\/hocuspocus\/sbom-pnpm\.json\s+\/myapp\/sbom-pnpm\.json\s*$/im.test(runtimeBody)) {
passes.push(`${relativePath}: final image ships the target-specific runtime SBOM`);
} else {
failures.push(`${relativePath}: final image must copy its SBOM from hocuspocus-production-deps`);
}
if (
/^FROM\s+build\s+AS\s+hocuspocus-runtime-artifacts\s*$/im.test(dockerfile) &&
/find\s+\/myapp\/apps\/hocuspocus\/dist\s+-type\s+f\s+-name\s+['"]\*\.map['"]\s+-delete/.test(dockerfile) &&
runtimeBody &&
/^COPY\s+--from=hocuspocus-runtime-artifacts\s+\/myapp\/apps\/hocuspocus\/dist\s+\/myapp\/apps\/hocuspocus\/dist\s*$/im.test(runtimeBody)
) {
passes.push(`${relativePath}: strips source maps before the runtime copy`);
} else {
failures.push(`${relativePath}: must strip dist source maps in a runtime-artifacts stage`);
}
if (runtimeBody && /^USER\s+node\s*$/im.test(runtimeBody)) {
passes.push(`${relativePath}: final image runs as the non-root node user`);
} else {
failures.push(`${relativePath}: final image must run as USER node`);
}
checkFastHealthcheckCadence(runtimeBody, relativePath, "Hocuspocus", passes, failures);
if (
runtimeBody &&
/rm\s+-rf[\s\S]*\/usr\/local\/lib\/node_modules\/corepack/.test(runtimeBody) &&
/\/usr\/local\/bin\/pnpm/.test(runtimeBody) &&
/\/usr\/local\/bin\/yarn/.test(runtimeBody)
) {
passes.push(`${relativePath}: removes inherited package-manager executables`);
} else {
failures.push(`${relativePath}: must remove inherited Corepack, pnpm, and Yarn executables`);
}
return { passes, failures, observations: [] };
}
export function verifyDockerBaseline(rootDir = process.cwd()) {
const requiredFilesResult = checkRequiredFiles(rootDir);
const results = [requiredFilesResult];
if (fs.existsSync(path.join(rootDir, "Dockerfile"))) {
results.push(checkDockerfileDepsManifestContract(rootDir));
results.push(checkDockerfileWebContract(rootDir));
results.push(checkDockerfileWorkerContract(rootDir));
results.push(checkDockerfileApiContract(rootDir));
results.push(checkDockerfileMcpContract(rootDir));
}
if (fs.existsSync(path.join(rootDir, "apps/hocuspocus/Dockerfile"))) {
results.push(checkHocuspocusDockerfileContract(rootDir));
}
if (fs.existsSync(path.join(rootDir, ".dockerignore"))) {
results.push(checkDockerignoreContract(rootDir));
}
if (fs.existsSync(path.join(rootDir, "start.sh"))) {
results.push(checkStartScript(rootDir));
}
if (fs.existsSync(path.join(rootDir, "docker-compose.yml")) && fs.existsSync(path.join(rootDir, "docker-compose.staging.yml"))) {
results.push(checkDockerComposeWorkerTopology(rootDir));
}
const manifestChecks = [
["package.json", checkRootManifest],
["packages/db/package.json", checkDbManifest],
["apps/web/package.json", checkWebManifest],
["apps/api/package.json", checkApiManifest],
["apps/worker/package.json", checkWorkerManifest],
];
for (const [relativePath, check] of manifestChecks) {
if (fs.existsSync(path.join(rootDir, relativePath))) {
try {
results.push(check(rootDir));
} catch (error) {
results.push({
passes: [],
failures: [`${manifestPath(relativePath)}: failed to read manifest JSON: ${error.message}`],
observations: [],
});
}
}
}
return results.reduce(
(result, check) => ({
passes: [...result.passes, ...check.passes],
failures: [...result.failures, ...check.failures],
observations: [...result.observations, ...check.observations],
}),
{ passes: [], failures: [], observations: [] }
);
}
export function formatVerificationResult(result) {
const status = result.failures.length > 0 ? "FAIL" : "PASS";
return [
`${status} Docker baseline 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 = verifyDockerBaseline(process.cwd());
const output = formatVerificationResult(result);
if (result.failures.length > 0) {
process.stderr.write(output);
process.exitCode = 1;
} else {
process.stdout.write(output);
}
}