fix: preserve hwlab refresh render evidence
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
Reference in New Issue
Block a user