fix: expose tekton taskrun failures
This commit is contained in:
@@ -50,6 +50,8 @@ Multi-follower status summaries should omit per-follower `command.payload`/nativ
|
||||
|
||||
Argo closeout visibility must include the bounded reason for non-ready health, not only `Synced/Progressing`: health message, operation phase/message, short Application conditions and a small list of non-healthy resources when available.
|
||||
|
||||
Tekton failure visibility must include bounded TaskRun detail, not only PipelineRun `Failed`: failed TaskRuns, active TaskRuns and slow TaskRuns with task name, reason and duration. Without this, performance/failure work cannot move past the PipelineRun gate.
|
||||
|
||||
When Argo exposes operation start/finish timestamps, stage timing rows should report the Argo operation duration directly. Missing timestamps still render `-`; do not infer Argo duration from total elapsed time or from unrelated runtime polling.
|
||||
|
||||
The automatic controller loop is non-blocking, so closeout acceleration cannot live only in the user-facing `--wait` path. Once a triggered PipelineRun has succeeded and required runtime/GitOps gates are not aligned, the in-cluster controller path should perform the same bounded target-side Argo refresh used by wait closeout; otherwise convergence depends on Argo's background poll interval and can exceed the 120s budget even when Tekton finished quickly.
|
||||
|
||||
@@ -219,6 +219,7 @@ function compactRuntime(runtime) {
|
||||
function compactTaskRuns(taskRuns) {
|
||||
const value = recordOrNull(taskRuns);
|
||||
if (value === null) return null;
|
||||
const items = arrayRecords(value.items);
|
||||
return {
|
||||
ok: value.ok === true,
|
||||
count: numberOrNull(value.count),
|
||||
@@ -226,6 +227,19 @@ function compactTaskRuns(taskRuns) {
|
||||
failedCount: numberOrNull(value.failedCount),
|
||||
activeCount: numberOrNull(value.activeCount),
|
||||
performance: recordOrNull(value.performance),
|
||||
failedItems: items.filter((item) => item.status === "False").slice(0, 5).map(compactTaskRunItem),
|
||||
activeItems: items.filter((item) => item.status !== "True" && item.status !== "False").slice(0, 5).map(compactTaskRunItem),
|
||||
slowItems: arrayRecords(recordOrNull(value.performance)?.slowTaskRuns).slice(0, 5).map(compactTaskRunItem),
|
||||
};
|
||||
}
|
||||
|
||||
function compactTaskRunItem(item) {
|
||||
return {
|
||||
name: stringOrNull(item.name),
|
||||
pipelineTask: stringOrNull(item.pipelineTask),
|
||||
status: stringOrNull(item.status),
|
||||
reason: stringOrNull(item.reason),
|
||||
durationSeconds: numberOrNull(item.durationSeconds),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -291,6 +291,7 @@ function compactStatusGates(payload: Record<string, unknown> | null): Record<str
|
||||
if (payload === null) return null;
|
||||
const gitMirror = asOptionalRecord(payload.gitMirror);
|
||||
const tekton = asOptionalRecord(payload.tekton);
|
||||
const taskRuns = asOptionalRecord(payload.taskRuns);
|
||||
const argo = asOptionalRecord(payload.argo);
|
||||
const runtime = asOptionalRecord(payload.runtime);
|
||||
return {
|
||||
@@ -312,6 +313,14 @@ function compactStatusGates(payload: Record<string, unknown> | null): Record<str
|
||||
completionTime: stringOrNull(tekton.completionTime),
|
||||
durationSeconds: numberOrNull(tekton.durationSeconds),
|
||||
},
|
||||
taskRuns: taskRuns === null ? null : {
|
||||
count: numberOrNull(taskRuns.count),
|
||||
failedCount: numberOrNull(taskRuns.failedCount),
|
||||
activeCount: numberOrNull(taskRuns.activeCount),
|
||||
failedItems: taskRunItems(taskRuns, "failed"),
|
||||
activeItems: taskRunItems(taskRuns, "active"),
|
||||
slowItems: taskRunItems(taskRuns, "slow"),
|
||||
},
|
||||
argo: argo === null ? null : {
|
||||
syncStatus: stringOrNull(argo.syncStatus),
|
||||
healthStatus: stringOrNull(argo.healthStatus),
|
||||
@@ -336,6 +345,30 @@ function compactStatusGates(payload: Record<string, unknown> | null): Record<str
|
||||
};
|
||||
}
|
||||
|
||||
function taskRunItems(taskRuns: Record<string, unknown>, mode: "failed" | "active" | "slow"): Record<string, unknown>[] {
|
||||
const explicit = mode === "failed" ? taskRuns.failedItems : mode === "active" ? taskRuns.activeItems : taskRuns.slowItems;
|
||||
const explicitItems = arrayRecords(explicit);
|
||||
if (explicitItems.length > 0) return explicitItems.slice(0, 5).map(compactTaskRunItem);
|
||||
const items = arrayRecords(taskRuns.items);
|
||||
if (mode === "failed") return items.filter((item) => item.status === "False").slice(0, 5).map(compactTaskRunItem);
|
||||
if (mode === "active") return items.filter((item) => item.status !== "True" && item.status !== "False").slice(0, 5).map(compactTaskRunItem);
|
||||
return arrayRecords(asOptionalRecord(taskRuns.performance)?.slowTaskRuns).slice(0, 5).map(compactTaskRunItem);
|
||||
}
|
||||
|
||||
function compactTaskRunItem(item: Record<string, unknown>): Record<string, unknown> {
|
||||
return {
|
||||
name: stringOrNull(item.name),
|
||||
pipelineTask: stringOrNull(item.pipelineTask),
|
||||
status: stringOrNull(item.status),
|
||||
reason: stringOrNull(item.reason),
|
||||
durationSeconds: numberOrNull(item.durationSeconds),
|
||||
};
|
||||
}
|
||||
|
||||
function arrayRecords(value: unknown): Record<string, unknown>[] {
|
||||
return Array.isArray(value) ? value.filter((item): item is Record<string, unknown> => typeof item === "object" && item !== null && !Array.isArray(item)) : [];
|
||||
}
|
||||
|
||||
function compactFollowerDecision(state: FollowerState): Record<string, unknown> {
|
||||
return {
|
||||
phase: state.phase,
|
||||
|
||||
@@ -45,6 +45,19 @@ function nativeGateRows(native: Record<string, unknown> | null): unknown[][] {
|
||||
const duration = numberOrNull(tekton.durationSeconds);
|
||||
rows.push(["tekton", status, duration === null ? stringOrNull(tekton.reason) ?? "-" : `${duration}s`, stringOrNull(tekton.name) ?? "-"]);
|
||||
}
|
||||
const taskRuns = asOptionalRecord(native.taskRuns);
|
||||
if (taskRuns !== null) {
|
||||
const failed = arrayRecords(taskRuns.failedItems)[0];
|
||||
const active = arrayRecords(taskRuns.activeItems)[0];
|
||||
const slow = arrayRecords(taskRuns.slowItems)[0];
|
||||
const detail = taskRunDetail(failed ?? active ?? slow);
|
||||
const status = numberOrNull(taskRuns.failedCount) !== null && numberOrNull(taskRuns.failedCount)! > 0
|
||||
? `failed:${numberOrNull(taskRuns.failedCount)}`
|
||||
: numberOrNull(taskRuns.activeCount) !== null && numberOrNull(taskRuns.activeCount)! > 0
|
||||
? `active:${numberOrNull(taskRuns.activeCount)}`
|
||||
: "ok";
|
||||
rows.push(["taskruns", status, detail, "-"]);
|
||||
}
|
||||
const argo = asOptionalRecord(native.argo);
|
||||
if (argo !== null) {
|
||||
rows.push(["argo", `${stringOrNull(argo.syncStatus) ?? "unknown"}/${stringOrNull(argo.healthStatus) ?? "unknown"}`, argoDetail(argo), stringOrNull(argo.name) ?? "-"]);
|
||||
@@ -58,6 +71,12 @@ function nativeGateRows(native: Record<string, unknown> | null): unknown[][] {
|
||||
return rows;
|
||||
}
|
||||
|
||||
function taskRunDetail(item: Record<string, unknown> | undefined): string {
|
||||
if (item === undefined) return "-";
|
||||
const duration = numberOrNull(item.durationSeconds);
|
||||
return `${item.pipelineTask ?? item.name ?? "task"} ${item.reason ?? item.status ?? "-"}${duration === null ? "" : ` ${duration}s`}`;
|
||||
}
|
||||
|
||||
function argoDetail(argo: Record<string, unknown>): string {
|
||||
const resource = arrayRecords(argo.nonReadyResources)[0];
|
||||
const condition = arrayRecords(argo.conditions)[0];
|
||||
|
||||
Reference in New Issue
Block a user