fix(images): minimize ql3 runtime base

This commit is contained in:
whyour
2026-08-22 12:44:49 +08:00
parent ca8ddf34b8
commit f1cca9a3ed
13 changed files with 119 additions and 40 deletions
+17 -5
View File
@@ -517,8 +517,10 @@ function assertExactExternalClosure(readFile, root, findings) {
}
function assertDockerfile(readFile, root, findings) {
const pinnedNodeBase =
const pinnedBuildNodeBase =
'node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d';
const pinnedRuntimeNodeBase =
'node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436';
const dockerfile = readFile(
path.join(root, 'deploy/containers/ql3-cluster-control/Dockerfile'),
'utf8',
@@ -596,11 +598,16 @@ function assertDockerfile(readFile, root, findings) {
),
);
}
if (dockerfile.split(`FROM ${pinnedNodeBase}`).length - 1 !== 2) {
if (
!dockerfile.includes(
`FROM ${pinnedBuildNodeBase} AS dependency-manifest`,
) ||
!dockerfile.includes(`FROM ${pinnedRuntimeNodeBase} AS runtime-base`)
) {
findings.push(
finding(
'QL3_CLUSTER_DOCKERFILE_BASE_IMAGE_NOT_PINNED',
'Cluster control build and runtime stages must use the exact immutable Node base digest',
'Cluster control build and runtime stages must use their exact immutable Node base digests',
),
);
}
@@ -652,11 +659,16 @@ function assertDockerfile(readFile, root, findings) {
);
}
}
if (adminDockerfile.split(`FROM ${pinnedNodeBase}`).length - 1 !== 2) {
if (
!adminDockerfile.includes(
`FROM ${pinnedBuildNodeBase} AS dependency-manifest`,
) ||
!adminDockerfile.includes(`FROM ${pinnedRuntimeNodeBase} AS runtime`)
) {
findings.push(
finding(
'QL3_CLUSTER_ADMIN_DOCKERFILE_BASE_IMAGE_NOT_PINNED',
'Cluster admin build and runtime stages must use the exact immutable Node base digest',
'Cluster admin build and runtime stages must use their exact immutable Node base digests',
),
);
}
+23 -8
View File
@@ -6,8 +6,10 @@ const { readReleaseIdentity } = require('./lib/ql3-release-identity.cjs');
const IMAGE_DIRECTORY = 'deploy/containers/ql3-local-application';
const QL3_VERSION = readReleaseIdentity(path.resolve(__dirname, '..')).version;
const NODE_IMAGE =
const BUILD_NODE_IMAGE =
'node:24.18.0-bookworm-slim@sha256:6f7b03f7c2c8e2e784dcf9295400527b9b1270fd37b7e9a7285cf83b6951452d';
const RUNTIME_NODE_IMAGE =
'node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436';
const BUILD_DEPENDENCIES = Object.freeze({
croner: '7.0.8',
'drizzle-orm': '1.0.0-rc.4',
@@ -169,14 +171,26 @@ function counts(values) {
}
function auditDockerfile(contents, findings) {
const exactBasePattern = new RegExp(
`^FROM ${NODE_IMAGE.replace(
/[.*+?^${}()|[\]\\]/g,
'\\$&',
)} AS (?:dependency-manifest|runtime)$`,
const escapedBuildNodeImage = BUILD_NODE_IMAGE.replace(
/[.*+?^${}()|[\]\\]/g,
'\\$&',
);
const escapedRuntimeNodeImage = RUNTIME_NODE_IMAGE.replace(
/[.*+?^${}()|[\]\\]/g,
'\\$&',
);
const exactBuildBasePattern = new RegExp(
`^FROM ${escapedBuildNodeImage} AS dependency-manifest$`,
'gm',
);
if ([...contents.matchAll(exactBasePattern)].length !== 2) {
const exactRuntimeBasePattern = new RegExp(
`^FROM ${escapedRuntimeNodeImage} AS runtime$`,
'gm',
);
if (
[...contents.matchAll(exactBuildBasePattern)].length !== 1 ||
[...contents.matchAll(exactRuntimeBasePattern)].length !== 1
) {
addFinding(findings, 'BASE_IMAGE_NOT_EXACTLY_PINNED');
}
if (/(?:^|\n)\s*ARG\s+NODE_IMAGE\b/.test(contents)) {
@@ -323,7 +337,8 @@ function auditLocalImageContract(root) {
return Object.freeze({
schemaVersion: 1,
image: 'local-application',
nodeImage: NODE_IMAGE,
nodeImage: RUNTIME_NODE_IMAGE,
buildNodeImage: BUILD_NODE_IMAGE,
runtimePackages: Object.freeze(
[
...RUNTIME_PACKAGES.map((name) => `@qinglong/${name.slice(4)}`),
+1
View File
@@ -78,6 +78,7 @@ const dockerfile = fs.readFileSync(
);
for (const required of [
'node:24.18.0-bookworm-slim@sha256:',
'node:24.18.0-alpine3.23@sha256:595398b0081eacda8e1c4c5b97b76cd1020e4d58a8ebcb4843b9bca1e79e7436',
'npm ci --omit=dev --ignore-scripts',
'packages/ql3-runtime-core',
'packages/ql3-local-process',