fix: 增强 AgentRun control-plane status drill-down
This commit is contained in:
+176
-7
@@ -48,6 +48,8 @@ export function agentRunHelp(): unknown {
|
||||
"bun scripts/cli.ts agentrun v01 sessions read <sessionId> --reader-id cli",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane status",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane status --full",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run agentrun-v01-ci-<short-sha>",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane status --source-commit <full-sha>",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane trigger-current --dry-run",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
|
||||
"bun scripts/cli.ts agentrun v01 control-plane refresh --dry-run",
|
||||
@@ -69,7 +71,7 @@ export async function runAgentRunCommand(config: UniDeskConfig, args: string[]):
|
||||
const [lane, group, action] = args;
|
||||
if (lane !== "v01") return unsupported(args);
|
||||
if (group === "control-plane") {
|
||||
if (action === "status") return await status(config, parseDisclosureOptions(args.slice(3)));
|
||||
if (action === "status") return await status(config, parseStatusOptions(args.slice(3)));
|
||||
if (action === "trigger-current") return await triggerCurrent(config, parseTriggerOptions(args.slice(3)));
|
||||
if (action === "refresh") return await refresh(config, parseConfirmOptions(args.slice(3)));
|
||||
if (action === "cleanup-runs") return await cleanupRuns(config, parseCleanupRunsOptions(args.slice(3)));
|
||||
@@ -124,6 +126,12 @@ interface DisclosureOptions {
|
||||
raw: boolean;
|
||||
}
|
||||
|
||||
interface StatusOptions extends DisclosureOptions {
|
||||
sourceCommit: string | null;
|
||||
pipelineRun: string | null;
|
||||
targetMode: "latest-source-head" | "source-commit" | "pipeline-run";
|
||||
}
|
||||
|
||||
interface TimedValue<T> {
|
||||
value: T;
|
||||
elapsedMs: number;
|
||||
@@ -137,6 +145,50 @@ function parseDisclosureOptions(args: string[]): DisclosureOptions {
|
||||
return { full: raw || args.includes("--full"), raw };
|
||||
}
|
||||
|
||||
function parseStatusOptions(args: string[]): StatusOptions {
|
||||
let sourceCommit: string | null = null;
|
||||
let pipelineRun: string | null = null;
|
||||
let full = false;
|
||||
let raw = false;
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--full") {
|
||||
full = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--raw") {
|
||||
raw = true;
|
||||
full = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--source-commit") {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) throw new Error("--source-commit requires a value");
|
||||
if (!isGitSha(value)) throw new Error("--source-commit must be a full 40-character git SHA");
|
||||
sourceCommit = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--pipeline-run") {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) throw new Error("--pipeline-run requires a value");
|
||||
if (!isAgentRunPipelineRunName(value)) throw new Error("--pipeline-run must be an agentrun-v01-ci-<12+ hex> PipelineRun name");
|
||||
pipelineRun = value;
|
||||
index += 1;
|
||||
continue;
|
||||
}
|
||||
throw new Error(`unsupported status option: ${arg}`);
|
||||
}
|
||||
if (sourceCommit !== null && pipelineRun !== null) throw new Error("control-plane status accepts only one of --source-commit or --pipeline-run");
|
||||
return {
|
||||
full,
|
||||
raw,
|
||||
sourceCommit,
|
||||
pipelineRun,
|
||||
targetMode: pipelineRun !== null ? "pipeline-run" : sourceCommit !== null ? "source-commit" : "latest-source-head",
|
||||
};
|
||||
}
|
||||
|
||||
function parseTriggerOptions(args: string[]): TriggerOptions {
|
||||
return parseConfirmOptions(args);
|
||||
}
|
||||
@@ -208,7 +260,7 @@ function positiveIntegerOption(args: string[], name: string, defaultValue: numbe
|
||||
return Math.min(value, maxValue);
|
||||
}
|
||||
|
||||
async function status(config: UniDeskConfig, options: DisclosureOptions): Promise<Record<string, unknown>> {
|
||||
async function status(config: UniDeskConfig, options: StatusOptions): Promise<Record<string, unknown>> {
|
||||
const sourceProbe = await timedStatusStage("source", () => capture(config, g14SourceRoute, ["script", "--", [
|
||||
"cd /root/agentrun-v01",
|
||||
"git fetch origin v0.1 >/dev/null 2>&1 || true",
|
||||
@@ -223,9 +275,17 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
const source = sourceProbe.value;
|
||||
const localSourceCommit = matchLine(source.stdout, "sourceCommit=");
|
||||
const originSourceCommit = matchLine(source.stdout, "originV01=");
|
||||
const sourceCommit = isGitSha(originSourceCommit ?? "") ? originSourceCommit : localSourceCommit;
|
||||
const latestSourceCommit = isGitSha(originSourceCommit ?? "") ? originSourceCommit : localSourceCommit;
|
||||
const gitopsLatest = matchLine(source.stdout, "gitopsLatest=");
|
||||
const pipelineRun = sourceCommit ? pipelineRunName(sourceCommit) : null;
|
||||
let sourceCommit = options.sourceCommit ?? (options.pipelineRun !== null ? null : latestSourceCommit);
|
||||
let sourceCommitSource = options.sourceCommit !== null
|
||||
? "option"
|
||||
: options.pipelineRun !== null
|
||||
? "pipeline-run-param"
|
||||
: sourceCommit === originSourceCommit
|
||||
? "origin/v0.1"
|
||||
: "local-head";
|
||||
const pipelineRun = options.pipelineRun ?? (sourceCommit ? pipelineRunName(sourceCommit) : null);
|
||||
const [runtimeProbe, mirrorProbe] = await Promise.all([
|
||||
timedStatusStage("runtime", () => capture(config, g14K3sRoute, ["script", "--", statusScript(pipelineRun)])),
|
||||
timedStatusStage("git-mirror", () => readGitMirrorStatus(config)),
|
||||
@@ -237,6 +297,22 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
const pipelineRunCondition = labeledJson(k3s.stdout, "pipelineRunCondition");
|
||||
const managerImage = parseManagerImage(k3s.stdout);
|
||||
const mirrorSummary = mirror.summary;
|
||||
const pipelineRunSourceCommit = stringOrNull(pipelineRunCondition.sourceCommit);
|
||||
if (options.pipelineRun !== null && sourceCommit === null && pipelineRunSourceCommit !== null && isGitSha(pipelineRunSourceCommit)) {
|
||||
sourceCommit = pipelineRunSourceCommit;
|
||||
sourceCommitSource = "pipeline-run-param";
|
||||
}
|
||||
const target = {
|
||||
mode: options.targetMode,
|
||||
sourceCommit,
|
||||
sourceCommitSource,
|
||||
pipelineRun,
|
||||
pipelineRunSourceCommit,
|
||||
latestSourceCommit,
|
||||
latestPipelineRun: latestSourceCommit ? pipelineRunName(latestSourceCommit) : null,
|
||||
isLatestSource: Boolean(sourceCommit && latestSourceCommit && sourceCommit === latestSourceCommit),
|
||||
sourceMatchesPipelineRun: sourceCommit === null || pipelineRunSourceCommit === null ? null : sourceCommit === pipelineRunSourceCommit,
|
||||
};
|
||||
const runtimeAlignment = {
|
||||
localHeadMatchesOrigin: Boolean(localSourceCommit && originSourceCommit && localSourceCommit === originSourceCommit),
|
||||
argoRevision: argo.revision,
|
||||
@@ -246,7 +322,15 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
managerSourceCommit: managerImage.sourceCommit,
|
||||
managerSourceMatchesExpected: Boolean(sourceCommit && managerImage.sourceCommit === sourceCommit),
|
||||
};
|
||||
const targetValidation = buildAgentRunTargetValidation({
|
||||
pipelineRun,
|
||||
pipelineRunCondition,
|
||||
sourceCommit,
|
||||
targetIsLatestSource: target.isLatestSource,
|
||||
managerSourceMatchesExpected: runtimeAlignment.managerSourceMatchesExpected,
|
||||
});
|
||||
const summary = {
|
||||
target,
|
||||
sourceCommit,
|
||||
expectedPipelineRun: pipelineRun,
|
||||
pipelineRun: {
|
||||
@@ -254,6 +338,7 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
reason: pipelineRunCondition.reason ?? null,
|
||||
completionTime: pipelineRunCondition.completionTime ?? null,
|
||||
},
|
||||
targetValidation,
|
||||
argo,
|
||||
managerImage,
|
||||
gitMirror: {
|
||||
@@ -278,8 +363,10 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
command: "agentrun v01 control-plane status",
|
||||
lane: "v0.1",
|
||||
summary,
|
||||
target,
|
||||
sourceCommit,
|
||||
sourceCommitSource: sourceCommit === originSourceCommit ? "origin/v0.1" : "local-head",
|
||||
sourceCommitSource,
|
||||
latestSourceCommit,
|
||||
localSourceCommit,
|
||||
originSourceCommit,
|
||||
gitopsLatest,
|
||||
@@ -303,16 +390,21 @@ async function status(config: UniDeskConfig, options: DisclosureOptions): Promis
|
||||
...(options.raw ? { raw: mirror.raw } : {}),
|
||||
},
|
||||
runtimeAlignment,
|
||||
targetValidation,
|
||||
disclosure: {
|
||||
defaultView: "compact-low-noise",
|
||||
full: options.full,
|
||||
raw: options.raw,
|
||||
stdoutTailOmitted: !(options.full || options.raw),
|
||||
rawGitMirrorOmitted: !options.raw,
|
||||
expandWith: "bun scripts/cli.ts agentrun v01 control-plane status --full",
|
||||
rawWith: "bun scripts/cli.ts agentrun v01 control-plane status --raw",
|
||||
expandWith: `bun scripts/cli.ts agentrun v01 control-plane status${statusTargetArg(options, target)} --full`,
|
||||
rawWith: `bun scripts/cli.ts agentrun v01 control-plane status${statusTargetArg(options, target)} --raw`,
|
||||
},
|
||||
next: {
|
||||
statusByPipelineRun: pipelineRun ? `bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run ${pipelineRun} --full` : null,
|
||||
statusBySourceCommit: sourceCommit ? `bun scripts/cli.ts agentrun v01 control-plane status --source-commit ${sourceCommit} --full` : null,
|
||||
taskRuns: pipelineRun ? `trans G14:k3s kubectl get taskrun -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} -o wide` : null,
|
||||
logs: pipelineRun ? `trans G14:k3s logs -n ${ciNamespace} -l tekton.dev/pipelineRun=${pipelineRun} --tail 120` : null,
|
||||
triggerCurrent: "bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
|
||||
refresh: "bun scripts/cli.ts agentrun v01 control-plane refresh --confirm",
|
||||
},
|
||||
@@ -935,8 +1027,12 @@ function statusScript(pipelineRun: string | null): string {
|
||||
"const fs = require('node:fs');",
|
||||
"const pr = JSON.parse(fs.readFileSync('/tmp/agentrun-v01-pipelinerun.json', 'utf8'));",
|
||||
"const condition = pr?.status?.conditions?.[0] || {};",
|
||||
"const params = Array.isArray(pr?.spec?.params) ? pr.spec.params : [];",
|
||||
"const sourceCommit = params.find((entry) => entry?.name === 'revision')?.value || null;",
|
||||
"console.log(JSON.stringify({",
|
||||
" name: pr?.metadata?.name || null,",
|
||||
" sourceCommit,",
|
||||
" createdAt: pr?.metadata?.creationTimestamp || null,",
|
||||
" status: condition.status || null,",
|
||||
" reason: condition.reason || null,",
|
||||
" message: condition.message || null,",
|
||||
@@ -1709,6 +1805,79 @@ function pipelineRunName(sourceCommit: string): string {
|
||||
return `agentrun-v01-ci-${sourceCommit.slice(0, 12)}`;
|
||||
}
|
||||
|
||||
function isAgentRunPipelineRunName(value: string): boolean {
|
||||
return /^agentrun-v01-ci-[0-9a-f]{12,40}(?:-[a-z0-9-]+)?$/u.test(value);
|
||||
}
|
||||
|
||||
function statusTargetArg(options: StatusOptions, target: Record<string, unknown>): string {
|
||||
if (options.pipelineRun !== null) return ` --pipeline-run ${options.pipelineRun}`;
|
||||
if (options.sourceCommit !== null) return ` --source-commit ${options.sourceCommit}`;
|
||||
const sourceCommit = stringOrNull(target.sourceCommit);
|
||||
return sourceCommit ? ` --source-commit ${sourceCommit}` : "";
|
||||
}
|
||||
|
||||
function buildAgentRunTargetValidation(input: {
|
||||
pipelineRun: string | null;
|
||||
pipelineRunCondition: Record<string, unknown>;
|
||||
sourceCommit: string | null;
|
||||
targetIsLatestSource: boolean;
|
||||
managerSourceMatchesExpected: boolean;
|
||||
}): Record<string, unknown> {
|
||||
const pipelineRunExists = input.pipelineRun !== null && stringOrNull(input.pipelineRunCondition.name) !== null;
|
||||
const pipelineStatus = stringOrNull(input.pipelineRunCondition.status);
|
||||
const pipelineReason = stringOrNull(input.pipelineRunCondition.reason);
|
||||
const pipelineRunSourceCommit = stringOrNull(input.pipelineRunCondition.sourceCommit);
|
||||
const sourceMatchesPipelineRun = input.sourceCommit === null || pipelineRunSourceCommit === null ? null : input.sourceCommit === pipelineRunSourceCommit;
|
||||
let state = "unknown";
|
||||
const blockers: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
if (input.pipelineRun === null) {
|
||||
blockers.push("pipeline-run-unresolved");
|
||||
} else if (!pipelineRunExists) {
|
||||
state = "missing-pipelinerun";
|
||||
blockers.push("pipeline-run-not-found");
|
||||
} else if (sourceMatchesPipelineRun === false) {
|
||||
state = "source-mismatch";
|
||||
blockers.push("source-commit-does-not-match-pipelinerun-param");
|
||||
} else if (pipelineStatus === "True") {
|
||||
state = input.targetIsLatestSource && input.managerSourceMatchesExpected ? "current-succeeded" : "historical-succeeded";
|
||||
} else if (pipelineStatus === "False") {
|
||||
state = "pipeline-failed";
|
||||
blockers.push(pipelineReason ?? "pipeline-failed");
|
||||
} else if (pipelineStatus === "Unknown") {
|
||||
state = "pipeline-running";
|
||||
} else {
|
||||
state = "pipeline-status-unknown";
|
||||
warnings.push("pipeline-condition-missing");
|
||||
}
|
||||
|
||||
if (pipelineRunExists && input.targetIsLatestSource && !input.managerSourceMatchesExpected) {
|
||||
warnings.push("runtime-manager-source-not-yet-aligned");
|
||||
}
|
||||
if (pipelineRunExists && !input.targetIsLatestSource) {
|
||||
warnings.push("target-is-not-latest-source-head");
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
pipelineRunExists,
|
||||
pipelineStatus,
|
||||
pipelineReason,
|
||||
sourceMatchesPipelineRun,
|
||||
targetIsLatestSource: input.targetIsLatestSource,
|
||||
managerSourceMatchesExpected: input.managerSourceMatchesExpected,
|
||||
blockers,
|
||||
warnings,
|
||||
interruptedOrUnknown: state === "pipeline-status-unknown" || state === "missing-pipelinerun",
|
||||
nextActions: {
|
||||
inspectPipelineRun: input.pipelineRun ? `bun scripts/cli.ts agentrun v01 control-plane status --pipeline-run ${input.pipelineRun} --full` : null,
|
||||
inspectSourceCommit: input.sourceCommit ? `bun scripts/cli.ts agentrun v01 control-plane status --source-commit ${input.sourceCommit} --full` : null,
|
||||
triggerCurrent: "bun scripts/cli.ts agentrun v01 control-plane trigger-current --confirm",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function matchLine(text: string, prefix: string): string | null {
|
||||
const line = text.split(/\r?\n/u).find((item) => item.startsWith(prefix));
|
||||
return line ? line.slice(prefix.length).trim() || null : null;
|
||||
|
||||
Reference in New Issue
Block a user