cicd track follower timing start and finish
This commit is contained in:
@@ -291,6 +291,8 @@ export interface FollowerState {
|
||||
totalSeconds: number | null;
|
||||
totalStatus: string;
|
||||
totalSource: string;
|
||||
startedAt: string | null;
|
||||
finishedAt: string | null;
|
||||
overBudget: boolean | null;
|
||||
stages: StageTiming[];
|
||||
};
|
||||
|
||||
+52
-10
@@ -828,7 +828,7 @@ async function decideAndMaybeTrigger(
|
||||
decision,
|
||||
dryRun: options.dryRun,
|
||||
updatedAt: new Date().toISOString(),
|
||||
timings: buildFollowerTimings(follower, live, triggerCommand),
|
||||
timings: buildFollowerTimings(follower, live, triggerCommand, null, phase),
|
||||
warnings,
|
||||
next: followerNextCommands(follower),
|
||||
command: triggerCommand ?? {
|
||||
@@ -1034,6 +1034,8 @@ function nativeK8sStageFailure(
|
||||
jobName,
|
||||
sourceCommit: observedSha,
|
||||
ok: false,
|
||||
startedAt: startedAt === undefined ? null : new Date(startedAt).toISOString(),
|
||||
finishedAt: new Date().toISOString(),
|
||||
elapsedMs: startedAt === undefined ? job.elapsedMs : Date.now() - startedAt,
|
||||
payload,
|
||||
job,
|
||||
@@ -1109,6 +1111,9 @@ function nativeTektonTriggerResult(input: {
|
||||
? `native PipelineRun ${input.pipelineRun} is still running; query status/events/logs for closeout`
|
||||
: `native PipelineRun ${input.pipelineRun} submitted`;
|
||||
const ok = !failed && (input.closeout === null || input.closeout.completed === true);
|
||||
const finishedAt = failed || input.result.timedOut || input.closeout?.completed === true || input.closeout?.timedOut === true
|
||||
? new Date().toISOString()
|
||||
: null;
|
||||
return {
|
||||
ok,
|
||||
completed: input.closeout?.completed === true,
|
||||
@@ -1124,6 +1129,8 @@ function nativeTektonTriggerResult(input: {
|
||||
wait: input.wait,
|
||||
pipelineRunCompleted,
|
||||
stillRunning,
|
||||
startedAt: new Date(input.startedAt).toISOString(),
|
||||
finishedAt,
|
||||
elapsedMs: Date.now() - input.startedAt,
|
||||
closeout: input.closeout,
|
||||
statusAuthority: "kubernetes-api-serviceaccount",
|
||||
@@ -1192,6 +1199,9 @@ async function executeNativeSentinelTrigger(registry: BranchFollowerRegistry, fo
|
||||
? `native sentinel PipelineRun ${pipelineRun} is still running; query status/events/logs for closeout`
|
||||
: `native sentinel PipelineRun ${pipelineRun} submitted`;
|
||||
const ok = !failed && (closeout === null || closeout.completed === true);
|
||||
const finishedAt = failed || result.timedOut || closeout?.completed === true || closeout?.timedOut === true
|
||||
? new Date().toISOString()
|
||||
: null;
|
||||
return {
|
||||
ok,
|
||||
completed: closeout?.completed === true,
|
||||
@@ -1207,6 +1217,8 @@ async function executeNativeSentinelTrigger(registry: BranchFollowerRegistry, fo
|
||||
wait: options.wait,
|
||||
pipelineRunCompleted,
|
||||
stillRunning,
|
||||
startedAt: new Date(startedAt).toISOString(),
|
||||
finishedAt,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
closeout,
|
||||
statusAuthority: "kubernetes-api-serviceaccount",
|
||||
@@ -1775,7 +1787,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)),
|
||||
timings: live === null ? asOptionalRecord(stored.timings) : buildFollowerTimings(follower, live, undefined, asOptionalRecord(stored.timings), phase),
|
||||
warnings: Array.isArray(stored.warnings) ? stored.warnings.slice(0, 6) : [],
|
||||
next: followerNextCommands(follower),
|
||||
};
|
||||
@@ -2072,8 +2084,9 @@ function buildFollowerTimings(
|
||||
live: AdapterSummary,
|
||||
triggerCommand: Record<string, unknown> | undefined,
|
||||
storedTimings?: Record<string, unknown> | null,
|
||||
phase?: BranchFollowerPhase,
|
||||
): FollowerState["timings"] {
|
||||
const total = totalTimingFromCommand(triggerCommand) ?? totalTimingFromStored(storedTimings);
|
||||
const total = totalTimingFromCommand(triggerCommand, phase) ?? totalTimingFromStored(storedTimings, phase);
|
||||
const stages = dedupeTimingStages([
|
||||
...stageTimingsFromCommand(triggerCommand),
|
||||
...stageTimingsFromNativePayload(asOptionalRecord(live.payload)),
|
||||
@@ -2083,6 +2096,8 @@ function buildFollowerTimings(
|
||||
totalSeconds: total?.seconds ?? null,
|
||||
totalStatus: total?.status ?? "unknown",
|
||||
totalSource: total?.source ?? "unavailable",
|
||||
startedAt: total?.startedAt ?? null,
|
||||
finishedAt: total?.finishedAt ?? null,
|
||||
overBudget: total === null ? null : total.seconds > follower.budgets.endToEndSeconds,
|
||||
stages,
|
||||
};
|
||||
@@ -2094,6 +2109,8 @@ function compactTimings(timings: FollowerState["timings"]): FollowerState["timin
|
||||
totalSeconds: timings.totalSeconds,
|
||||
totalStatus: timings.totalStatus,
|
||||
totalSource: timings.totalSource,
|
||||
startedAt: timings.startedAt,
|
||||
finishedAt: timings.finishedAt,
|
||||
overBudget: timings.overBudget,
|
||||
stages: timings.stages.slice(0, 24).map((stage) => ({
|
||||
stage: stage.stage,
|
||||
@@ -2106,9 +2123,11 @@ function compactTimings(timings: FollowerState["timings"]): FollowerState["timin
|
||||
};
|
||||
}
|
||||
|
||||
function totalTimingFromCommand(command: Record<string, unknown> | undefined): { seconds: number; status: string; source: string } | null {
|
||||
function totalTimingFromCommand(command: Record<string, unknown> | undefined, phase?: BranchFollowerPhase): { seconds: number; status: string; source: string; startedAt: string | null; finishedAt: string | null } | null {
|
||||
if (command === undefined) return null;
|
||||
const seconds = secondsFromMs(numberOrNull(command.elapsedMs));
|
||||
const startedAt = stringOrNull(command.startedAt);
|
||||
const finishedAt = stringOrNull(command.finishedAt);
|
||||
const seconds = totalSecondsFromRange(startedAt, finishedAt) ?? secondsFromMs(numberOrNull(command.elapsedMs));
|
||||
if (seconds === null) return null;
|
||||
const closeout = asOptionalRecord(command.closeout);
|
||||
const exitCode = numberOrNull(command.exitCode);
|
||||
@@ -2122,21 +2141,44 @@ function totalTimingFromCommand(command: Record<string, unknown> | undefined): {
|
||||
? "running"
|
||||
: command.pipelineRunCompleted === true
|
||||
? "ci-completed"
|
||||
: "submitted";
|
||||
return { seconds, status, source: stringOrNull(command.mode) ?? stringOrNull(command.status) ?? "command" };
|
||||
: phase === undefined
|
||||
? "submitted"
|
||||
: phase.toLowerCase();
|
||||
return { seconds, status, source: stringOrNull(command.mode) ?? stringOrNull(command.status) ?? "command", startedAt, finishedAt };
|
||||
}
|
||||
|
||||
function totalTimingFromStored(storedTimings: Record<string, unknown> | null | undefined): { seconds: number; status: string; source: string } | null {
|
||||
function totalTimingFromStored(storedTimings: Record<string, unknown> | null | undefined, phase?: BranchFollowerPhase): { seconds: number; status: string; source: string; startedAt: string | null; finishedAt: string | null } | null {
|
||||
if (storedTimings === null || storedTimings === undefined) return null;
|
||||
const seconds = numberOrNull(storedTimings.totalSeconds);
|
||||
const startedAt = stringOrNull(storedTimings.startedAt);
|
||||
const finishedAt = stringOrNull(storedTimings.finishedAt);
|
||||
const seconds = totalSecondsFromRange(startedAt, finishedAt) ?? numberOrNull(storedTimings.totalSeconds);
|
||||
if (seconds === null) return null;
|
||||
return {
|
||||
seconds,
|
||||
status: stringOrNull(storedTimings.totalStatus) ?? "recorded",
|
||||
status: finishedAt === null && phase !== undefined && !terminalPhase(phase) ? phase.toLowerCase() : stringOrNull(storedTimings.totalStatus) ?? "recorded",
|
||||
source: stringOrNull(storedTimings.totalSource) ?? "stored-state",
|
||||
startedAt,
|
||||
finishedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function totalSecondsFromRange(startedAt: string | null, finishedAt: string | null): number | null {
|
||||
const startedMs = timestampMs(startedAt);
|
||||
if (startedMs === null) return null;
|
||||
const finishedMs = timestampMs(finishedAt) ?? Date.now();
|
||||
return finishedMs >= startedMs ? roundSeconds((finishedMs - startedMs) / 1000) : null;
|
||||
}
|
||||
|
||||
function timestampMs(value: string | null): number | null {
|
||||
if (value === null) return null;
|
||||
const parsed = Date.parse(value);
|
||||
return Number.isFinite(parsed) ? parsed : null;
|
||||
}
|
||||
|
||||
function terminalPhase(phase: BranchFollowerPhase): boolean {
|
||||
return phase === "Succeeded" || phase === "Failed" || phase === "Blocked" || phase === "Skipped" || phase === "Noop";
|
||||
}
|
||||
|
||||
function stageTimingsFromNativePayload(payload: Record<string, unknown> | null): StageTiming[] {
|
||||
if (payload === null) return [];
|
||||
const stages: StageTiming[] = [];
|
||||
|
||||
Reference in New Issue
Block a user