import type { RenderedCliResult } from "./output"; export function renderMirrorPlan(result: Record): RenderedCliResult { const target = record(result.target); const source = record(result.sourceAuthority); const repos = arrayRecords(result.repositories).map((repo) => [stringValue(repo.key), stringValue(repo.upstreamRepository), stringValue(repo.upstreamBranch), stringValue(repo.readUrl), stringValue(repo.legacyDisposition)]); const responsibilities = arrayRecords(result.responsibilities).map((item) => [stringValue(item.name), stringValue(item.current), stringValue(item.target), stringValue(item.disposition)]); const next = record(result.next); return rendered(result, "platform-infra gitea mirror plan", [ "PLATFORM-INFRA GITEA MIRROR PLAN", ...table(["TARGET", "STAGE", "AUTHORITY", "REPOS"], [[stringValue(target.id), stringValue(source.stage), stringValue(source.statusAuthority), stringValue(source.repositoryCount)]]), "", "REPOSITORIES", ...(repos.length === 0 ? ["-"] : table(["KEY", "UPSTREAM", "BRANCH", "GITEA_READ_URL", "LEGACY"], repos)), "", "RESPONSIBILITIES", ...(responsibilities.length === 0 ? ["-"] : table(["NAME", "CURRENT", "TARGET", "DISPOSITION"], responsibilities)), "", "NEXT", ` status: ${stringValue(next.status)}`, ` full: ${stringValue(next.statusFull)}`, ` webhook-status: ${stringValue(next.webhookStatus)}`, ` fix-automatic-delivery: ${stringValue(next.fixAutomaticDelivery)}`, "", "Disclosure: credentials are summarized by presence/fingerprint only.", ]); } export function renderMirrorBootstrap(result: Record): RenderedCliResult { const repos = arrayRecords(result.repositories).map((repo) => [stringValue(repo.key), stringValue(repo.owner), stringValue(repo.name), boolText(record(repo.create).ok)]); const next = record(result.next); return rendered(result, "platform-infra gitea mirror bootstrap", [ "PLATFORM-INFRA GITEA MIRROR BOOTSTRAP", ...table(["OK", "MUTATION", "TARGET"], [[boolText(result.ok), boolText(result.mutation), stringValue(record(result.target).id)]]), "", "REPOSITORIES", ...(repos.length === 0 ? ["-"] : table(["KEY", "OWNER", "REPO", "API_OK"], repos)), ...remoteErrorLines(result), "", `NEXT ${stringValue(next.webhookStatus)}`, ]); } export function renderMirrorSync(result: Record): RenderedCliResult { const bundles = arrayRecords(result.sourceBundles).map((bundle) => [stringValue(bundle.key), stringValue(bundle.sourceCommit).slice(0, 12), stringValue(bundle.snapshotRef), stringValue(bundle.readUrl)]); const repos = arrayRecords(result.repositories).map((repo) => [ stringValue(repo.key), boolText(repo.syncOk), stringValue(repo.fetchRc), stringValue(repo.pushRc), stringValue(repo.gitopsPushRc), stringValue(repo.sourceCommit).slice(0, 12), compactTail(stringValue(repo.fetchTail) || stringValue(repo.pushTail) || stringValue(repo.revparseTail)), ]); const next = record(result.next); return rendered(result, "platform-infra gitea mirror sync", [ "PLATFORM-INFRA GITEA MIRROR SYNC", ...table(["OK", "MUTATION", "TARGET"], [[boolText(result.ok), boolText(result.mutation), stringValue(record(result.target).id)]]), "", "REPOSITORIES", ...(repos.length === 0 ? ["-"] : table(["KEY", "SYNC", "FETCH", "PUSH", "GITOPS", "COMMIT", "ERROR"], repos)), "", "SOURCE BUNDLES", ...(bundles.length === 0 ? ["-"] : table(["KEY", "COMMIT", "SNAPSHOT_REF", "READ_URL"], bundles)), ...remoteErrorLines(result), "", `NEXT ${stringValue(next.status)}`, ]); } export function renderMirrorStatus(result: Record): RenderedCliResult { const source = record(result.sourceAuthority); const service = record(result.serviceHealth); const repos = arrayRecords(result.repositories).map((repo) => [stringValue(repo.key), stringValue(repo.mirrorState), stringValue(repo.branchCommit).slice(0, 12), stringValue(repo.snapshotCommit).slice(0, 12), stringValue(repo.snapshotRef)]); const next = record(result.next); return rendered(result, "platform-infra gitea mirror status", [ "PLATFORM-INFRA GITEA MIRROR STATUS", ...table(["TARGET", "SERVICE", "MIRROR_READY", "AUTHORITY"], [[stringValue(record(result.target).id), boolText(service.ok), boolText(source.mirrorReady), stringValue(source.statusAuthority)]]), "", "REPOSITORIES", ...(repos.length === 0 ? ["-"] : table(["KEY", "STATE", "BRANCH", "SNAPSHOT", "SNAPSHOT_REF"], repos)), ...remoteErrorLines(result), "", `NEXT ${stringValue(next.webhookStatus)}`, ]); } export function renderMirrorWebhookApply(result: Record): RenderedCliResult { const repos = arrayRecords(result.repositories).map((repo) => [stringValue(repo.key), stringValue(repo.repository), boolText(repo.hookReady), stringValue(repo.hookId), compactTail(stringValue(repo.error))]); const next = record(result.next); return rendered(result, "platform-infra gitea mirror webhook apply", [ "PLATFORM-INFRA GITEA MIRROR WEBHOOK APPLY", ...table(["OK", "MUTATION", "TARGET", "URL"], [[boolText(result.ok), boolText(result.mutation), stringValue(record(result.target).id), stringValue(record(result.webhook).publicUrl)]]), "", "GITHUB HOOKS", ...(repos.length === 0 ? ["-"] : table(["KEY", "REPOSITORY", "READY", "HOOK_ID", "ERROR"], repos)), ...remoteErrorLines(result), "", `NEXT ${stringValue(next.webhookStatus)}`, ]); } export function renderMirrorWebhookStatus(result: Record, disclosure: Record): RenderedCliResult { const repos = arrayRecords(result.repositories).map((repo) => { const latestPush = record(repo.latestPushDelivery); const latest = record(repo.latestDelivery); const delivery = latestPush.event !== "" ? latestPush : latest; const inbox = record(repo.durableInboxRecord); const inboxResult = record(inbox.result); const inboxState = stringValue(repo.durableInboxState, "missing"); const durableProof = repo.preDurableBootstrap === true ? "pre-durable-bootstrap" : repo.durableInboxCommitted === true ? `${stringValue(inboxResult.disposition)}:${stringValue(inboxResult.sourceCommit).slice(0, 12)}` : "-"; return [ stringValue(repo.key), boolText(repo.hookReady), stringValue(repo.githubHead).slice(0, 12), stringValue(repo.branchCommit).slice(0, 12), stringValue(repo.snapshotCommit).slice(0, 12), boolText(repo.authorityMatches), boolText(repo.bridgeEventStale), `${stringValue(repo.deliveryId)}:${stringValue(delivery.statusCode)}:${stringValue(delivery.status)}`, inboxState, durableProof, compactTail(stringValue(repo.error)), ]; }); const bridge = record(result.bridge); const bridgeLogs = record(result.bridgeLogs); const retry = record(record(record(result.webhook).bridge).retry); const retryText = `${stringValue(retry.maxAttempts)}x/${stringValue(retry.initialDelayMs)}-${stringValue(retry.maxDelayMs)}ms`; const selection = record(disclosure.selection); const selectedRepoKey = stringValue(selection.selectedRepoKey, ""); const delivery = arrayRecords(disclosure.deliveries)[0]; const deliveryResult = record(delivery?.result); const retryDetail = record(disclosure.retry); const logs = record(disclosure.logs); const logRows = arrayRecords(logs.events).map((event) => [ stringValue(event.syncAttempt), boolText(event.ok), boolText(event.terminal), stringValue(event.disposition), stringValue(event.elapsedMs), compactLine(stringValue(event.error)), ]); const firstError = record(record(disclosure.diagnostics).firstError); const repositoryCommands = arrayRecords(record(disclosure.next).repositories).map((item) => [ stringValue(item.key), stringValue(item.deliveryId), stringValue(item.command), ]); const drillDown = selectedRepoKey === "" ? [ "", "DRILL-DOWN", ...(repositoryCommands.length === 0 ? ["-"] : table(["REPOSITORY", "DELIVERY", "COMMAND"], repositoryCommands)), ] : [ "", "DELIVERY", ...(delivery === undefined ? ["-"] : table(["ID", "REPOSITORY", "STATE", "ATTEMPTS", "NEXT", "COMMIT", "DISPOSITION"], [[ stringValue(delivery.deliveryId), stringValue(delivery.repo), stringValue(delivery.state), `${stringValue(retryDetail.cycleAttempt)}/${stringValue(retryDetail.totalAttempts)}`, stringValue(retryDetail.nextAttemptAt), stringValue(deliveryResult.sourceCommit).slice(0, 12), stringValue(deliveryResult.disposition), ]])), "", "BRIDGE EVENTS", ...(logRows.length === 0 ? ["-"] : table(["ATTEMPT", "OK", "TERMINAL", "DISPOSITION", "ELAPSED_MS", "ERROR"], logRows)), ...(firstError.message === undefined ? [] : ["", `FIRST ERROR ${stringValue(firstError.domain)}: ${stringValue(firstError.message)}`]), ]; return rendered(disclosure, "platform-infra gitea mirror webhook status", [ "PLATFORM-INFRA GITEA MIRROR WEBHOOK STATUS", ...table(["TARGET", "BRIDGE", "READY", "RETRY", "URL", "LOGS"], [[stringValue(record(result.target).id), stringValue(bridge.deployment), boolText(bridge.ready), retryText, stringValue(record(result.webhook).publicUrl), `rc=${stringValue(bridgeLogs.exitCode)}`]]), "", "GITHUB HOOKS", ...(repos.length === 0 ? ["-"] : table(["KEY", "HOOK", "GH_HEAD", "GITEA", "SNAPSHOT", "MATCH", "STALE", "DELIVERY", "DURABLE", "PROOF", "ERROR"], repos)), ...drillDown, ...remoteErrorLines(result), "", `NEXT ${stringValue(record(disclosure.next).full)}`, ]); } export function renderPlan(result: Record): RenderedCliResult { const config = record(result.config); const target = record(config.target); const app = record(config.app); const migration = record(config.migration); const policy = arrayRecords(result.policy); const failed = policy.filter((item) => item.ok === false); const next = record(result.next); return rendered(result, "platform-infra gitea plan", [ "PLATFORM-INFRA GITEA PLAN", ...table(["FIELD", "VALUE", "DETAIL", "VALUE"], [ ["TARGET", stringValue(target.id), "route", stringValue(target.route)], ["NAMESPACE", stringValue(target.namespace), "role", stringValue(target.role)], ["IMAGE", stringValue(app.image), "replicas", stringValue(app.replicas)], ["SERVICE", `${stringValue(app.serviceName)}:${stringValue(record(app.service).httpPort)}`, "dns", serviceDnsFromObjects(app, target)], ["WEBUI", stringValue(record(app.publicExposure).publicBaseUrl), "internalGit", serviceDnsFromObjects(app, target)], ["ACTIONS", boolText(app.actionsEnabled), "registrationDisabled", boolText(app.registrationDisabled)], ["MIGRATION", stringValue(migration.role), "replaces", stringValue(migration.replaces)], ["POLICY", failed.length === 0 ? "ok" : `failed=${failed.length}`, "valuesPrinted", "false"], ]), "", "NEXT", ` status: ${stringValue(next.status)}`, ` validate: ${stringValue(next.validate)}`, ` mirror-status: ${stringValue(next.mirrorStatus)}`, ` webhook-status: ${stringValue(next.webhookStatus)}`, ` fix-automatic-delivery: ${stringValue(next.fixAutomaticDelivery)}`, "", "Boundary: Gitea is internal ClusterIP source authority for GH-1560; CI trigger authority is Pipelines-as-Code, not Gitea Actions/act_runner.", "Disclosure: Secret values are not printed; this stage does not create runner credentials.", ]); } export function renderApply(result: Record): RenderedCliResult { const target = record(result.target); const payload = record(result.payloadTransport); const remote = record(result.remote); const steps = record(remote.steps); const serverDryRunStep = record(steps.serverDryRun); const frpcStep = record(steps.frpcSecret); const frpcCleanupStep = record(steps.frpcCleanup); const webhookSecretStep = record(steps.webhookSyncSecret); const applyStep = record(steps.apply); const rolloutStep = record(steps.rollout); const frpcRolloutStep = record(steps.frpcRollout); const webhookRolloutStep = record(steps.webhookSyncRollout); const caddy = record(result.pk01Caddy); return rendered(result, "platform-infra gitea apply", [ "PLATFORM-INFRA GITEA APPLY", ...table(["TARGET", "NAMESPACE", "MODE", "OK"], [[stringValue(target.id), stringValue(target.namespace), stringValue(result.mode), boolText(result.ok)]]), ...table(["TRANSPORT", "BYTES", "MATERIALIZATION", "VALUES"], [[stringValue(payload.kind), stringValue(payload.totalBytes), stringValue(payload.materialization), stringValue(payload.valuesPrinted)]]), "", "STEPS", ...table(["STEP", "EXIT", "DETAIL"], [ ["serverDryRun", stringValue(serverDryRunStep.exitCode), compactLine(stringValue(serverDryRunStep.stderrTail, stringValue(serverDryRunStep.stdoutTail)))], ["frpc-secret", stringValue(frpcStep.exitCode), compactLine(stringValue(frpcStep.stderrTail, stringValue(frpcStep.stdoutTail)))], ["frpc-cleanup", stringValue(frpcCleanupStep.exitCode), compactLine(stringValue(frpcCleanupStep.stderrTail, stringValue(frpcCleanupStep.stdoutTail)))], ["webhook-secret", stringValue(webhookSecretStep.exitCode), compactLine(stringValue(webhookSecretStep.stderrTail, stringValue(webhookSecretStep.stdoutTail)))], ["apply", stringValue(applyStep.exitCode), compactLine(stringValue(applyStep.stderrTail, stringValue(applyStep.stdoutTail)))], ["rollout", stringValue(rolloutStep.exitCode), compactLine(stringValue(rolloutStep.stderrTail, stringValue(rolloutStep.stdoutTail)))], ["frpc-rollout", stringValue(frpcRolloutStep.exitCode), compactLine(stringValue(frpcRolloutStep.stderrTail, stringValue(frpcRolloutStep.stdoutTail)))], ["webhook-rollout", stringValue(webhookRolloutStep.exitCode), compactLine(stringValue(webhookRolloutStep.stderrTail, stringValue(webhookRolloutStep.stdoutTail)))], ["pk01-caddy", stringValue(caddy.exitCode ?? (caddy.ok === false ? 1 : 0)), compactLine(stringValue(caddy.error, stringValue(caddy.status, stringValue(caddy.reason))))], ]), ...remoteErrorLines(result), "", `NEXT bun scripts/cli.ts platform-infra gitea status --target ${stringValue(target.id)}`, ]); } export function renderStatus(result: Record): RenderedCliResult { const summary = record(result.summary); const statefulSet = record(summary.statefulSet); const service = record(summary.service); const networkPolicy = record(summary.networkPolicy); const pods = arrayRecords(summary.pods).map((pod) => [stringValue(pod.name), stringValue(pod.phase), boolText(pod.ready), stringValue(pod.restarts)]); const pvcs = arrayRecords(summary.pvcs).map((pvc) => [stringValue(pvc.name), stringValue(pvc.phase), stringValue(pvc.capacity)]); return rendered(result, "platform-infra gitea status", [ "PLATFORM-INFRA GITEA STATUS", ...table(["TARGET", "ROUTE", "NAMESPACE", "READY"], [[stringValue(summary.target), stringValue(summary.route), stringValue(summary.namespace), boolText(summary.ready)]]), "", "CONTROL", ...table(["CHECK", "VALUE", "DETAIL"], [ ["statefulSet", `${stringValue(statefulSet.readyReplicas)}/${stringValue(statefulSet.desired)}`, stringValue(statefulSet.name)], ["service", stringValue(service.type), stringValue(summary.serviceDns)], ["endpoints", boolText(summary.endpointsReady), stringValue(service.clusterIP)], ["allow-all", boolText(networkPolicy.allowAllPresent), "NetworkPolicy"], ]), "", "PODS", ...(pods.length === 0 ? ["-"] : table(["POD", "PHASE", "READY", "RESTARTS"], pods)), "", "PVCS", ...(pvcs.length === 0 ? ["-"] : table(["PVC", "PHASE", "CAPACITY"], pvcs)), ...remoteErrorLines(result), "", `NEXT bun scripts/cli.ts platform-infra gitea validate --target ${stringValue(summary.target)}`, ]); } function remoteErrorLines(result: Record): string[] { if (result.ok !== false) return []; const remote = record(result.remote); const exitCode = stringValue(remote.exitCode); const stderr = compactLine(stringValue(remote.stderrTail)); const stdout = compactLine(stringValue(remote.stdoutTail)); const detail = stderr !== "-" ? stderr : stdout; return ["", "ERROR", ...table(["EXIT", "DETAIL"], [[exitCode, detail]])]; } function compactTail(value: string): string { const line = value.split(/\r?\n/u).map((item) => item.trim()).filter(Boolean).at(-1) ?? ""; return line.length > 96 ? `${line.slice(0, 93)}...` : line; } function rendered(result: Record, command: string, lines: string[]): RenderedCliResult { return { ok: result.ok !== false, command, renderedText: lines.join("\n"), contentType: "text/plain" }; } function serviceDnsFromObjects(app: Record, target: Record): string { const service = record(app.service); return `${stringValue(app.serviceName)}.${stringValue(target.namespace)}.svc.cluster.local:${stringValue(service.httpPort)}`; } function record(value: unknown): Record { return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : {}; } function arrayRecords(value: unknown): Record[] { return Array.isArray(value) ? value.filter((item) => typeof item === "object" && item !== null && !Array.isArray(item)) as Record[] : []; } function stringValue(value: unknown, fallback = "-"): string { if (typeof value === "string" && value.length > 0) return value; if (typeof value === "number" || typeof value === "boolean") return String(value); return fallback; } function boolText(value: unknown): string { return value === true ? "true" : "false"; } function compactLine(value: string): string { const trimmed = value.replace(/\s+/gu, " ").trim(); return trimmed.length > 0 ? trimmed.slice(0, 220) : "-"; } function table(headers: string[], rows: string[][]): string[] { const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => (row[index] ?? "").length))); const format = (row: string[]) => row.map((cell, index) => cell.padEnd(widths[index])).join(" "); return [format(headers), format(headers.map((header, index) => "-".repeat(Math.max(header.length, widths[index])))), ...rows.map(format)]; }