|
|
|
@@ -1116,7 +1116,7 @@ function nativeTektonTriggerResult(input: {
|
|
|
|
|
const failed = input.payload.failed === true || input.result.exitCode !== 0;
|
|
|
|
|
const stillRunning = input.payload.stillRunning === true || input.payload.timedOutWait === true;
|
|
|
|
|
const message = failed
|
|
|
|
|
? tailText(input.result.stderr || stringOrNull(input.payload.message) || input.result.stdout, 500)
|
|
|
|
|
? nativeTektonFailureText(input.payload, input.result)
|
|
|
|
|
: input.closeout?.completed === true
|
|
|
|
|
? input.successMessage
|
|
|
|
|
: input.closeout?.timedOut === true
|
|
|
|
@@ -1159,6 +1159,17 @@ function nativeTektonTriggerResult(input: {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nativeTektonFailureText(payload: Record<string, unknown>, result: CommandResult): string {
|
|
|
|
|
return [
|
|
|
|
|
stringOrNull(payload.message),
|
|
|
|
|
stringOrNull(payload.conditionReason),
|
|
|
|
|
stringOrNull(payload.conditionMessage),
|
|
|
|
|
payload.failed === true ? "PipelineRun failed" : null,
|
|
|
|
|
result.timedOut ? "timedOut=true" : null,
|
|
|
|
|
].filter((item): item is string => item !== null && item.length > 0).join("; ")
|
|
|
|
|
|| "native PipelineRun failed; query events/logs for structured details";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function remainingSeconds(startedAt: number, timeoutSeconds: number): number {
|
|
|
|
|
return Math.max(1, timeoutSeconds - Math.ceil((Date.now() - startedAt) / 1000));
|
|
|
|
|
}
|
|
|
|
@@ -1439,7 +1450,7 @@ async function readAdapterStatus(registry: BranchFollowerRegistry, follower: Fol
|
|
|
|
|
taskRuns: bundle.taskRuns,
|
|
|
|
|
planArtifacts: bundle.planArtifacts,
|
|
|
|
|
argo: nativeArgoSummary(bundle.argoApplication),
|
|
|
|
|
runtime: nativeRuntimeSummary(follower.nativeStatus.runtime, bundle.workloads),
|
|
|
|
|
runtime: nativeRuntimeSummary(follower.nativeStatus.runtime, bundle.workloads, observedSha),
|
|
|
|
|
timings: { statusRead: { elapsedMs: bundle.elapsedMs, budgetSeconds: timeoutSeconds } },
|
|
|
|
|
errors: bundle.errors,
|
|
|
|
|
statusAuthority: "k8s-native",
|
|
|
|
@@ -1713,18 +1724,24 @@ function nativeArgoSummary(application: Record<string, unknown> | null): Record<
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function nativeRuntimeSummary(runtime: NativeStatusSpec["runtime"], workloads: Record<string, unknown>[]): Record<string, unknown> | null {
|
|
|
|
|
function nativeRuntimeSummary(runtime: NativeStatusSpec["runtime"], workloads: Record<string, unknown>[], expectedSha: string | null): Record<string, unknown> | null {
|
|
|
|
|
if (runtime === null) return null;
|
|
|
|
|
const targetSha = runtimeTargetShaFromWorkloads(runtime, workloads);
|
|
|
|
|
return {
|
|
|
|
|
namespace: runtime.namespace,
|
|
|
|
|
ready: runtimeWorkloadsReady(runtime, workloads),
|
|
|
|
|
targetSha,
|
|
|
|
|
expectedSha,
|
|
|
|
|
aligned: expectedSha !== null && targetSha !== null ? targetSha === expectedSha : null,
|
|
|
|
|
workloads: runtime.workloads.map((spec, index) => {
|
|
|
|
|
const workload = workloads[index] ?? null;
|
|
|
|
|
const sourceCommit = workloadSourceCommit(spec, workload);
|
|
|
|
|
return {
|
|
|
|
|
kind: spec.kind,
|
|
|
|
|
name: spec.name,
|
|
|
|
|
ready: workloadReady(spec, workload),
|
|
|
|
|
sourceCommit: workloadSourceCommit(spec, workload),
|
|
|
|
|
sourceCommit,
|
|
|
|
|
aligned: expectedSha !== null && sourceCommit !== null ? sourceCommit === expectedSha : null,
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
@@ -1824,7 +1841,7 @@ function mergeFollowerStatus(
|
|
|
|
|
stateConfigMap: registry.controller.stateConfigMapName,
|
|
|
|
|
live: liveRequested,
|
|
|
|
|
message: live?.message ?? stringOrNull(stored.decision) ?? "no controller state yet",
|
|
|
|
|
timings: live === null ? asOptionalRecord(stored.timings) : buildFollowerTimings(follower, live, undefined, asOptionalRecord(stored.timings), phase),
|
|
|
|
|
timings: live === null ? storedFollowerTimingsForStatus(follower, asOptionalRecord(stored.timings), phase, observedSha) : buildFollowerTimings(follower, live, undefined, asOptionalRecord(stored.timings), phase),
|
|
|
|
|
warnings: Array.isArray(stored.warnings) ? stored.warnings.slice(0, 6) : [],
|
|
|
|
|
next: followerNextCommands(follower),
|
|
|
|
|
};
|
|
|
|
@@ -2073,7 +2090,7 @@ function buildFollowerTimings(
|
|
|
|
|
phase?: BranchFollowerPhase,
|
|
|
|
|
): FollowerState["timings"] {
|
|
|
|
|
const nativePayload = asOptionalRecord(live.payload);
|
|
|
|
|
const total = totalTimingFromCommand(triggerCommand, phase) ?? totalTimingFromStored(storedTimings, phase, stringOrNull(triggerCommand?.finishedAt));
|
|
|
|
|
const total = totalTimingFromCommand(triggerCommand, phase) ?? totalTimingFromStored(storedTimings, phase, stringOrNull(triggerCommand?.finishedAt), live.observedSha);
|
|
|
|
|
const stages = dedupeTimingStages([
|
|
|
|
|
...stageTimingsFromCommand(triggerCommand),
|
|
|
|
|
...stageTimingsFromNativePayload(nativePayload),
|
|
|
|
@@ -2082,7 +2099,8 @@ function buildFollowerTimings(
|
|
|
|
|
budgetSeconds: follower.budgets.endToEndSeconds,
|
|
|
|
|
totalSeconds: total?.seconds ?? null,
|
|
|
|
|
totalStatus: total?.status ?? "unknown",
|
|
|
|
|
totalSource: total?.source ?? "unavailable",
|
|
|
|
|
totalSource: total?.source ?? "-",
|
|
|
|
|
sourceCommit: total?.sourceCommit ?? stringOrNull(triggerCommand?.sourceCommit) ?? null,
|
|
|
|
|
startedAt: total?.startedAt ?? null,
|
|
|
|
|
finishedAt: total?.finishedAt ?? null,
|
|
|
|
|
overBudget: total === null ? null : total.seconds > follower.budgets.endToEndSeconds,
|
|
|
|
@@ -2090,12 +2108,49 @@ function buildFollowerTimings(
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function storedFollowerTimingsForStatus(
|
|
|
|
|
follower: FollowerSpec,
|
|
|
|
|
storedTimings: Record<string, unknown> | null,
|
|
|
|
|
phase: BranchFollowerPhase,
|
|
|
|
|
observedSha: string | null,
|
|
|
|
|
): FollowerState["timings"] {
|
|
|
|
|
const total = totalTimingFromStored(storedTimings, phase, null, observedSha);
|
|
|
|
|
const sourceCommit = total?.sourceCommit ?? null;
|
|
|
|
|
return {
|
|
|
|
|
budgetSeconds: follower.budgets.endToEndSeconds,
|
|
|
|
|
totalSeconds: total?.seconds ?? null,
|
|
|
|
|
totalStatus: total?.status ?? "unknown",
|
|
|
|
|
totalSource: total?.source ?? "-",
|
|
|
|
|
sourceCommit,
|
|
|
|
|
startedAt: total?.startedAt ?? null,
|
|
|
|
|
finishedAt: total?.finishedAt ?? null,
|
|
|
|
|
overBudget: total === null ? null : total.seconds > follower.budgets.endToEndSeconds,
|
|
|
|
|
stages: sourceCommit === null ? [] : storedStageTimings(storedTimings),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function storedStageTimings(storedTimings: Record<string, unknown> | null): StageTiming[] {
|
|
|
|
|
if (storedTimings === null) return [];
|
|
|
|
|
return arrayRecords(storedTimings.stages)
|
|
|
|
|
.map((stage) => stageTiming(
|
|
|
|
|
stringOrNull(stage.stage) ?? "",
|
|
|
|
|
stringOrNull(stage.status) ?? "unknown",
|
|
|
|
|
numberOrNull(stage.seconds),
|
|
|
|
|
numberOrNull(stage.budgetSeconds),
|
|
|
|
|
stringOrNull(stage.source) ?? "stored-state",
|
|
|
|
|
stringOrNull(stage.object),
|
|
|
|
|
))
|
|
|
|
|
.filter((stage) => stage.stage.length > 0)
|
|
|
|
|
.slice(0, 24);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function compactTimings(timings: FollowerState["timings"]): FollowerState["timings"] {
|
|
|
|
|
return {
|
|
|
|
|
budgetSeconds: timings.budgetSeconds,
|
|
|
|
|
totalSeconds: timings.totalSeconds,
|
|
|
|
|
totalStatus: timings.totalStatus,
|
|
|
|
|
totalSource: timings.totalSource,
|
|
|
|
|
sourceCommit: timings.sourceCommit,
|
|
|
|
|
startedAt: timings.startedAt,
|
|
|
|
|
finishedAt: timings.finishedAt,
|
|
|
|
|
overBudget: timings.overBudget,
|
|
|
|
@@ -2110,7 +2165,7 @@ function compactTimings(timings: FollowerState["timings"]): FollowerState["timin
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function totalTimingFromCommand(command: Record<string, unknown> | undefined, phase?: BranchFollowerPhase): { seconds: number; status: string; source: string; startedAt: string | null; finishedAt: string | null } | null {
|
|
|
|
|
function totalTimingFromCommand(command: Record<string, unknown> | undefined, phase?: BranchFollowerPhase): { seconds: number; status: string; source: string; sourceCommit: string | null; startedAt: string | null; finishedAt: string | null } | null {
|
|
|
|
|
if (command === undefined) return null;
|
|
|
|
|
if (command.mode === "k8s-native-closeout") return null;
|
|
|
|
|
const payload = asOptionalRecord(command.payload);
|
|
|
|
@@ -2134,13 +2189,16 @@ function totalTimingFromCommand(command: Record<string, unknown> | undefined, ph
|
|
|
|
|
: phase === undefined
|
|
|
|
|
? "submitted"
|
|
|
|
|
: phase.toLowerCase();
|
|
|
|
|
return { seconds, status, source: stringOrNull(command.mode) ?? stringOrNull(command.status) ?? "command", startedAt, finishedAt };
|
|
|
|
|
return { seconds, status, source: stringOrNull(command.mode) ?? stringOrNull(command.status) ?? "command", sourceCommit: stringOrNull(command.sourceCommit), startedAt, finishedAt };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function totalTimingFromStored(storedTimings: Record<string, unknown> | null | undefined, phase?: BranchFollowerPhase, finishOverride?: string | null): { seconds: number; status: string; source: string; startedAt: string | null; finishedAt: string | null } | null {
|
|
|
|
|
function totalTimingFromStored(storedTimings: Record<string, unknown> | null | undefined, phase?: BranchFollowerPhase, finishOverride?: string | null, observedSha?: string | null): { seconds: number; status: string; source: string; sourceCommit: string | null; startedAt: string | null; finishedAt: string | null } | null {
|
|
|
|
|
if (storedTimings === null || storedTimings === undefined) return null;
|
|
|
|
|
const status = stringOrNull(storedTimings.totalStatus);
|
|
|
|
|
const source = stringOrNull(storedTimings.totalSource);
|
|
|
|
|
const sourceCommit = stringOrNull(storedTimings.sourceCommit);
|
|
|
|
|
if (sourceCommit === null) return null;
|
|
|
|
|
if (observedSha !== null && observedSha !== undefined && sourceCommit !== observedSha) return null;
|
|
|
|
|
const startedAt = stringOrNull(storedTimings.startedAt);
|
|
|
|
|
const finishedAt = stringOrNull(storedTimings.finishedAt) ?? finishOverride ?? null;
|
|
|
|
|
if (phase === "Noop" && finishedAt === null) return null;
|
|
|
|
@@ -2150,6 +2208,7 @@ function totalTimingFromStored(storedTimings: Record<string, unknown> | null | u
|
|
|
|
|
seconds,
|
|
|
|
|
status: finishedAt === null && phase !== undefined && !terminalPhase(phase) ? phase.toLowerCase() : phase === undefined ? status ?? "recorded" : phase.toLowerCase(),
|
|
|
|
|
source: source ?? "stored-state",
|
|
|
|
|
sourceCommit,
|
|
|
|
|
startedAt,
|
|
|
|
|
finishedAt,
|
|
|
|
|
};
|
|
|
|
@@ -2202,7 +2261,8 @@ function stageTimingsFromNativePayload(payload: Record<string, unknown> | null):
|
|
|
|
|
}
|
|
|
|
|
const runtime = asOptionalRecord(payload.runtime);
|
|
|
|
|
if (runtime !== null) {
|
|
|
|
|
stages.push(stageTiming("runtime", runtime.ready === true ? "ready" : "not-ready", null, null, "kubernetes-workload", stringOrNull(runtime.namespace)));
|
|
|
|
|
const aligned = runtime.aligned === true ? "aligned" : runtime.aligned === false ? "stale" : "unknown-target";
|
|
|
|
|
stages.push(stageTiming("runtime", `${runtime.ready === true ? "ready" : "not-ready"}/${aligned}`, null, null, "kubernetes-workload", stringOrNull(runtime.namespace)));
|
|
|
|
|
}
|
|
|
|
|
return stages;
|
|
|
|
|
}
|
|
|
|
@@ -2741,7 +2801,7 @@ function timingRowsForFollower(item: Record<string, unknown>): unknown[][] {
|
|
|
|
|
stringOrNull(timings.totalStatus) ?? "unknown",
|
|
|
|
|
formatSeconds(numberOrNull(timings.totalSeconds)),
|
|
|
|
|
formatSeconds(budget),
|
|
|
|
|
stringOrNull(timings.totalSource) ?? "-",
|
|
|
|
|
[stringOrNull(timings.totalSource), shortSha(stringOrNull(timings.sourceCommit))].filter((value) => value !== null && value !== "-").join(":") || "-",
|
|
|
|
|
]];
|
|
|
|
|
for (const stage of arrayRecords(timings.stages)) {
|
|
|
|
|
rows.push([
|
|
|
|
|