fix: preserve hwlab refresh render evidence

This commit is contained in:
Codex
2026-07-04 03:42:47 +00:00
parent 779274b26f
commit e8bf83821a
7 changed files with 391 additions and 7 deletions
@@ -26,8 +26,8 @@ try {
prepareYamlDependency();
applyDeployOverlay();
renderControlPlane();
await applyPipeline();
emit({ ok: true, status: "applied" });
const evidence = await applyPipeline();
emit({ ok: true, status: "applied", ...evidence });
} finally {
rmSync(workDir, { recursive: true, force: true });
}
@@ -143,16 +143,21 @@ async function applyPipeline() {
if (typeof renderedPipelineName !== "string" || renderedPipelineName.length === 0) {
throw new Error(`rendered Pipeline metadata.name missing: ${pipelinePath}`);
}
const render = summarizeRenderedPipeline(pipeline, renderedPipelineName);
const pipelineName = requiredOverlayString("pipelineName");
pipeline.metadata = pipeline.metadata && typeof pipeline.metadata === "object" ? pipeline.metadata : {};
pipeline.metadata.name = pipelineName;
const pipelineText = YAML.stringify(pipeline);
await kubeRequest(
const applyText = await kubeRequest(
"PATCH",
`/apis/tekton.dev/v1/namespaces/${encodeURIComponent(tektonNamespace)}/pipelines/${encodeURIComponent(pipelineName)}?fieldManager=${encodeURIComponent(fieldManager)}&force=true`,
pipelineText,
"application/apply-patch+yaml",
);
return {
render,
apply: summarizeAppliedPipeline(parseJsonObject(applyText), pipelineName, tektonNamespace),
};
}
function yamlModule() {
@@ -281,6 +286,102 @@ function requiredNonNegativeNumber(name) {
return Math.floor(value);
}
function summarizeRenderedPipeline(pipeline, pipelineName) {
const tasks = Array.isArray(pipeline?.spec?.tasks) ? pipeline.spec.tasks : [];
const runtimeReady = tasks.find((task) => recordOrNull(task)?.name === "runtime-ready");
return {
pipelineName,
taskCount: tasks.length,
runtimeReadyTask: summarizeRuntimeReadyTask(runtimeReady),
};
}
function summarizeRuntimeReadyTask(value) {
const task = recordOrNull(value);
if (task === null) return { present: false, name: null, runAfter: [], when: [] };
return {
present: true,
name: stringOrNull(task.name),
runAfter: compactStringArray(task.runAfter, 4),
when: compactWhenList(task.when, 4),
};
}
function summarizeAppliedPipeline(value, pipelineName, namespace) {
const metadata = recordOrNull(value?.metadata);
return {
pipelineName: stringOrNull(metadata?.name) || pipelineName,
namespace: stringOrNull(metadata?.namespace) || namespace,
resourceVersion: stringOrNull(metadata?.resourceVersion),
annotations: compactMetadataMap(metadata?.annotations, [
"sourceConfig",
"ciContract",
"policy",
"hwlab.pikastech.local/source-commit",
"tekton.dev/pipelines.minVersion",
], 6),
labels: compactMetadataMap(metadata?.labels, [
"hwlab.pikastech.local/source-commit",
"app.kubernetes.io/name",
"app.kubernetes.io/part-of",
"app.kubernetes.io/component",
], 6),
degradedReason: metadata === null ? "apply-response-metadata-missing" : null,
};
}
function compactMetadataMap(value, preferredKeys, limit) {
const record = recordOrNull(value);
if (record === null) return null;
const output = {};
for (const key of preferredKeys) {
const item = stringOrNull(record[key]);
if (item === null || output[key] !== undefined) continue;
output[key] = item;
if (Object.keys(output).length >= limit) return output;
}
for (const key of Object.keys(record).sort()) {
const item = stringOrNull(record[key]);
if (item === null || output[key] !== undefined) continue;
output[key] = item;
if (Object.keys(output).length >= limit) break;
}
return Object.keys(output).length === 0 ? null : output;
}
function compactStringArray(value, limit) {
return Array.isArray(value)
? value.map((item) => stringOrNull(item)).filter(Boolean).slice(0, limit)
: [];
}
function compactWhenList(value, limit) {
return Array.isArray(value)
? value.map((item) => recordOrNull(item)).filter(Boolean).slice(0, limit).map((item) => ({
input: stringOrNull(item.input),
operator: stringOrNull(item.operator),
values: compactStringArray(item.values, 4),
}))
: [];
}
function parseJsonObject(text) {
try {
const parsed = JSON.parse(text);
return recordOrNull(parsed);
} catch {
return null;
}
}
function recordOrNull(value) {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : null;
}
function stringOrNull(value) {
return typeof value === "string" && value.length > 0 ? value : null;
}
function emit(extra) {
process.stdout.write(`${JSON.stringify({
...extra,
+62 -1
View File
@@ -292,16 +292,69 @@ function compactRefreshEvidence(refresh) {
jobName: stringOrNull(value.jobName) ?? stringOrNull(summary.jobName),
namespace: stringOrNull(value.namespace) ?? stringOrNull(summary.namespace),
status: stringOrNull(summary.status),
pipeline: stringOrNull(summary.pipeline),
pipeline: stringOrNull(summary.pipeline) || stringOrNull(recordOrNull(summary.apply)?.pipelineName) || stringOrNull(recordOrNull(summary.render)?.pipelineName),
sourceCommit: stringOrNull(summary.sourceCommit),
sourceStageRef: stringOrNull(summary.sourceStageRef),
elapsedMs: numberOrNull(summary.elapsedMs),
render: compactRefreshRender(recordOrNull(summary.render)),
apply: compactRefreshApply(recordOrNull(summary.apply)),
sourceAuthority: stringOrNull(summary.sourceAuthority),
statusAuthority: stringOrNull(summary.statusAuthority),
parsedDownstreamCliOutput: false,
};
}
function compactRefreshRender(value) {
if (value === null) return null;
return {
pipelineName: stringOrNull(value.pipelineName),
taskCount: numberOrNull(value.taskCount),
runtimeReadyTask: compactRefreshRuntimeReady(recordOrNull(value.runtimeReadyTask)),
};
}
function compactRefreshApply(value) {
if (value === null) return null;
return {
pipelineName: stringOrNull(value.pipelineName),
namespace: stringOrNull(value.namespace),
resourceVersion: stringOrNull(value.resourceVersion),
annotations: compactStringMap(recordOrNull(value.annotations)),
labels: compactStringMap(recordOrNull(value.labels)),
degradedReason: stringOrNull(value.degradedReason),
};
}
function compactRefreshRuntimeReady(value) {
if (value === null) return null;
return {
present: booleanOrNull(value.present),
name: stringOrNull(value.name),
runAfter: compactStringArray(value.runAfter, 4),
when: compactWhenList(value.when, 4),
};
}
function compactWhenList(value, limit) {
return Array.isArray(value)
? value.map((item) => recordOrNull(item)).filter(Boolean).slice(0, limit).map((item) => ({
input: stringOrNull(item.input),
operator: stringOrNull(item.operator),
values: compactStringArray(item.values, 4),
}))
: [];
}
function compactStringMap(value) {
if (value === null) return null;
const output = {};
for (const [key, item] of Object.entries(value).slice(0, 8)) {
const text = stringOrNull(item);
if (text !== null) output[key] = text;
}
return Object.keys(output).length === 0 ? null : output;
}
function compactArgo(argo) {
const value = recordOrNull(argo);
if (value === null) return null;
@@ -492,6 +545,14 @@ function numberOrNull(value) {
return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function booleanOrNull(value) {
return value === true ? true : value === false ? false : null;
}
function compactStringArray(value, limit) {
return Array.isArray(value) ? value.map((item) => stringOrNull(item)).filter(Boolean).slice(0, limit) : [];
}
const result = await readConfigMap();
const errors = [];
const stateByFollower = {};