fix: expose AgentRun branch-follower CI reuse evidence

This commit is contained in:
Codex
2026-07-04 08:08:41 +00:00
parent 111aa222bb
commit 3a0407f87c
5 changed files with 562 additions and 44 deletions
+31 -42
View File
@@ -24,11 +24,12 @@ import { configRefGraph, resolveConfigRefString } from "./ops/config-refs";
import { renderControllerManifests, renderControllerReconcileJob, waitForJobShell } from "./cicd-controller-render";
import { buildDebugStep } from "./cicd-debug";
import { runNativeHwlabControlPlaneRefresh } from "./cicd-hwlab-refresh";
import { agentRunCiConsumptionEvidence, agentRunManagerDecision, applyAgentRunReuseConfig, buildAgentRunReusePlan, compactAgentRunPayload, compactAgentRunReusePlan, reusableAgentRunImageArtifact, skippedAgentRunImageBuildResult } from "./cicd-agentrun-reuse";
import { nativeCicdScriptLoadShell, readNativeObjectBundle } from "./cicd-native-bundle";
import { compactRefreshEvidence, followerEvidenceSummary } from "./cicd-evidence";
import { runNativeK8sJob, runNativeTektonPipelineRun } from "./cicd-native";
import { argoApplicationReady, nativeArgoSummary, nativeGitMirrorReady, nativeGitMirrorRequired, nativeGitMirrorSummary, nativePipelineRunSummary, nativeRuntimeSummary, pipelineRunSucceeded, runtimeTargetShaFromWorkloads, runtimeWorkloadsReady } from "./cicd-native-summary";
import { invalidRuntimeReuseConfig, missingRuntimeReuseConfig, parseRuntimeReuseConfig, RUNTIME_REUSE_CONFIG_PATH, runtimeReuseService, summarizeRuntimeReuseConfig, type RuntimeReuseConfig } from "./cicd-reuse-config";
import { invalidRuntimeReuseConfig, missingRuntimeReuseConfig, parseRuntimeReuseConfig, requiredReuseServiceError, RUNTIME_REUSE_CONFIG_PATH, summarizeRuntimeReuseConfig, type RuntimeReuseConfig } from "./cicd-reuse-config";
import { prioritizedTaskRunItems } from "./cicd-taskruns";
import { runBranchFollowerTaskRunDrillDown } from "./cicd-taskrun-drilldown";
import { runBranchFollowerJobDrillDown, runBranchFollowerRuntimeDrillDown } from "./cicd-job-runtime-drilldown";
@@ -1275,15 +1276,30 @@ async function executeNativeAgentRunTrigger(registry: BranchFollowerRegistry, fo
const agentRunReuseError = requiredReuseServiceError(reuseConfig, ["agentrun-mgr", "manager"], "envReuse");
if (agentRunReuseError !== null) return nativeReuseConfigFailure(follower, observedSha, invalidRuntimeReuseConfig(reuseConfig, agentRunReuseError), startedAt);
const effectiveSpec = applyAgentRunReuseConfig(spec, reuseConfig);
const reusePlan = buildAgentRunReusePlan(follower, reuseConfig, observedSha);
const managerDecision = agentRunManagerDecision(reusePlan);
const buildJob = `${jobPrefix}-build-${observedSha.slice(0, 12)}`.slice(0, 63);
const build = runNativeK8sJob(effectiveSpec.ci.namespace, buildJob, yamlLaneK3sBuildImageJobManifest(effectiveSpec, observedSha, buildJob), Math.min(remainingSeconds(startedAt, timeoutSeconds), effectiveSpec.deployment.manager.imageBuild.timeoutSeconds), "buildkit", registry.controller.budgets);
const buildPayload = yamlLaneGitopsPublishPayloadFromProbe({ logsTail: stringOrNull(build.logsTail) ?? "" });
const buildSkipped = managerDecision?.skipImageBuild === true;
const reusedArtifact = buildSkipped ? reusableAgentRunImageArtifact(effectiveSpec, follower.nativeStatus.source.repoPath, observedSha) : null;
if (buildSkipped && reusedArtifact?.image === null) {
return nativeTriggerError(follower, `native AgentRun image reuse failed: ${stringOrNull(reusedArtifact.evidence.reason) ?? "reusable image artifact missing"}`, "agentrun-reusable-image-missing");
}
const build = buildSkipped && reusedArtifact !== null
? skippedAgentRunImageBuildResult(effectiveSpec.ci.namespace, buildJob, { decision: "skipImageBuild", reason: managerDecision?.reason ?? "reuse-plan-hit", artifact: reusedArtifact.evidence })
: runNativeK8sJob(effectiveSpec.ci.namespace, buildJob, yamlLaneK3sBuildImageJobManifest(effectiveSpec, observedSha, buildJob), Math.min(remainingSeconds(startedAt, timeoutSeconds), effectiveSpec.deployment.manager.imageBuild.timeoutSeconds), "buildkit", registry.controller.budgets);
const buildPayload = buildSkipped && reusedArtifact !== null
? { ok: true, status: "skipped", sourceCommit: observedSha, skipImageBuild: true, buildDecision: "skipImageBuild", reason: managerDecision?.reason ?? "reuse-plan-hit", artifact: reusedArtifact.evidence, valuesPrinted: false }
: yamlLaneGitopsPublishPayloadFromProbe({ logsTail: stringOrNull(build.logsTail) ?? "" });
const digest = stringOrNull(buildPayload.digest);
const envIdentity = stringOrNull(buildPayload.envIdentity);
if (!build.ok || digest === null || envIdentity === null) {
const image = buildSkipped && reusedArtifact !== null
? reusedArtifact.image
: digest !== null && envIdentity !== null
? agentRunImageArtifact(effectiveSpec, { sourceCommit: observedSha, envIdentity, digest, status: stringOrNull(buildPayload.status) ?? "built" })
: null;
if (!build.ok || image === null) {
return nativeK8sStageFailure(follower, observedSha, "image-build", buildJob, build, buildPayload, "native AgentRun image build failed", startedAt);
}
const image = agentRunImageArtifact(effectiveSpec, { sourceCommit: observedSha, envIdentity, digest, status: stringOrNull(buildPayload.status) ?? "built" });
const renderedFiles = renderAgentRunGitopsFiles(effectiveSpec, { sourceCommit: observedSha, image });
const publishJob = `${jobPrefix}-gitops-${observedSha.slice(0, 12)}`.slice(0, 63);
const publish = runNativeK8sJob(effectiveSpec.gitMirror.namespace, publishJob, yamlLaneGitopsPublishJobManifest(effectiveSpec, renderedFiles, publishJob), remainingSeconds(startedAt, timeoutSeconds), "publish", registry.controller.budgets);
@@ -1291,6 +1307,12 @@ async function executeNativeAgentRunTrigger(registry: BranchFollowerRegistry, fo
if (!publish.ok || publishPayload.ok === false || stringOrNull(publishPayload.gitopsCommit) === null) {
return nativeK8sStageFailure(follower, observedSha, "gitops-publish", publishJob, publish, publishPayload, "native AgentRun GitOps publish failed", startedAt);
}
const ciConsumption = agentRunCiConsumptionEvidence({
sourceCommit: observedSha,
plan: reusePlan,
imageBuild: { jobName: buildJob, result: build, payload: buildPayload },
gitopsPublish: { jobName: publishJob, result: publish, payload: publishPayload },
});
const flush = runNativeGitMirrorStage(registry, follower, observedSha, "flush", remainingSeconds(startedAt, timeoutSeconds));
if (flush !== null && !flush.result.ok) {
return nativeK8sStageFailure(follower, observedSha, "git-mirror-flush", flush.jobName, flush.result, { action: "flush" }, "native AgentRun git-mirror flush failed", startedAt);
@@ -1316,7 +1338,10 @@ async function executeNativeAgentRunTrigger(registry: BranchFollowerRegistry, fo
...tektonPayload,
agentrun: {
configPath,
sourceCommit: observedSha,
reuseConfig: summarizeRuntimeReuseConfig(reuseConfig),
reusePlan: compactAgentRunReusePlan(reusePlan),
ciConsumption,
gitMirrorSync: sync === null ? null : { jobName: sync.jobName, payload: sync.result },
imageBuild: { jobName: buildJob, result: build, payload: buildPayload },
gitopsPublish: { jobName: publishJob, result: publish, payload: publishPayload },
@@ -1692,21 +1717,6 @@ function nativeReuseConfigFailure(follower: FollowerSpec, observedSha: string, r
};
}
function requiredReuseServiceError(reuseConfig: RuntimeReuseConfig, ids: readonly string[], mode: "runtimeReuse" | "envReuse"): string | null {
const service = runtimeReuseService(reuseConfig, ids);
if (service === null) return `${RUNTIME_REUSE_CONFIG_PATH} must declare service ${ids.join("|")}`;
if (mode === "runtimeReuse") {
if (service.runtimeReuse === null) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} must declare runtimeReuse`;
if (service.runtimeReuse.enabled === false) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} runtimeReuse is disabled`;
if (service.runtimeReuse.codeIdentityPaths.length === 0 && service.runtimeReuse.envIdentityPaths.length === 0) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} runtimeReuse must declare codeIdentity or envIdentity paths`;
return null;
}
if (service.envReuse === null) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} must declare envReuse`;
if (service.envReuse.enabled === false) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} envReuse is disabled`;
if (service.envReuse.envIdentityFiles.length === 0 && service.envReuse.nodeDepsPath === null) return `${RUNTIME_REUSE_CONFIG_PATH} service ${service.id} envReuse must declare envIdentityFiles or nodeDepsPath`;
return null;
}
function annotatePipelineRunReuseConfig(manifest: Record<string, unknown>, reuseConfig: RuntimeReuseConfig): Record<string, unknown> {
const metadata = asOptionalRecord(manifest.metadata) ?? {};
const annotations = asOptionalRecord(metadata.annotations) ?? {};
@@ -1719,28 +1729,6 @@ function annotatePipelineRunReuseConfig(manifest: Record<string, unknown>, reuse
return manifest;
}
function applyAgentRunReuseConfig(spec: ReturnType<typeof resolveAgentRunLaneTarget>["spec"], reuseConfig: RuntimeReuseConfig): ReturnType<typeof resolveAgentRunLaneTarget>["spec"] {
const service = runtimeReuseService(reuseConfig, ["agentrun-mgr", "manager"]);
const envReuse = service?.envReuse;
if (envReuse === undefined || envReuse === null) return spec;
if (envReuse.enabled === false) return spec;
const imageBuild = spec.deployment.manager.imageBuild;
return {
...spec,
deployment: {
...spec.deployment,
manager: {
...spec.deployment.manager,
imageBuild: {
...imageBuild,
buildArgs: Object.keys(envReuse.buildArgs).length === 0 ? imageBuild.buildArgs : envReuse.buildArgs,
envIdentityFiles: envReuse.envIdentityFiles.length === 0 ? imageBuild.envIdentityFiles : envReuse.envIdentityFiles,
},
},
},
};
}
function shouldFlushNativeGitMirrorDuringCloseout(follower: FollowerSpec, gitMirror: Record<string, unknown> | null): boolean {
if (!nativeGitMirrorRequired(follower) || gitMirror === null) return false;
if (gitMirror.sourceSnapshotReady === false) return false;
@@ -2333,6 +2321,7 @@ function compactNativePayload(payload: Record<string, unknown> | null): Record<s
pipeline: asOptionalRecord(payload.pipeline),
taskRuns: compactTaskRunsPayload(asOptionalRecord(payload.taskRuns)),
planArtifacts: compactPlanArtifactsPayload(asOptionalRecord(payload.planArtifacts)),
agentrun: compactAgentRunPayload(asOptionalRecord(payload.agentrun)),
argo: asOptionalRecord(payload.argo),
runtime: asOptionalRecord(payload.runtime),
refreshEvidence: compactRefreshEvidence(asOptionalRecord(asOptionalRecord(payload.nativeCapabilities)?.controlPlaneRefresh)),