// 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; spec: Record; 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 { 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 ] [--config ]"); } 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, spec: Record, 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 { 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 { 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[]; 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 | null): Record { 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 | null): Record { 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 | null): Record { 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 | null): Record { 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 | null): Record { 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): Record { 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): Record { return { id: stringOrNull(value.id), owner: stringOrNull(value.owner), statusAuthority: stringOrNull(value.statusAuthority), output: stringOrNull(value.output), }; } function compactComponent(value: Record): Record { 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 | null): Record { return { mode: stringOrNull(value?.mode), defaultOutputMustNotDump: value?.defaultOutputMustNotDump === true, requiredFields: stringArray(value?.requiredFields), drillDown: optionalRecord(value?.drillDown) ?? {}, }; } function nextCommands(loaded: LoadedPoc, firstTarget: Record | undefined): Record { 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 { 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 { 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 | 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 { if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error(`${path} must be a YAML object`); return value as Record; } function optionalRecord(value: unknown): Record | null { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : null; } function arrayRecords(value: unknown): Record[] { return Array.isArray(value) ? value.filter((item): item is Record => 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; }