This commit is contained in:
@@ -124,6 +124,11 @@ interface HistoryOptions extends CommonOptions {
|
||||
detailId: string | null;
|
||||
}
|
||||
|
||||
interface CloseoutOptions extends CommonOptions {
|
||||
sourceCommit: string | null;
|
||||
wait: boolean;
|
||||
}
|
||||
|
||||
interface ApplyOptions extends CommonOptions {
|
||||
confirm: boolean;
|
||||
dryRun: boolean;
|
||||
@@ -161,6 +166,11 @@ export async function runPlatformInfraPipelinesAsCodeCommand(config: UniDeskConf
|
||||
const result = await status(config, options);
|
||||
return options.full || options.raw ? result : renderStatus(result);
|
||||
}
|
||||
if (action === "closeout") {
|
||||
const options = parseCloseoutOptions(args.slice(1));
|
||||
const result = await closeout(config, options);
|
||||
return options.full || options.raw ? result : renderCloseout(result);
|
||||
}
|
||||
if (action === "history" || action === "runs") {
|
||||
const options = parseHistoryOptions(args.slice(1));
|
||||
const result = await history(config, options);
|
||||
@@ -176,13 +186,14 @@ export async function runPlatformInfraPipelinesAsCodeCommand(config: UniDeskConf
|
||||
|
||||
function help(): Record<string, unknown> {
|
||||
return {
|
||||
command: "platform-infra pipelines-as-code plan|apply|status|history|webhook-test",
|
||||
command: "platform-infra pipelines-as-code plan|apply|status|closeout|history|webhook-test",
|
||||
configTruth: configLabel,
|
||||
usage: [
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code plan --target JD01",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code apply --target JD01 --dry-run",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code apply --target JD01 --confirm",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01 [--full|--raw]",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code closeout --target JD01 --consumer <consumer> --source-commit <sha> --wait",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 [--consumer hwlab-jd01-v03] [--limit 10]",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 --id <pipelinerun>",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code webhook-test --target JD01 --confirm",
|
||||
@@ -413,6 +424,52 @@ async function status(config: UniDeskConfig, options: CommonOptions): Promise<Re
|
||||
};
|
||||
}
|
||||
|
||||
async function closeout(config: UniDeskConfig, options: CloseoutOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig();
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
const consumer = resolveConsumer(pac, options.consumerId);
|
||||
const startedAt = Date.now();
|
||||
const waitMs = Math.max(1, pac.release.waitTimeoutSeconds) * 1000;
|
||||
let attempts = 0;
|
||||
let current = await status(config, options);
|
||||
attempts += 1;
|
||||
while (options.wait && !closeoutReady(current, options.sourceCommit) && Date.now() - startedAt < waitMs) {
|
||||
await sleep(3000);
|
||||
current = await status(config, options);
|
||||
attempts += 1;
|
||||
}
|
||||
const summary = record(current.summary);
|
||||
const latest = record(summary.latestPipelineRun);
|
||||
const diagnostics = record(summary.diagnostics);
|
||||
const observedSourceCommit = stringValue(latest.sourceCommit) !== "-" ? stringValue(latest.sourceCommit) : stringValue(diagnostics.sourceCommit);
|
||||
const sourceMatched = options.sourceCommit === null || observedSourceCommit === options.sourceCommit;
|
||||
const ready = current.ok === true && sourceMatched;
|
||||
return {
|
||||
ok: ready,
|
||||
action: "platform-infra-pipelines-as-code-closeout",
|
||||
mutation: false,
|
||||
target: targetSummary(target),
|
||||
consumer,
|
||||
sourceCommit: options.sourceCommit,
|
||||
observedSourceCommit,
|
||||
sourceMatched,
|
||||
ready,
|
||||
wait: {
|
||||
requested: options.wait,
|
||||
attempts,
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
budgetSeconds: pac.release.waitTimeoutSeconds,
|
||||
source: `${configLabel}.release.waitTimeoutSeconds`,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
summary,
|
||||
status: current,
|
||||
blocker: ready ? null : closeoutBlocker(current, options.sourceCommit, observedSourceCommit, sourceMatched),
|
||||
next: nextCommands(target.id, consumer.id, pac.defaults.consumerId),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
async function history(config: UniDeskConfig, options: HistoryOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig();
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
@@ -721,6 +778,7 @@ function nextCommands(targetId: string, consumerId: string, defaultConsumerId: s
|
||||
const suffix = consumerSuffix(consumerId, defaultConsumerId);
|
||||
return {
|
||||
apply: `bun scripts/cli.ts platform-infra pipelines-as-code apply --target ${targetId}${suffix} --confirm`,
|
||||
closeout: `bun scripts/cli.ts platform-infra pipelines-as-code closeout --target ${targetId}${suffix} --wait`,
|
||||
status: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${targetId}${suffix}`,
|
||||
history: `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${targetId}${suffix}`,
|
||||
webhookTest: `bun scripts/cli.ts platform-infra pipelines-as-code webhook-test --target ${targetId}${suffix} --confirm`,
|
||||
@@ -819,12 +877,50 @@ function renderStatus(result: Record<string, unknown>): RenderedCliResult {
|
||||
` pipeline-run: ${latest.name === undefined ? "-" : `bun scripts/cli.ts platform-infra pipelines-as-code history --target ${stringValue(record(result.target).id)} --id ${stringValue(latest.name)}`}`,
|
||||
"",
|
||||
"NEXT",
|
||||
` closeout: ${stringValue(record(result.next).closeout)}`,
|
||||
` full: ${stringValue(record(result.next).status)} --full`,
|
||||
` all-history: ${stringValue(record(result.next).history).replace(/ --consumer [^ ]+/u, "")} --limit 10`,
|
||||
];
|
||||
return rendered(result, "platform-infra pipelines-as-code status", lines);
|
||||
}
|
||||
|
||||
function renderCloseout(result: Record<string, unknown>): RenderedCliResult {
|
||||
const target = record(result.target);
|
||||
const consumer = record(result.consumer);
|
||||
const wait = record(result.wait);
|
||||
const summary = record(result.summary);
|
||||
const latest = record(summary.latestPipelineRun);
|
||||
const artifact = record(summary.artifact);
|
||||
const argo = record(summary.argo);
|
||||
const runtime = record(summary.runtime);
|
||||
const diagnostics = record(summary.diagnostics);
|
||||
const blocker = record(result.blocker);
|
||||
const lines = [
|
||||
"PLATFORM-INFRA PIPELINES-AS-CODE CLOSEOUT",
|
||||
...table(["TARGET", "CONSUMER", "READY", "SOURCE_MATCHED", "MUTATION"], [[stringValue(target.id), stringValue(consumer.id), boolText(result.ready), boolText(result.sourceMatched), boolText(result.mutation)]]),
|
||||
"",
|
||||
"WAIT",
|
||||
...table(["REQUESTED", "ATTEMPTS", "ELAPSED_MS", "BUDGET_S", "SOURCE"], [[stringValue(wait.requested), stringValue(wait.attempts), stringValue(wait.elapsedMs), stringValue(wait.budgetSeconds), stringValue(wait.source)]]),
|
||||
"",
|
||||
"SOURCE / PIPELINERUN",
|
||||
...table(["EXPECTED", "OBSERVED", "PIPELINERUN", "STATUS", "DURATION_S"], [[short(stringValue(result.sourceCommit), 16), short(stringValue(result.observedSourceCommit), 16), stringValue(latest.name), stringValue(latest.reason ?? latest.status), stringValue(latest.durationSeconds)]]),
|
||||
"",
|
||||
"RUNTIME",
|
||||
...table(["IMAGE_STATUS", "ENV_REUSE", "DIGEST", "GITOPS", "ARGO", "RUNTIME"], [[stringValue(artifact.imageStatus), stringValue(artifact.envReuse), short(stringValue(artifact.digest), 18), short(stringValue(artifact.gitopsCommit), 12), `${stringValue(argo.sync)}/${stringValue(argo.health)}`, runtimeText(runtime)]]),
|
||||
"",
|
||||
"CICD DIAGNOSIS",
|
||||
...table(["OK", "CODE", "PHASE", "HINT"], [[boolText(diagnostics.ok !== false), stringValue(diagnostics.code), stringValue(diagnostics.phase), compactLine(stringValue(diagnostics.hint))]]),
|
||||
"",
|
||||
Object.keys(blocker).length === 0 ? "BLOCKER\n-" : ["BLOCKER", table(["CODE", "REASON"], [[stringValue(blocker.code), compactLine(stringValue(blocker.reason))]])].join("\n"),
|
||||
"",
|
||||
"NEXT",
|
||||
` status: ${stringValue(record(result.next).status)}`,
|
||||
` history: ${stringValue(record(result.next).history)} --limit 10`,
|
||||
latest.name === undefined ? " pipeline-run: -" : ` pipeline-run: ${stringValue(record(result.next).history)} --id ${stringValue(latest.name)}`,
|
||||
];
|
||||
return rendered(result, "platform-infra pipelines-as-code closeout", lines);
|
||||
}
|
||||
|
||||
function renderHistory(result: Record<string, unknown>): RenderedCliResult {
|
||||
const rows = arrayRecords(result.rows);
|
||||
const config = record(result.config);
|
||||
@@ -912,6 +1008,31 @@ function parseWebhookTestOptions(args: string[]): WebhookTestOptions {
|
||||
return { ...parseCommonOptions(commonArgs), confirm };
|
||||
}
|
||||
|
||||
function parseCloseoutOptions(args: string[]): CloseoutOptions {
|
||||
const commonArgs: string[] = [];
|
||||
let sourceCommit: string | null = null;
|
||||
let wait = false;
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index];
|
||||
if (arg === "--source-commit" || arg === "--commit") {
|
||||
const value = args[index + 1];
|
||||
if (value === undefined || value.startsWith("--")) throw new Error(`${arg} requires a value`);
|
||||
if (!/^[0-9a-f]{40}$/iu.test(value)) throw new Error(`${arg} must be a full git sha`);
|
||||
sourceCommit = value;
|
||||
index += 1;
|
||||
} else if (arg === "--wait") {
|
||||
wait = true;
|
||||
} else {
|
||||
commonArgs.push(arg);
|
||||
if (arg === "--target" || arg === "--node" || arg === "--consumer") {
|
||||
commonArgs.push(args[index + 1] ?? "");
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...parseCommonOptions(commonArgs), sourceCommit, wait };
|
||||
}
|
||||
|
||||
function parseHistoryOptions(args: string[]): HistoryOptions {
|
||||
const commonArgs: string[] = [];
|
||||
let limit = 5;
|
||||
@@ -968,6 +1089,37 @@ function parseCommonOptions(args: string[]): CommonOptions {
|
||||
return { targetId, consumerId, full, raw };
|
||||
}
|
||||
|
||||
function closeoutReady(value: Record<string, unknown>, sourceCommit: string | null): boolean {
|
||||
if (value.ok !== true) return false;
|
||||
if (sourceCommit === null) return true;
|
||||
const summary = record(value.summary);
|
||||
const latest = record(summary.latestPipelineRun);
|
||||
const diagnostics = record(summary.diagnostics);
|
||||
const observedSourceCommit = stringValue(latest.sourceCommit) !== "-" ? stringValue(latest.sourceCommit) : stringValue(diagnostics.sourceCommit);
|
||||
return observedSourceCommit === sourceCommit;
|
||||
}
|
||||
|
||||
function closeoutBlocker(value: Record<string, unknown>, expected: string | null, observed: string, sourceMatched: boolean): Record<string, unknown> {
|
||||
if (!sourceMatched) {
|
||||
return {
|
||||
code: "pac-closeout-source-mismatch",
|
||||
reason: `expected source commit ${expected ?? "-"} but latest observed source commit is ${observed}`,
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const summary = record(value.summary);
|
||||
const diagnostics = record(summary.diagnostics);
|
||||
return {
|
||||
code: stringValue(diagnostics.code, "pac-closeout-not-ready"),
|
||||
reason: stringValue(diagnostics.hint, "PaC status is not ready; inspect status/history for the selected consumer"),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function positiveInteger(obj: Record<string, unknown>, key: string, path: string): number {
|
||||
const value = y.integerField(obj, key, path);
|
||||
if (value < 1) throw new Error(`${configLabel}.${path}.${key} must be positive`);
|
||||
|
||||
Reference in New Issue
Block a user