Files
pikasTech-unidesk/scripts/native/cicd/plan-artifacts.mjs
T
2026-07-03 11:00:11 +00:00

97 lines
3.9 KiB
JavaScript

import { readFileSync } from "node:fs";
import https from "node:https";
const namespace = process.argv[2] || "";
const pipelineRun = process.argv[3] || "";
const host = process.env.KUBERNETES_SERVICE_HOST;
const port = Number(process.env.KUBERNETES_SERVICE_PORT || "443");
const token = readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/token", "utf8").trim();
const ca = readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
function request(path) {
return new Promise((resolve) => {
const req = https.request({ host, port, path, method: "GET", ca, headers: { authorization: `Bearer ${token}` } }, (res) => {
let body = "";
res.setEncoding("utf8");
res.on("data", (chunk) => { body += chunk; });
res.on("end", () => resolve({ status: res.statusCode || 0, body }));
});
req.on("error", (error) => resolve({ status: 599, body: error?.message || String(error) }));
req.end();
});
}
function parse(text) {
try {
return text ? JSON.parse(text) : null;
} catch {
return null;
}
}
function strings(value) {
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
}
const out = {
ok: false,
pipelineRun,
pods: [],
eventFound: false,
degradedReason: "plan-artifacts-log-query-skipped",
statusAuthority: "kubernetes-api-serviceaccount",
};
if (!namespace || !pipelineRun) {
process.stdout.write(JSON.stringify(out));
process.exit(0);
}
const selector = encodeURIComponent(`tekton.dev/pipelineRun=${pipelineRun}`);
const podsResult = await request(`/api/v1/namespaces/${encodeURIComponent(namespace)}/pods?labelSelector=${selector}`);
const pods = parse(podsResult.body);
const items = Array.isArray(pods?.items) ? pods.items : [];
const planPods = items.filter((pod) => {
const labels = pod?.metadata?.labels || {};
const name = pod?.metadata?.name || "";
return labels["tekton.dev/pipelineTask"] === "plan-artifacts" || labels["tekton.dev/task"] === "plan-artifacts" || /plan-artifacts/u.test(name);
});
out.pods = planPods.map((pod) => pod?.metadata?.name).filter(Boolean);
const events = [];
for (const podName of out.pods.slice(-4)) {
const log = await request(`/api/v1/namespaces/${encodeURIComponent(namespace)}/pods/${encodeURIComponent(podName)}/log?tailLines=240`);
if (log.status < 200 || log.status >= 300) continue;
for (const raw of log.body.split(/\r?\n/u)) {
const line = raw.trim();
if (!line.startsWith("{")) continue;
const event = parse(line);
if (event?.event === "g14-ci-plan") events.push(event);
}
}
const latest = events.at(-1) || null;
if (latest) {
const audit = latest.artifactProvenanceAudit && typeof latest.artifactProvenanceAudit === "object" ? latest.artifactProvenanceAudit : null;
const unsafeReuseServices = strings(audit?.unsafeReuseServices);
const provenanceRebuildServices = strings(audit?.provenanceRebuildServices);
Object.assign(out, {
ok: true,
eventFound: true,
degradedReason: null,
sourceCommitId: typeof latest.sourceCommitId === "string" ? latest.sourceCommitId : null,
affectedServices: strings(latest.affectedServices),
rolloutServices: strings(latest.rolloutServices),
buildServices: strings(latest.buildServices),
reusedServices: strings(latest.reusedServices),
buildSkippedCount: typeof latest.buildSkippedCount === "number" ? latest.buildSkippedCount : null,
artifactProvenanceAudit: audit,
summary: `build=${strings(latest.buildServices).length} reuse=${strings(latest.reusedServices).length} unsafeReuse=${unsafeReuseServices.length} provenanceRebuild=${provenanceRebuildServices.length}`,
disclosure: "parsed from plan-artifacts g14-ci-plan log event via Kubernetes pod logs",
});
} else {
out.degradedReason = podsResult.status >= 200 && podsResult.status < 300 ? "g14-ci-plan-event-not-found" : "plan-artifacts-pod-list-failed";
}
process.stdout.write(JSON.stringify(out));