Files
gongxue-base/scripts/api-docker-runtime-contract-test.js
2026-07-12 19:26:57 +08:00

26 lines
1.3 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
const repoRoot = process.cwd();
const dockerfile = fs.readFileSync(path.join(repoRoot, 'apps', 'api', 'Dockerfile'), 'utf8');
assert.match(
dockerfile,
/ARG NODE_IMAGE=node:20\.20\.2-alpine3\.23@sha256:fb4cd12c85ee03686f6af5362a0b0d56d50c58a04632e6c0fb8363f609372293/,
'API image must pin the reviewed multi-architecture Node/Alpine manifest',
);
assert.match(dockerfile, /FROM deps AS production-deps/);
assert.match(dockerfile, /npm prune --omit=dev --workspaces --include-workspace-root/);
assert.match(dockerfile, /COPY --from=production-deps --chown=node:node \/app\/node_modules \.\/node_modules/);
assert.match(dockerfile, /COPY --from=build --chown=node:node \/app\/apps\/api\/dist \.\/apps\/api\/dist/);
assert.match(dockerfile, /\nUSER node\n/);
assert.match(dockerfile, /CMD \["node", "apps\/api\/dist\/apps\/api\/src\/server\.js"\]/);
assert.doesNotMatch(
dockerfile.slice(dockerfile.lastIndexOf(`FROM \${NODE_IMAGE} AS runner`)),
/package-lock\.json|package\.json|scripts\/import-pocketbase|COPY --from=deps \/app\/node_modules/,
'runtime stage must contain only production dependencies and compiled API files',
);
console.log('[PASS] API Docker runtime least-privilege contract');