fix: optimize web sentinel publish build

This commit is contained in:
Codex
2026-06-30 05:29:59 +00:00
parent 800d92f4ff
commit 6fbdf9b5a5
7 changed files with 110 additions and 11 deletions
@@ -52,6 +52,11 @@ sentinel:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
@@ -52,6 +52,11 @@ sentinel:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
@@ -52,6 +52,11 @@ sentinel:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
@@ -53,6 +53,11 @@ sentinel:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
@@ -52,6 +52,11 @@ sentinel:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
@@ -63,6 +63,11 @@ baselines:
envReuse:
mode: docker-layer-and-ci-node-deps
nodeDepsPath: /opt/hwlab-ci-node-deps/node_modules
dockerBuild:
networkMode: host
proxySource: node.networkProfile.dockerBuildProxy
contextIgnore: generated
verifyPhase: pre-docker-build
gitMirror:
source: source.gitMirrorReadUrl
preSync: required
+80 -11
View File
@@ -390,11 +390,18 @@ function sentinelDockerfile(baseImage: string, entrypoint: string, envReuseNodeD
const nodeDepsPath = shellQuote(envReuseNodeDepsPath);
return [
`FROM ${baseImage}`,
"ARG HTTP_PROXY",
"ARG HTTPS_PROXY",
"ARG ALL_PROXY",
"ARG NO_PROXY",
"ARG http_proxy",
"ARG https_proxy",
"ARG all_proxy",
"ARG no_proxy",
"WORKDIR /app",
"COPY . /app",
`RUN if [ -d ${nodeDepsPath} ]; then mkdir -p /app/node_modules; for dep in ${nodeDepsPath}/*; do ln -sf "$dep" "/app/node_modules/$(basename "$dep")"; done; fi`,
"RUN printf '%s\\n' '#!/bin/sh' 'exec bun /app/scripts/ssh-cli.ts \"$@\"' > /usr/local/bin/trans && chmod 0755 /usr/local/bin/trans",
"RUN bun scripts/verify-web-probe-sentinel-monitor-web.ts",
"ENV NODE_ENV=production",
`ENTRYPOINT ["bun", "${entrypoint}"]`,
"",
@@ -411,11 +418,21 @@ function monitorWebCicdPlan(cicd: Record<string, unknown>): Record<string, unkno
sourceMode: stringAt(cicd, "builder.sourceMode"),
envReuseMode: stringAtNullable(cicd, "monitorWeb.envReuse.mode") ?? "docker-layer-and-ci-node-deps",
envReuseNodeDepsPath: stringAtNullable(cicd, "monitorWeb.envReuse.nodeDepsPath") ?? "/opt/hwlab-ci-node-deps/node_modules",
verifyPhase: stringAtNullable(cicd, "monitorWeb.dockerBuild.verifyPhase") ?? "pre-docker-build",
dockerBuildNetworkMode: monitorWebDockerBuildNetworkMode(cicd),
dockerBuildProxySource: stringAtNullable(cicd, "monitorWeb.dockerBuild.proxySource") ?? "node.networkProfile.dockerBuildProxy",
dockerBuildContextIgnore: stringAtNullable(cicd, "monitorWeb.dockerBuild.contextIgnore") ?? "generated",
ciBudgetSeconds: numberAtNullable(cicd, "monitorWeb.ciBudget.maxSeconds") ?? numberAt(cicd, "confirmWait.maxSeconds"),
valuesRedacted: true,
};
}
function monitorWebDockerBuildNetworkMode(cicd: Record<string, unknown>): "default" | "host" {
const value = stringAtNullable(cicd, "monitorWeb.dockerBuild.networkMode") ?? "host";
if (value !== "default" && value !== "host") throw new Error(`monitorWeb.dockerBuild.networkMode must be default or host, got ${value}`);
return value;
}
function renderSentinelManifests(
spec: HwlabRuntimeLaneSpec,
sentinelId: string,
@@ -1666,6 +1683,10 @@ function sentinelPublishShell(state: SentinelCicdState, jobName: string, publish
const dockerfileB64 = Buffer.from(state.image.dockerfilePreview, "utf8").toString("base64");
const envReuseMode = stringAt(monitorWeb, "envReuseMode");
const envReuseNodeDepsPath = stringAt(monitorWeb, "envReuseNodeDepsPath");
const dockerBuildNetworkMode = stringAt(monitorWeb, "dockerBuildNetworkMode");
const dockerBuildProxySource = stringAt(monitorWeb, "dockerBuildProxySource");
const dockerBuildProxy = state.spec.networkProfile.dockerBuildProxy;
const dockerBuildNoProxy = dockerBuildProxy.noProxy.join(",");
return [
"set -eu",
`job_name=${shellQuote(jobName)}`,
@@ -1682,6 +1703,12 @@ function sentinelPublishShell(state: SentinelCicdState, jobName: string, publish
`files_b64=${shellQuote(filesB64)}`,
`env_reuse_mode=${shellQuote(envReuseMode)}`,
`env_reuse_node_deps_path=${shellQuote(envReuseNodeDepsPath)}`,
`docker_build_network_mode=${shellQuote(dockerBuildNetworkMode)}`,
`docker_build_proxy_source=${shellQuote(dockerBuildProxySource)}`,
`docker_build_http_proxy=${shellQuote(dockerBuildProxy.http)}`,
`docker_build_https_proxy=${shellQuote(dockerBuildProxy.https)}`,
`docker_build_all_proxy=${shellQuote(dockerBuildProxy.all)}`,
`docker_build_no_proxy=${shellQuote(dockerBuildNoProxy)}`,
"started_ms=$(node -e 'console.log(Date.now())')",
"emit_stage() { stage=$1; status=$2; started=$3; finished=$(node -e 'console.log(Date.now())'); node - \"$stage\" \"$status\" \"$started\" \"$finished\" <<'NODE'\nconst [stage, status, started, finished] = process.argv.slice(2); console.log(JSON.stringify({ event:'sentinel-publish-stage', stage, status, elapsedMs:Number(finished)-Number(started), valuesRedacted:true }));\nNODE\n}",
"emit_failed() { code=$?; if [ \"$code\" -ne 0 ]; then node - \"$code\" \"$job_name\" <<'NODE'\nconst [code, jobName] = process.argv.slice(2); console.log(JSON.stringify({ ok:false, status:'failed', exitCode:Number(code), jobName, valuesRedacted:true }));\nNODE\nfi; exit \"$code\"; }",
@@ -1716,16 +1743,51 @@ function sentinelPublishShell(state: SentinelCicdState, jobName: string, publish
"env_reuse_node_deps_present=false",
"env_reuse_node_deps_entries=0",
"if [ -d \"$env_reuse_node_deps_path\" ]; then env_reuse_node_deps_present=true; env_reuse_node_deps_entries=$(find \"$env_reuse_node_deps_path\" -mindepth 1 -maxdepth 1 2>/dev/null | wc -l | tr -d ' '); fi",
"node - \"$env_reuse_mode\" \"$env_reuse_node_deps_path\" \"$env_reuse_node_deps_present\" \"$env_reuse_node_deps_entries\" <<'NODE'",
"const [mode, nodeDepsPath, nodeDepsPresent, nodeDepsEntries] = process.argv.slice(2); console.log(JSON.stringify({ event:'sentinel-publish-env-reuse', mode, nodeDepsPath, nodeDepsPresent: nodeDepsPresent === 'true', nodeDepsEntries: Number(nodeDepsEntries || 0), dependencyReuse: nodeDepsPresent === 'true' ? 'hit' : 'miss', valuesRedacted:true }));",
"env_reuse_linked_node_deps=0",
"rm -rf node_modules",
"if [ \"$env_reuse_node_deps_present\" = true ]; then mkdir -p node_modules; for dep in \"$env_reuse_node_deps_path\"/*; do [ -e \"$dep\" ] || continue; ln -sf \"$dep\" \"node_modules/$(basename \"$dep\")\"; env_reuse_linked_node_deps=$((env_reuse_linked_node_deps + 1)); done; fi",
"node - \"$env_reuse_mode\" \"$env_reuse_node_deps_path\" \"$env_reuse_node_deps_present\" \"$env_reuse_node_deps_entries\" \"$env_reuse_linked_node_deps\" <<'NODE'",
"const [mode, nodeDepsPath, nodeDepsPresent, nodeDepsEntries, linkedNodeDeps] = process.argv.slice(2); console.log(JSON.stringify({ event:'sentinel-publish-env-reuse', mode, nodeDepsPath, nodeDepsPresent: nodeDepsPresent === 'true', nodeDepsEntries: Number(nodeDepsEntries || 0), linkedNodeDeps: Number(linkedNodeDeps || 0), dependencyReuse: nodeDepsPresent === 'true' ? 'hit' : 'miss', valuesRedacted:true }));",
"NODE",
"monitor_web_verify_started_ms=$(node -e 'console.log(Date.now())')",
"emit_stage monitor-web-verify running \"$monitor_web_verify_started_ms\"",
"if ! bun scripts/verify-web-probe-sentinel-monitor-web.ts > /tmp/web-probe-sentinel-monitor-web-verify.log 2>&1; then cat /tmp/web-probe-sentinel-monitor-web-verify.log; emit_stage monitor-web-verify failed \"$monitor_web_verify_started_ms\"; exit 1; fi",
"cat /tmp/web-probe-sentinel-monitor-web-verify.log",
"monitor_web_verify_finished_ms=$(node -e 'console.log(Date.now())')",
"emit_stage monitor-web-verify succeeded \"$monitor_web_verify_started_ms\"",
"DOCKERFILE_B64=\"$dockerfile_b64\" node <<'NODE'",
"const fs = require('node:fs');",
"fs.writeFileSync('Dockerfile.web-probe-sentinel', Buffer.from(process.env.DOCKERFILE_B64 || '', 'base64'));",
"NODE",
"cat > .dockerignore <<'EOF_DOCKERIGNORE'",
".git",
".git/**",
".state",
".state/**",
"logs",
"logs/**",
"node_modules",
"node_modules/**",
"**/node_modules",
"**/node_modules/**",
"**/dist",
"**/dist/**",
"**/target",
"**/target/**",
"**/coverage",
"**/coverage/**",
"npm-debug.log*",
".env",
".env.*",
"EOF_DOCKERIGNORE",
"docker_ignore_entries=$(wc -l < .dockerignore | tr -d ' ')",
"docker_build_http_proxy_present=false; if [ -n \"$docker_build_http_proxy\" ]; then docker_build_http_proxy_present=true; fi",
"docker_build_https_proxy_present=false; if [ -n \"$docker_build_https_proxy\" ]; then docker_build_https_proxy_present=true; fi",
"docker_build_all_proxy_present=false; if [ -n \"$docker_build_all_proxy\" ]; then docker_build_all_proxy_present=true; fi",
"docker_build_no_proxy_present=false; if [ -n \"$docker_build_no_proxy\" ]; then docker_build_no_proxy_present=true; fi",
"docker_build_started_ms=$(node -e 'console.log(Date.now())')",
"emit_stage docker-build running \"$docker_build_started_ms\"",
"if ! docker build -f Dockerfile.web-probe-sentinel -t \"$image_ref\" . > /tmp/web-probe-sentinel-docker-build.log 2>&1; then cat /tmp/web-probe-sentinel-docker-build.log; emit_stage docker-build failed \"$docker_build_started_ms\"; exit 1; fi",
"if ! env HTTP_PROXY=\"$docker_build_http_proxy\" HTTPS_PROXY=\"$docker_build_https_proxy\" ALL_PROXY=\"$docker_build_all_proxy\" NO_PROXY=\"$docker_build_no_proxy\" http_proxy=\"$docker_build_http_proxy\" https_proxy=\"$docker_build_https_proxy\" all_proxy=\"$docker_build_all_proxy\" no_proxy=\"$docker_build_no_proxy\" docker build --network \"$docker_build_network_mode\" --build-arg HTTP_PROXY=\"$docker_build_http_proxy\" --build-arg HTTPS_PROXY=\"$docker_build_https_proxy\" --build-arg ALL_PROXY=\"$docker_build_all_proxy\" --build-arg NO_PROXY=\"$docker_build_no_proxy\" --build-arg http_proxy=\"$docker_build_http_proxy\" --build-arg https_proxy=\"$docker_build_https_proxy\" --build-arg all_proxy=\"$docker_build_all_proxy\" --build-arg no_proxy=\"$docker_build_no_proxy\" -f Dockerfile.web-probe-sentinel -t \"$image_ref\" . > /tmp/web-probe-sentinel-docker-build.log 2>&1; then cat /tmp/web-probe-sentinel-docker-build.log; emit_stage docker-build failed \"$docker_build_started_ms\"; exit 1; fi",
"cat /tmp/web-probe-sentinel-docker-build.log",
"docker_build_finished_ms=$(node -e 'console.log(Date.now())')",
"emit_stage docker-build succeeded \"$docker_build_started_ms\"",
@@ -1781,11 +1843,11 @@ function sentinelPublishShell(state: SentinelCicdState, jobName: string, publish
"fi",
"gitops_finished_ms=$(node -e 'console.log(Date.now())')",
"finished_ms=$(node -e 'console.log(Date.now())')",
"node - \"$job_name\" \"$source_commit\" \"$mirror_commit\" \"$image_ref\" \"$digest_ref\" \"$gitops_commit\" \"$changed\" \"$file_count\" \"$started_ms\" \"$finished_ms\" \"$source_fetch_started_ms\" \"$source_fetch_finished_ms\" \"$docker_build_started_ms\" \"$docker_build_finished_ms\" \"$docker_push_started_ms\" \"$docker_push_finished_ms\" \"$gitops_started_ms\" \"$gitops_finished_ms\" \"$env_reuse_mode\" \"$env_reuse_node_deps_path\" \"$env_reuse_node_deps_present\" \"$env_reuse_node_deps_entries\" \"$docker_build_cache_hits\" \"$docker_build_step_lines\" \"$docker_build_log_tail_b64\" <<'NODE'",
"const [jobName, sourceCommit, mirrorCommit, imageRef, digestRef, gitopsCommit, changed, fileCount, startedMs, finishedMs, sourceFetchStartedMs, sourceFetchFinishedMs, dockerBuildStartedMs, dockerBuildFinishedMs, dockerPushStartedMs, dockerPushFinishedMs, gitopsStartedMs, gitopsFinishedMs, envReuseMode, envReuseNodeDepsPath, envReuseNodeDepsPresent, envReuseNodeDepsEntries, dockerBuildCacheHits, dockerBuildStepLines, dockerBuildLogTailB64] = process.argv.slice(2);",
"node - \"$job_name\" \"$source_commit\" \"$mirror_commit\" \"$image_ref\" \"$digest_ref\" \"$gitops_commit\" \"$changed\" \"$file_count\" \"$started_ms\" \"$finished_ms\" \"$source_fetch_started_ms\" \"$source_fetch_finished_ms\" \"$monitor_web_verify_started_ms\" \"$monitor_web_verify_finished_ms\" \"$docker_build_started_ms\" \"$docker_build_finished_ms\" \"$docker_push_started_ms\" \"$docker_push_finished_ms\" \"$gitops_started_ms\" \"$gitops_finished_ms\" \"$env_reuse_mode\" \"$env_reuse_node_deps_path\" \"$env_reuse_node_deps_present\" \"$env_reuse_node_deps_entries\" \"$env_reuse_linked_node_deps\" \"$docker_build_cache_hits\" \"$docker_build_step_lines\" \"$docker_build_log_tail_b64\" \"$docker_build_network_mode\" \"$docker_build_proxy_source\" \"$docker_build_http_proxy_present\" \"$docker_build_https_proxy_present\" \"$docker_build_all_proxy_present\" \"$docker_build_no_proxy_present\" \"$docker_ignore_entries\" <<'NODE'",
"const [jobName, sourceCommit, mirrorCommit, imageRef, digestRef, gitopsCommit, changed, fileCount, startedMs, finishedMs, sourceFetchStartedMs, sourceFetchFinishedMs, monitorWebVerifyStartedMs, monitorWebVerifyFinishedMs, dockerBuildStartedMs, dockerBuildFinishedMs, dockerPushStartedMs, dockerPushFinishedMs, gitopsStartedMs, gitopsFinishedMs, envReuseMode, envReuseNodeDepsPath, envReuseNodeDepsPresent, envReuseNodeDepsEntries, envReuseLinkedNodeDeps, dockerBuildCacheHits, dockerBuildStepLines, dockerBuildLogTailB64, dockerBuildNetworkMode, dockerBuildProxySource, dockerBuildHttpProxyPresent, dockerBuildHttpsProxyPresent, dockerBuildAllProxyPresent, dockerBuildNoProxyPresent, dockerIgnoreEntries] = process.argv.slice(2);",
"const elapsed = (start, finish) => Number(finish) - Number(start);",
"const cacheHits = Number(dockerBuildCacheHits || 0);",
"console.log(JSON.stringify({ ok:true, status:'succeeded', jobName, sourceCommit, mirrorCommit, imageRef, digestRef, gitopsCommit: gitopsCommit || null, changed: changed === 'true', fileCount: Number(fileCount || 0), elapsedMs: elapsed(startedMs, finishedMs), stageTimings: { sourceFetchMs: elapsed(sourceFetchStartedMs, sourceFetchFinishedMs), dockerBuildMs: elapsed(dockerBuildStartedMs, dockerBuildFinishedMs), dockerPushMs: elapsed(dockerPushStartedMs, dockerPushFinishedMs), gitopsMs: elapsed(gitopsStartedMs, gitopsFinishedMs), totalMs: elapsed(startedMs, finishedMs), valuesRedacted:true }, envReuse: { mode: envReuseMode, nodeDepsPath: envReuseNodeDepsPath, nodeDepsPresent: envReuseNodeDepsPresent === 'true', nodeDepsEntries: Number(envReuseNodeDepsEntries || 0), dependencyReuse: envReuseNodeDepsPresent === 'true' ? 'hit' : 'miss', valuesRedacted:true }, dockerBuild: { cacheHitLines: cacheHits, stepLines: Number(dockerBuildStepLines || 0), layerCache: cacheHits > 0 ? 'hit' : 'unknown-or-miss', logTail: Buffer.from(dockerBuildLogTailB64 || '', 'base64').toString('utf8'), valuesRedacted:true }, completedStages: ['source-fetch', 'docker-build', 'docker-push', gitopsCommit ? 'gitops' : 'gitops-skipped'], valuesRedacted:true }));",
"console.log(JSON.stringify({ ok:true, status:'succeeded', jobName, sourceCommit, mirrorCommit, imageRef, digestRef, gitopsCommit: gitopsCommit || null, changed: changed === 'true', fileCount: Number(fileCount || 0), elapsedMs: elapsed(startedMs, finishedMs), stageTimings: { sourceFetchMs: elapsed(sourceFetchStartedMs, sourceFetchFinishedMs), monitorWebVerifyMs: elapsed(monitorWebVerifyStartedMs, monitorWebVerifyFinishedMs), dockerBuildMs: elapsed(dockerBuildStartedMs, dockerBuildFinishedMs), dockerPushMs: elapsed(dockerPushStartedMs, dockerPushFinishedMs), gitopsMs: elapsed(gitopsStartedMs, gitopsFinishedMs), totalMs: elapsed(startedMs, finishedMs), valuesRedacted:true }, envReuse: { mode: envReuseMode, nodeDepsPath: envReuseNodeDepsPath, nodeDepsPresent: envReuseNodeDepsPresent === 'true', nodeDepsEntries: Number(envReuseNodeDepsEntries || 0), linkedNodeDeps: Number(envReuseLinkedNodeDeps || 0), dependencyReuse: envReuseNodeDepsPresent === 'true' ? 'hit' : 'miss', valuesRedacted:true }, dockerBuild: { cacheHitLines: cacheHits, stepLines: Number(dockerBuildStepLines || 0), layerCache: cacheHits > 0 ? 'hit' : 'unknown-or-miss', networkMode: dockerBuildNetworkMode, proxySource: dockerBuildProxySource, proxy: { httpProxyPresent: dockerBuildHttpProxyPresent === 'true', httpsProxyPresent: dockerBuildHttpsProxyPresent === 'true', allProxyPresent: dockerBuildAllProxyPresent === 'true', noProxyPresent: dockerBuildNoProxyPresent === 'true', valuesRedacted:true }, dockerIgnoreEntries: Number(dockerIgnoreEntries || 0), verifyLocation: 'pre-docker-build', logTail: Buffer.from(dockerBuildLogTailB64 || '', 'base64').toString('utf8'), valuesRedacted:true }, completedStages: ['source-fetch', 'monitor-web-verify', 'docker-build', 'docker-push', gitopsCommit ? 'gitops' : 'gitops-skipped'], valuesRedacted:true }));",
"NODE",
"trap - EXIT",
].join("\n");
@@ -1933,7 +1995,7 @@ function sentinelCurrentRemotePhase(result: SentinelRemoteJobResult, events: rea
const running = reversed.find((event) => event.status === "running");
if (running !== undefined) return text(running.stage);
const completed = new Set(events.filter((event) => event.status === "succeeded" || event.status === "skipped").map((event) => text(event.stage)));
const order = domain === "publish" ? ["source-fetch", "docker-build", "docker-push", "gitops"] : ["source-mirror-fetch"];
const order = domain === "publish" ? ["source-fetch", "monitor-web-verify", "docker-build", "docker-push", "gitops"] : ["source-mirror-fetch"];
const next = order.find((stage) => !completed.has(stage));
return next ?? result.phase;
}
@@ -2580,8 +2642,10 @@ function renderPublishResult(publish: Record<string, unknown>): string {
const diagnosticEnvReuse = record(diagnostics.envReuse);
const envReuse = Object.keys(record(payload.envReuse)).length > 0 ? record(payload.envReuse) : diagnosticEnvReuse;
const dockerBuild = record(payload.dockerBuild);
const dockerBuildProxy = record(dockerBuild.proxy);
const timings = record(payload.stageTimings);
const commands = record(diagnostics.commands);
const proxySummary = [dockerBuildProxy.httpProxyPresent, dockerBuildProxy.httpsProxyPresent, dockerBuildProxy.allProxyPresent].some((item) => item === true) ? "present" : "none";
const lines = [
"PUBLISH",
table(["OK", "PHASE", "JOB", "ELAPSED", "POD", "CURRENT", "DIGEST", "GITOPS"], [[
@@ -2599,11 +2663,12 @@ function renderPublishResult(publish: Record<string, unknown>): string {
lines.push(
"",
"PUBLISH_ENV_REUSE",
table(["MODE", "NODE_DEPS", "PRESENT", "ENTRIES", "DEPENDENCY"], [[
table(["MODE", "NODE_DEPS", "PRESENT", "ENTRIES", "LINKED", "DEPENDENCY"], [[
envReuse.mode,
envReuse.nodeDepsPath,
envReuse.nodeDepsPresent,
envReuse.nodeDepsEntries,
envReuse.linkedNodeDeps ?? "-",
envReuse.dependencyReuse,
]]),
);
@@ -2612,11 +2677,15 @@ function renderPublishResult(publish: Record<string, unknown>): string {
lines.push(
"",
"PUBLISH_BUILD",
table(["CACHE", "CACHE_LINES", "STEP_LINES", "SOURCE_MS", "BUILD_MS", "PUSH_MS", "GITOPS_MS", "TOTAL_MS"], [[
table(["NETWORK", "PROXY", "IGNORE", "CACHE", "CACHE_LINES", "STEP_LINES", "SOURCE_MS", "VERIFY_MS", "BUILD_MS", "PUSH_MS", "GITOPS_MS", "TOTAL_MS"], [[
dockerBuild.networkMode ?? "-",
proxySummary,
dockerBuild.dockerIgnoreEntries ?? "-",
dockerBuild.layerCache ?? "-",
dockerBuild.cacheHitLines ?? "-",
dockerBuild.stepLines ?? "-",
timings.sourceFetchMs ?? "-",
timings.monitorWebVerifyMs ?? "-",
timings.dockerBuildMs ?? "-",
timings.dockerPushMs ?? "-",
timings.gitopsMs ?? "-",
@@ -2673,7 +2742,7 @@ function renderImageResult(result: Record<string, unknown>): string {
"",
table(["IMAGE", "BASE", "ENTRYPOINT", "DOCKERFILE"], [[image.ref, image.baseImage, image.entrypoint, short(image.dockerfileSha256)]]),
"",
Object.keys(monitorWeb).length === 0 ? "MONITOR_WEB\n-" : table(["STACK", "MODE", "ASSETS", "VERIFY", "ENV_REUSE"], [[monitorWeb.stack, monitorWeb.runtimeMode, monitorWeb.assetRoot, monitorWeb.verifyCommand, `${monitorWeb.envReuseMode}:${monitorWeb.envReuseNodeDepsPath}`]]),
Object.keys(monitorWeb).length === 0 ? "MONITOR_WEB\n-" : table(["STACK", "MODE", "ASSETS", "VERIFY", "ENV_REUSE", "BUILD_NET", "CTX_IGNORE"], [[monitorWeb.stack, monitorWeb.runtimeMode, monitorWeb.assetRoot, monitorWeb.verifyCommand, `${monitorWeb.envReuseMode}:${monitorWeb.envReuseNodeDepsPath}`, monitorWeb.dockerBuildNetworkMode, monitorWeb.dockerBuildContextIgnore]]),
"",
Object.keys(registry).length === 0 ? "REGISTRY\n-" : table(["PROBED", "PRESENT", "DIGEST"], [[record(registry.probe).url ?? "-", record(registry.probe).present ?? "-", short(record(registry.probe).digest)]]),
"",