528 lines
25 KiB
TypeScript
528 lines
25 KiB
TypeScript
// SPEC: GH-1548/GH-1560 archived Gitea Actions POC.
|
|
// Responsibility: read-only historical plan/status for the retired Gitea Actions POC.
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
import { isAbsolute } from "node:path";
|
|
import { rootPath, type UniDeskConfig } from "./config";
|
|
import { renderMachine } from "./cicd-render";
|
|
import type { RenderedCliResult } from "./output";
|
|
|
|
const DEFAULT_CONFIG_PATH = "config/cicd-gitea-actions-poc.yaml";
|
|
|
|
type OutputMode = "text" | "json" | "yaml";
|
|
type Action = "plan" | "status" | "help";
|
|
|
|
interface Options {
|
|
action: Action;
|
|
configPath: string;
|
|
output: OutputMode;
|
|
targetId: string | null;
|
|
}
|
|
|
|
interface LoadedPoc {
|
|
configPath: string;
|
|
root: Record<string, unknown>;
|
|
spec: Record<string, unknown>;
|
|
errors: string[];
|
|
warnings: string[];
|
|
}
|
|
|
|
export function cicdGiteaActionsPocHelp(): unknown {
|
|
return {
|
|
command: "cicd gitea-actions-poc plan|status",
|
|
archived: true,
|
|
replacement: "bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01",
|
|
output: "text by default; use --json, --raw, or -o json|yaml for machine output",
|
|
usage: [
|
|
"bun scripts/cli.ts cicd gitea-actions-poc plan",
|
|
"bun scripts/cli.ts cicd gitea-actions-poc status",
|
|
"bun scripts/cli.ts cicd gitea-actions-poc plan --target agentrun-jd01-v02",
|
|
],
|
|
config: DEFAULT_CONFIG_PATH,
|
|
issue: "https://github.com/pikasTech/unidesk/issues/1560",
|
|
description: "Archived read-only POC. JD01 production CI/CD uses Gitea mirror -> Pipelines-as-Code -> Tekton -> Argo/k8s runtime; do not use Gitea Actions or act_runner as a fallback.",
|
|
};
|
|
}
|
|
|
|
export async function runGiteaActionsPocCommand(_config: UniDeskConfig | null, args: string[], alias = "gitea-actions-poc"): Promise<RenderedCliResult> {
|
|
const options = parseOptions(args);
|
|
const command = `cicd ${alias}${options.action === "help" ? "" : ` ${options.action}`}`;
|
|
if (options.action === "help") return renderMachine(command, cicdGiteaActionsPocHelp(), options.output === "yaml" ? "yaml" : "json");
|
|
const loaded = loadPoc(options.configPath);
|
|
const payload = options.action === "plan" ? buildPlan(loaded, options) : buildStatus(loaded, options);
|
|
if (options.output === "json") return renderMachine(command, payload, "json", payload.ok !== false);
|
|
if (options.output === "yaml") return renderMachine(command, payload, "yaml", payload.ok !== false);
|
|
return {
|
|
ok: payload.ok !== false,
|
|
command,
|
|
renderedText: options.action === "plan" ? renderPlanHuman(payload) : renderStatusHuman(payload),
|
|
contentType: "text/plain",
|
|
};
|
|
}
|
|
|
|
function parseOptions(args: string[]): Options {
|
|
const actionToken = args[0];
|
|
const action: Action = actionToken === undefined || isHelpToken(actionToken) ? "help" : parseAction(actionToken);
|
|
const options: Options = {
|
|
action,
|
|
configPath: DEFAULT_CONFIG_PATH,
|
|
output: "text",
|
|
targetId: null,
|
|
};
|
|
for (let index = action === "help" ? 0 : 1; index < args.length; index += 1) {
|
|
const arg = args[index];
|
|
if (arg === undefined || isHelpToken(arg)) {
|
|
options.action = "help";
|
|
continue;
|
|
}
|
|
if (arg === "--json" || arg === "--raw") {
|
|
options.output = "json";
|
|
continue;
|
|
}
|
|
if (arg === "-o" || arg === "--output") {
|
|
const value = args[index + 1];
|
|
if (value === undefined) throw new Error(`${arg} requires text, json, or yaml`);
|
|
options.output = parseOutput(value, arg);
|
|
index += 1;
|
|
continue;
|
|
}
|
|
if (arg.startsWith("-o=")) {
|
|
options.output = parseOutput(arg.slice(3), "-o");
|
|
continue;
|
|
}
|
|
if (arg.startsWith("--output=")) {
|
|
options.output = parseOutput(arg.slice("--output=".length), "--output");
|
|
continue;
|
|
}
|
|
if (arg === "--config") {
|
|
const value = args[index + 1];
|
|
if (value === undefined || value.length === 0) throw new Error("--config requires a path");
|
|
options.configPath = value;
|
|
index += 1;
|
|
continue;
|
|
}
|
|
if (arg.startsWith("--config=")) {
|
|
options.configPath = arg.slice("--config=".length);
|
|
continue;
|
|
}
|
|
if (arg === "--target") {
|
|
const value = args[index + 1];
|
|
if (value === undefined || value.length === 0) throw new Error("--target requires a target id");
|
|
options.targetId = value;
|
|
index += 1;
|
|
continue;
|
|
}
|
|
if (arg.startsWith("--target=")) {
|
|
options.targetId = arg.slice("--target=".length);
|
|
continue;
|
|
}
|
|
if (arg === "--confirm") throw new Error("cicd gitea-actions-poc is read-only in GH-1548 P1/P2; --confirm is not accepted");
|
|
if (arg === "--dry-run" || arg === "--all") continue;
|
|
throw new Error(`unsupported cicd gitea-actions-poc option: ${arg}`);
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function parseAction(value: string): Action {
|
|
if (value === "plan" || value === "status") return value;
|
|
if (isHelpToken(value)) return "help";
|
|
if (value === "apply" || value === "run-once" || value === "trigger-current") {
|
|
throw new Error(`cicd gitea-actions-poc ${value} is intentionally unavailable; GH-1548 first stage is read-only plan/status`);
|
|
}
|
|
throw new Error("cicd gitea-actions-poc usage: cicd gitea-actions-poc plan|status [--target <id>] [--config <path>]");
|
|
}
|
|
|
|
function parseOutput(value: string, flag: string): OutputMode {
|
|
if (value === "text" || value === "json" || value === "yaml") return value;
|
|
throw new Error(`${flag} must be text, json, or yaml`);
|
|
}
|
|
|
|
function isHelpToken(value: string): boolean {
|
|
return value === "help" || value === "--help" || value === "-h";
|
|
}
|
|
|
|
function loadPoc(configPath: string): LoadedPoc {
|
|
const absolutePath = isAbsolute(configPath) ? configPath : rootPath(configPath);
|
|
if (!existsSync(absolutePath)) throw new Error(`${configPath} does not exist`);
|
|
const root = record(Bun.YAML.parse(readFileSync(absolutePath, "utf8")), configPath);
|
|
const spec = record(root.spec, `${configPath}.spec`);
|
|
const errors: string[] = [];
|
|
const warnings: string[] = [];
|
|
validateRoot(root, spec, configPath, errors, warnings);
|
|
return { configPath, root, spec, errors, warnings };
|
|
}
|
|
|
|
function validateRoot(root: Record<string, unknown>, spec: Record<string, unknown>, configPath: string, errors: string[], warnings: string[]): void {
|
|
if (stringOrNull(root.kind) !== "CicdGiteaActionsPoc") errors.push(`${configPath}.kind must be CicdGiteaActionsPoc`);
|
|
const scope = optionalRecord(spec.scope);
|
|
if (scope?.productionFollowerReplacement !== false) errors.push(`${configPath}.spec.scope.productionFollowerReplacement must be false for first-stage POC`);
|
|
if (scope?.rolloutEnabled !== false) errors.push(`${configPath}.spec.scope.rolloutEnabled must be false for first-stage POC`);
|
|
const runtimePlane = optionalRecord(spec.runtimePlane);
|
|
if (runtimePlane?.dockerAllowed !== false) errors.push(`${configPath}.spec.runtimePlane.dockerAllowed must be false`);
|
|
if (runtimePlane?.buildAllowed !== false) errors.push(`${configPath}.spec.runtimePlane.buildAllowed must be false`);
|
|
if (runtimePlane?.dockerSocketAllowed !== false) errors.push(`${configPath}.spec.runtimePlane.dockerSocketAllowed must be false`);
|
|
const buildPlane = optionalRecord(spec.buildPlane);
|
|
if (buildPlane?.dockerAllowed !== true) errors.push(`${configPath}.spec.buildPlane.dockerAllowed must be true for the controlled builder POC`);
|
|
if (buildPlane?.forbidMasterServer !== true) errors.push(`${configPath}.spec.buildPlane.forbidMasterServer must be true`);
|
|
if (buildPlane?.forbidRuntimeNode !== true) errors.push(`${configPath}.spec.buildPlane.forbidRuntimeNode must be true`);
|
|
const reuse = optionalRecord(spec.reuse);
|
|
if (reuse?.p0NoRegression !== true) errors.push(`${configPath}.spec.reuse.p0NoRegression must be true`);
|
|
if (reuse?.ciConsumptionRequired !== true) errors.push(`${configPath}.spec.reuse.ciConsumptionRequired must be true`);
|
|
const decisions = stringArray(reuse?.requiredDecisions);
|
|
if (!decisions.includes("skipImageBuild")) errors.push(`${configPath}.spec.reuse.requiredDecisions must include skipImageBuild`);
|
|
if (!decisions.includes("reuseEnvImage")) errors.push(`${configPath}.spec.reuse.requiredDecisions must include reuseEnvImage`);
|
|
const targets = arrayRecords(spec.targets);
|
|
if (targets.length === 0) errors.push(`${configPath}.spec.targets must declare at least one POC target`);
|
|
const enabledTargets = targets.filter((target) => target.enabled !== false);
|
|
if (enabledTargets.length === 0) warnings.push(`${configPath}.spec.targets has no enabled target`);
|
|
if (arrayRecords(spec.componentSurvey).length === 0) warnings.push(`${configPath}.spec.componentSurvey is empty; component maturity evidence will be invisible`);
|
|
}
|
|
|
|
function buildPlan(loaded: LoadedPoc, options: Options): Record<string, unknown> {
|
|
const targets = selectedTargets(loaded, options);
|
|
const scope = optionalRecord(loaded.spec.scope);
|
|
return {
|
|
ok: loaded.errors.length === 0 && targets.errors.length === 0,
|
|
action: "plan",
|
|
configPath: loaded.configPath,
|
|
issue: stringOrNull(optionalRecord(loaded.root.metadata)?.issue),
|
|
specRef: stringOrNull(optionalRecord(loaded.root.metadata)?.specRef),
|
|
phase: stringOrNull(scope?.phase),
|
|
rolloutEnabled: scope?.rolloutEnabled === true,
|
|
productionFollowerReplacement: scope?.productionFollowerReplacement === true,
|
|
migration: compactMigration(optionalRecord(loaded.spec.migration)),
|
|
sourceAuthority: compactSourceAuthority(optionalRecord(loaded.spec.sourceAuthority)),
|
|
planes: [
|
|
compactRuntimePlane(optionalRecord(loaded.spec.runtimePlane)),
|
|
compactBuildPlane(optionalRecord(loaded.spec.buildPlane)),
|
|
],
|
|
reuse: compactReuse(optionalRecord(loaded.spec.reuse)),
|
|
targets: targets.items.map(compactTarget),
|
|
stages: arrayRecords(loaded.spec.stages).map(compactStage),
|
|
componentSurvey: arrayRecords(loaded.spec.componentSurvey).map(compactComponent),
|
|
statusProjection: compactStatusProjection(optionalRecord(loaded.spec.statusProjection)),
|
|
budgets: loaded.spec.budgets ?? null,
|
|
errors: [...loaded.errors, ...targets.errors],
|
|
warnings: loaded.warnings,
|
|
valuesRedacted: true,
|
|
next: nextCommands(loaded, targets.items[0]),
|
|
};
|
|
}
|
|
|
|
function buildStatus(loaded: LoadedPoc, options: Options): Record<string, unknown> {
|
|
const plan = buildPlan(loaded, options);
|
|
const targets = arrayRecords(plan.targets);
|
|
return {
|
|
...plan,
|
|
action: "status",
|
|
statusSource: "config-only",
|
|
statusMode: "declared-poc-not-applied",
|
|
checks: targets.map((target) => ({
|
|
target: target.id,
|
|
source: "declared",
|
|
giteaMirror: "not-applied",
|
|
actionsRun: "not-applied",
|
|
tekton: "existing-component",
|
|
builderPlane: "declared-controlled-docker",
|
|
argo: "existing-component",
|
|
runtimePlane: "zero-docker-required",
|
|
reuse: "p0-required-not-yet-proven-in-poc",
|
|
})),
|
|
};
|
|
}
|
|
|
|
function selectedTargets(loaded: LoadedPoc, options: Options): { items: Record<string, unknown>[]; errors: string[] } {
|
|
const targets = arrayRecords(loaded.spec.targets);
|
|
if (options.targetId === null) return { items: targets, errors: [] };
|
|
const selected = targets.filter((target) => stringOrNull(target.id) === options.targetId);
|
|
return selected.length > 0 ? { items: selected, errors: [] } : { items: [], errors: [`target ${options.targetId} not found in ${loaded.configPath}`] };
|
|
}
|
|
|
|
function compactSourceAuthority(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
const mirror = optionalRecord(value?.giteaMirror);
|
|
return {
|
|
mode: stringOrNull(value?.mode),
|
|
allowMutableBranchAsCiSource: value?.allowMutableBranchAsCiSource === true,
|
|
allowHostWorktree: value?.allowHostWorktree === true,
|
|
existingMirrorRef: stringOrNull(value?.existingMirrorRef),
|
|
giteaMirrorEnabledForPoc: mirror?.enabledForPoc === true,
|
|
giteaInternalBaseUrl: stringOrNull(mirror?.internalBaseUrl),
|
|
snapshotRefPrefix: stringOrNull(mirror?.snapshotRefPrefix),
|
|
repositories: arrayRecords(mirror?.repositories).map((repo) => `${repo.repository ?? "-"}@${repo.upstreamBranch ?? "-"}`),
|
|
};
|
|
}
|
|
|
|
function compactMigration(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
const branchFollower = optionalRecord(value?.branchFollower);
|
|
return {
|
|
issue: stringOrNull(value?.issue),
|
|
primaryEntrypoint: stringOrNull(value?.primaryEntrypoint),
|
|
replacementTarget: stringOrNull(value?.replacementTarget),
|
|
branchFollower: {
|
|
status: stringOrNull(branchFollower?.status),
|
|
mode: stringOrNull(branchFollower?.mode),
|
|
reason: stringOrNull(branchFollower?.reason),
|
|
allowedDuringMigration: stringArray(branchFollower?.allowedDuringMigration),
|
|
frozenCapabilities: stringArray(branchFollower?.frozenCapabilities),
|
|
finalDisposition: stringOrNull(branchFollower?.finalDisposition),
|
|
},
|
|
};
|
|
}
|
|
|
|
function compactRuntimePlane(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
return {
|
|
plane: "runtime",
|
|
dockerAllowed: value?.dockerAllowed === true,
|
|
buildAllowed: value?.buildAllowed === true,
|
|
dockerSocketAllowed: value?.dockerSocketAllowed === true,
|
|
hostWorktreeAllowed: value?.hostWorktreeAllowed === true,
|
|
sourceAuthority: stringOrNull(value?.sourceAuthority),
|
|
deployMode: stringOrNull(value?.deployMode),
|
|
statusAuthority: stringArray(value?.statusAuthority),
|
|
};
|
|
}
|
|
|
|
function compactBuildPlane(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
return {
|
|
plane: "ci-build",
|
|
dockerAllowed: value?.dockerAllowed === true,
|
|
buildAllowed: value?.buildAllowed === true,
|
|
dockerScope: stringOrNull(value?.dockerScope),
|
|
mode: stringOrNull(value?.mode),
|
|
engineCandidates: stringArray(value?.engineCandidates),
|
|
selectedEngineForPoc: stringOrNull(value?.selectedEngineForPoc),
|
|
forbidMasterServer: value?.forbidMasterServer === true,
|
|
forbidRuntimeNode: value?.forbidRuntimeNode === true,
|
|
endpointRef: stringOrNull(optionalRecord(value?.endpoint)?.ref),
|
|
registryRef: stringOrNull(optionalRecord(value?.registry)?.ref),
|
|
provenanceRequired: optionalRecord(value?.provenance)?.required === true,
|
|
provenanceFields: stringArray(optionalRecord(value?.provenance)?.fields),
|
|
};
|
|
}
|
|
|
|
function compactReuse(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
return {
|
|
p0NoRegression: value?.p0NoRegression === true,
|
|
sourceTruth: stringOrNull(value?.sourceTruth),
|
|
sourceRead: stringOrNull(value?.sourceRead),
|
|
existingParser: stringOrNull(value?.existingParser),
|
|
existingAgentRunPlanner: stringOrNull(value?.existingAgentRunPlanner),
|
|
ciConsumptionRequired: value?.ciConsumptionRequired === true,
|
|
requiredDecisions: stringArray(value?.requiredDecisions),
|
|
requiredArtifacts: stringArray(value?.requiredArtifacts),
|
|
noRegressionChecks: stringArray(value?.noRegressionChecks),
|
|
};
|
|
}
|
|
|
|
function compactTarget(value: Record<string, unknown>): Record<string, unknown> {
|
|
return {
|
|
id: stringOrNull(value.id),
|
|
enabled: value.enabled !== false,
|
|
repository: stringOrNull(value.repository),
|
|
branch: stringOrNull(value.branch),
|
|
node: stringOrNull(value.node),
|
|
lane: stringOrNull(value.lane),
|
|
baselineSeconds: optionalRecord(value.baseline)?.currentBranchFollowerSeconds ?? null,
|
|
budgetRef: stringOrNull(optionalRecord(value.baseline)?.budgetRef),
|
|
snapshotRef: stringOrNull(optionalRecord(value.source)?.snapshotRef),
|
|
mirrorReadUrlRef: stringOrNull(optionalRecord(value.source)?.currentMirrorReadUrlRef),
|
|
workflowRef: stringOrNull(optionalRecord(value.actions)?.workflowRef),
|
|
pipelineRef: stringOrNull(optionalRecord(value.tekton)?.pipelineRef),
|
|
argoApplicationRef: stringOrNull(optionalRecord(value.argo)?.applicationRef),
|
|
runtimeNamespaceRef: stringOrNull(optionalRecord(value.runtime)?.namespaceRef),
|
|
workload: stringOrNull(optionalRecord(value.runtime)?.workload),
|
|
healthPath: stringOrNull(optionalRecord(value.closeout)?.healthPath),
|
|
requiredEvidence: stringArray(optionalRecord(value.closeout)?.requiredEvidence),
|
|
};
|
|
}
|
|
|
|
function compactStage(value: Record<string, unknown>): Record<string, unknown> {
|
|
return {
|
|
id: stringOrNull(value.id),
|
|
owner: stringOrNull(value.owner),
|
|
statusAuthority: stringOrNull(value.statusAuthority),
|
|
output: stringOrNull(value.output),
|
|
};
|
|
}
|
|
|
|
function compactComponent(value: Record<string, unknown>): Record<string, unknown> {
|
|
return {
|
|
component: stringOrNull(value.component),
|
|
role: stringOrNull(value.role),
|
|
maturity: stringOrNull(value.maturity),
|
|
directReuse: stringOrNull(value.directReuse),
|
|
docs: stringOrNull(value.docs),
|
|
risk: stringOrNull(value.risk),
|
|
};
|
|
}
|
|
|
|
function compactStatusProjection(value: Record<string, unknown> | null): Record<string, unknown> {
|
|
return {
|
|
mode: stringOrNull(value?.mode),
|
|
defaultOutputMustNotDump: value?.defaultOutputMustNotDump === true,
|
|
requiredFields: stringArray(value?.requiredFields),
|
|
drillDown: optionalRecord(value?.drillDown) ?? {},
|
|
};
|
|
}
|
|
|
|
function nextCommands(loaded: LoadedPoc, firstTarget: Record<string, unknown> | undefined): Record<string, unknown> {
|
|
const declared = optionalRecord(loaded.spec.next);
|
|
const targetId = stringOrNull(firstTarget?.id) ?? "agentrun-jd01-v02";
|
|
return {
|
|
plan: stringOrNull(declared?.plan) ?? "bun scripts/cli.ts cicd gitea-actions-poc plan",
|
|
status: stringOrNull(declared?.status) ?? "bun scripts/cli.ts cicd gitea-actions-poc status",
|
|
existingFollowerStatus: stringOrNull(declared?.existingFollowerStatus) ?? `bun scripts/cli.ts cicd branch-follower status --follower ${targetId}`,
|
|
pocIssue: stringOrNull(declared?.pocIssue),
|
|
};
|
|
}
|
|
|
|
function renderPlanHuman(payload: Record<string, unknown>): string {
|
|
const next = optionalRecord(payload.next);
|
|
const planes = arrayRecords(payload.planes);
|
|
const reuse = optionalRecord(payload.reuse);
|
|
const migration = optionalRecord(payload.migration);
|
|
const branchFollower = optionalRecord(migration?.branchFollower);
|
|
const statusProjection = optionalRecord(payload.statusProjection);
|
|
const errors = stringArray(payload.errors);
|
|
const warnings = stringArray(payload.warnings);
|
|
return [
|
|
`CI/CD GITEA-ACTIONS POC PLAN (${payload.ok === false ? "blocked" : "ok"})`,
|
|
"",
|
|
"ARCHIVE NOTICE",
|
|
"This is historical GH-1548 POC material. Current JD01 CI/CD closeout is platform-infra pipelines-as-code status/history; Gitea Actions and act_runner are not fallback paths.",
|
|
"",
|
|
table(["TARGET", "SOURCE", "NODE/LANE", "BASELINE", "SNAPSHOT", "TEKTON", "ARGO", "RUNTIME"], arrayRecords(payload.targets).map((target) => [
|
|
target.id,
|
|
`${target.repository ?? "-"}@${target.branch ?? "-"}`,
|
|
`${target.node ?? "-"}/${target.lane ?? "-"}`,
|
|
target.baselineSeconds === null ? "-" : `${target.baselineSeconds}s`,
|
|
target.snapshotRef,
|
|
target.pipelineRef,
|
|
target.argoApplicationRef,
|
|
target.workload,
|
|
])),
|
|
"",
|
|
"SOURCE AUTHORITY",
|
|
sourceAuthorityLine(optionalRecord(payload.sourceAuthority)),
|
|
"",
|
|
"MIGRATION POLICY",
|
|
`replacement=${migration?.replacementTarget ?? "-"} primary=${migration?.primaryEntrypoint ?? "-"} branchFollower=${branchFollower?.status ?? "-"}:${branchFollower?.mode ?? "-"}`,
|
|
`frozen=${stringArray(branchFollower?.frozenCapabilities).join(",") || "-"}`,
|
|
"",
|
|
table(["PLANE", "DOCKER", "BUILDS", "MODE", "ENGINE", "MASTER", "RUNTIME_NODE", "AUTHORITY"], planes.map((plane) => [
|
|
plane.plane,
|
|
boolText(plane.dockerAllowed),
|
|
boolText(plane.buildAllowed),
|
|
plane.mode ?? plane.deployMode ?? "-",
|
|
plane.selectedEngineForPoc ?? "-",
|
|
plane.forbidMasterServer === true ? "forbidden" : "-",
|
|
plane.forbidRuntimeNode === true ? "forbidden" : "-",
|
|
plane.sourceAuthority ?? plane.endpointRef ?? "-",
|
|
])),
|
|
"",
|
|
"REUSE CONTRACT",
|
|
`source=${reuse?.sourceTruth ?? "-"} p0=${boolText(reuse?.p0NoRegression)} ciConsumption=${boolText(reuse?.ciConsumptionRequired)} decisions=${stringArray(reuse?.requiredDecisions).join(",") || "-"}`,
|
|
`artifacts=${stringArray(reuse?.requiredArtifacts).join(",") || "-"}`,
|
|
"",
|
|
table(["STAGE", "OWNER", "STATUS_AUTHORITY", "OUTPUT"], arrayRecords(payload.stages).map((stage) => [stage.id, stage.owner, stage.statusAuthority, stage.output])),
|
|
"",
|
|
table(["COMPONENT", "ROLE", "MATURITY", "DIRECT_REUSE", "RISK"], arrayRecords(payload.componentSurvey).map((component) => [component.component, component.role, component.maturity, component.directReuse, component.risk])),
|
|
"",
|
|
"STATUS FIELDS",
|
|
stringArray(statusProjection?.requiredFields).join(", ") || "-",
|
|
warnings.length === 0 ? "" : `\nWARNINGS\n${warnings.map((item) => `- ${item}`).join("\n")}`,
|
|
errors.length === 0 ? "" : `\nERRORS\n${errors.map((item) => `- ${item}`).join("\n")}`,
|
|
"",
|
|
"NEXT",
|
|
`pac-status: bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01`,
|
|
`pac-history: bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 --limit 10`,
|
|
`archived-status: ${next?.status ?? "-"}`,
|
|
`legacy-follower: ${next?.existingFollowerStatus ?? "-"}`,
|
|
`issue: ${next?.pocIssue ?? payload.issue ?? "-"}`,
|
|
"",
|
|
].filter((line) => line !== "").join("\n");
|
|
}
|
|
|
|
function renderStatusHuman(payload: Record<string, unknown>): string {
|
|
const next = optionalRecord(payload.next);
|
|
const migration = optionalRecord(payload.migration);
|
|
const branchFollower = optionalRecord(migration?.branchFollower);
|
|
const errors = stringArray(payload.errors);
|
|
return [
|
|
`CI/CD GITEA-ACTIONS POC STATUS (${payload.ok === false ? "blocked" : "declared-only"})`,
|
|
"",
|
|
"ARCHIVE NOTICE",
|
|
"This command reads an archived POC spec only. Use platform-infra pipelines-as-code status/history for current JD01 CI/CD evidence.",
|
|
"",
|
|
`statusSource=${payload.statusSource ?? "-"} mode=${payload.statusMode ?? "-"}`,
|
|
`replacement=${migration?.replacementTarget ?? "-"} branchFollower=${branchFollower?.status ?? "-"}:${branchFollower?.mode ?? "-"}`,
|
|
"",
|
|
table(["TARGET", "SOURCE", "GITEA", "ACTIONS", "TEKTON", "BUILDER", "ARGO", "RUNTIME", "REUSE"], arrayRecords(payload.checks).map((check) => [
|
|
check.target,
|
|
check.source,
|
|
check.giteaMirror,
|
|
check.actionsRun,
|
|
check.tekton,
|
|
check.builderPlane,
|
|
check.argo,
|
|
check.runtimePlane,
|
|
check.reuse,
|
|
])),
|
|
errors.length === 0 ? "" : `\nERRORS\n${errors.map((item) => `- ${item}`).join("\n")}`,
|
|
"",
|
|
"NEXT",
|
|
`pac-status: bun scripts/cli.ts platform-infra pipelines-as-code status --target JD01`,
|
|
`pac-history: bun scripts/cli.ts platform-infra pipelines-as-code history --target JD01 --limit 10`,
|
|
`archived-plan: ${next?.plan ?? "-"}`,
|
|
`legacy-follower: ${next?.existingFollowerStatus ?? "-"}`,
|
|
"",
|
|
].filter((line) => line !== "").join("\n");
|
|
}
|
|
|
|
function sourceAuthorityLine(value: Record<string, unknown> | null): string {
|
|
return [
|
|
`mode=${value?.mode ?? "-"}`,
|
|
`mutableBranch=${boolText(value?.allowMutableBranchAsCiSource)}`,
|
|
`hostWorktree=${boolText(value?.allowHostWorktree)}`,
|
|
`giteaEnabled=${boolText(value?.giteaMirrorEnabledForPoc)}`,
|
|
`snapshot=${value?.snapshotRefPrefix ?? "-"}`,
|
|
].join(" ");
|
|
}
|
|
|
|
function record(value: unknown, path: string): Record<string, unknown> {
|
|
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be a YAML object`);
|
|
return value as Record<string, unknown>;
|
|
}
|
|
|
|
function optionalRecord(value: unknown): Record<string, unknown> | null {
|
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
}
|
|
|
|
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 stringArray(value: unknown): string[] {
|
|
return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string" && item.length > 0) : [];
|
|
}
|
|
|
|
function stringOrNull(value: unknown): string | null {
|
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
}
|
|
|
|
function boolText(value: unknown): string {
|
|
return value === true ? "true" : value === false ? "false" : "-";
|
|
}
|
|
|
|
function table(headers: readonly string[], rows: readonly (readonly unknown[])[]): string {
|
|
const normalized = rows.map((row) => headers.map((_, index) => cell(row[index])));
|
|
const widths = headers.map((header, index) => Math.max(header.length, ...normalized.map((row) => row[index]?.length ?? 0)));
|
|
const format = (row: readonly string[]) => row.map((value, index) => value.padEnd(widths[index] ?? 0)).join(" ").trimEnd();
|
|
return [format(headers), format(headers.map((header) => "-".repeat(header.length))), ...normalized.map(format)].join("\n");
|
|
}
|
|
|
|
function cell(value: unknown): string {
|
|
if (value === null || value === undefined || value === "") return "-";
|
|
const text = String(value).replace(/\s+/gu, " ");
|
|
return text.length > 96 ? `${text.slice(0, 93)}...` : text;
|
|
}
|