feat: 收敛 PaC 首发 bootstrap
This commit is contained in:
@@ -28,6 +28,9 @@ import {
|
||||
} from "./platform-infra-pipelines-as-code-source-artifact";
|
||||
import { pacAdmissionDesiredIdentity } from "./platform-infra-pac-provenance";
|
||||
import { pacConsumerRbacDesiredIdentity, renderPacConsumerRbacDesiredFragment } from "./platform-infra-pac-consumer-rbac";
|
||||
import { runPlatformInfraGiteaCommand } from "./platform-infra-gitea";
|
||||
import { readGiteaConfig } from "./platform-infra-gitea-config";
|
||||
import { resolvePacBootstrapMirrorRepository } from "./platform-infra-pipelines-as-code-bootstrap";
|
||||
|
||||
const configFile = rootPath("config", "platform-infra", "pipelines-as-code.yaml");
|
||||
const configLabel = "config/platform-infra/pipelines-as-code.yaml";
|
||||
@@ -240,6 +243,11 @@ export async function runPlatformInfraPipelinesAsCodeCommand(config: UniDeskConf
|
||||
const result = await apply(config, options);
|
||||
return options.json || options.full || options.raw ? result : renderApply(result);
|
||||
}
|
||||
if (action === "bootstrap") {
|
||||
const options = parseApplyOptions(args.slice(1));
|
||||
const result = await bootstrap(config, options);
|
||||
return options.json || options.full || options.raw ? result : renderBootstrap(result);
|
||||
}
|
||||
if (action === "status") {
|
||||
const options = parseCommonOptions(args.slice(1));
|
||||
const result = await status(config, options);
|
||||
@@ -353,10 +361,12 @@ function help(scope: string | null): Record<string, unknown> {
|
||||
scope: "explicit-initial-platform-bootstrap",
|
||||
configTruth: configLabel,
|
||||
usage: [
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target <NODE> --consumer <consumer> --dry-run",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target <NODE> --consumer <consumer> --confirm",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code apply --target <NODE> --dry-run",
|
||||
"bun scripts/cli.ts platform-infra pipelines-as-code apply --target <NODE> --confirm",
|
||||
],
|
||||
boundary: "仅用于首次安装或显式平台 bootstrap;不得在 source PR 合并后用于交付、恢复或补跑。",
|
||||
boundary: "bootstrap 是首次引导的最短入口;apply 保留为单层维护入口。两者都不得在 source PR 合并后用于交付、恢复或补跑。",
|
||||
mutation: "explicit-confirm-required",
|
||||
};
|
||||
}
|
||||
@@ -889,7 +899,7 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
|
||||
return {
|
||||
ok: result.exitCode === 0 && parsed?.ok === true,
|
||||
action: "platform-infra-pipelines-as-code-apply",
|
||||
mutation: !options.dryRun,
|
||||
mutation: options.dryRun ? false : parsed?.mutation !== false,
|
||||
mode: options.dryRun ? "dry-run" : "confirmed",
|
||||
target: targetSummary(target),
|
||||
config: compactConfigSummary(pac, consumer, repository),
|
||||
@@ -902,6 +912,79 @@ async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Reco
|
||||
};
|
||||
}
|
||||
|
||||
async function bootstrap(config: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig();
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
const consumer = resolveConsumer(pac, options.consumerId);
|
||||
if (consumer.node.toLowerCase() !== target.id.toLowerCase()) {
|
||||
throw new Error(`Pipelines-as-Code consumer ${consumer.id} belongs to ${consumer.node}, not target ${target.id}`);
|
||||
}
|
||||
const repository = resolveRepository(pac, consumer.repositoryRef);
|
||||
const mirror = resolvePacBootstrapMirrorRepository(readGiteaConfig(), target.id, repository);
|
||||
const giteaArgs = ["mirror", "bootstrap", "--target", target.id, "--repo", mirror.key, ...(options.confirm ? ["--confirm"] : []), "--full"];
|
||||
const giteaBootstrap = record(await runPlatformInfraGiteaCommand(config, giteaArgs));
|
||||
if (options.confirm && giteaBootstrap.ok !== true) {
|
||||
return bootstrapResult(target, consumer, repository, mirror, options, giteaBootstrap, {
|
||||
ok: false,
|
||||
mutation: false,
|
||||
mode: "skipped",
|
||||
error: "gitea-bootstrap-failed",
|
||||
valuesPrinted: false,
|
||||
});
|
||||
}
|
||||
const pacApply = await apply(config, options);
|
||||
return bootstrapResult(target, consumer, repository, mirror, options, giteaBootstrap, pacApply);
|
||||
}
|
||||
|
||||
function bootstrapResult(
|
||||
target: PacTarget,
|
||||
consumer: PacConsumer,
|
||||
repository: PacRepository,
|
||||
mirror: ReturnType<typeof resolvePacBootstrapMirrorRepository>,
|
||||
options: ApplyOptions,
|
||||
giteaBootstrap: Record<string, unknown>,
|
||||
pacApply: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const remote = record(pacApply.remote);
|
||||
const repositoryMissing = remote.error === "gitea-repository-missing";
|
||||
const pacReady = pacApply.ok === true || (!options.confirm && repositoryMissing);
|
||||
const ok = giteaBootstrap.ok === true && pacReady;
|
||||
const confirm = `bun scripts/cli.ts platform-infra pipelines-as-code bootstrap --target ${target.id} --consumer ${consumer.id} --confirm`;
|
||||
return {
|
||||
ok,
|
||||
action: "platform-infra-pipelines-as-code-bootstrap",
|
||||
mutation: giteaBootstrap.mutation === true || pacApply.mutation === true,
|
||||
mode: options.confirm ? "confirmed" : "dry-run",
|
||||
target: targetSummary(target),
|
||||
consumer: consumerObservationSummary(consumer),
|
||||
repository: repositorySummary(repository),
|
||||
mirrorRepository: {
|
||||
key: mirror.key,
|
||||
upstreamRepository: mirror.upstream.repository,
|
||||
upstreamBranch: mirror.upstream.branch,
|
||||
owner: mirror.gitea.owner,
|
||||
repo: mirror.gitea.name,
|
||||
valuesPrinted: false,
|
||||
},
|
||||
steps: [
|
||||
{ id: "gitea-repository", ok: giteaBootstrap.ok === true, mutation: giteaBootstrap.mutation === true, mode: giteaBootstrap.mode, valuesPrinted: false },
|
||||
{ id: "pac-tekton-gitops", ok: pacReady, mutation: pacApply.mutation === true, mode: pacApply.mode, repositoryMissing, valuesPrinted: false },
|
||||
],
|
||||
giteaBootstrap,
|
||||
pipelinesAsCodeApply: pacApply,
|
||||
next: options.confirm
|
||||
? {
|
||||
deliveryTrigger: `Merge the prepared GitHub PR for ${mirror.upstream.repository}@${mirror.upstream.branch}; the automatic chain owns mirror, PaC, Tekton, GitOps, Argo, and runtime convergence.`,
|
||||
investigate: `bun scripts/cli.ts platform-infra pipelines-as-code status --target ${target.id} --consumer ${consumer.id}`,
|
||||
}
|
||||
: {
|
||||
confirm,
|
||||
boundary: "Run confirm only before the first source PR merge; do not use bootstrap as post-merge recovery.",
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
async function status(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
|
||||
const pac = readPacConfig();
|
||||
const target = resolveTarget(pac, options.targetId);
|
||||
@@ -2023,6 +2106,7 @@ function renderPlan(result: Record<string, unknown>): RenderedCliResult {
|
||||
function renderApply(result: Record<string, unknown>): RenderedCliResult {
|
||||
const target = record(result.target);
|
||||
const remote = record(result.remote);
|
||||
const preflight = record(remote.preflight);
|
||||
const errorText = compactLine(stringValue(remote.stderrTail, stringValue(remote.error, stringValue(result.error, ""))));
|
||||
const lines = [
|
||||
"PLATFORM-INFRA PIPELINES-AS-CODE APPLY",
|
||||
@@ -2030,7 +2114,13 @@ function renderApply(result: Record<string, unknown>): RenderedCliResult {
|
||||
"",
|
||||
"REMOTE",
|
||||
...table(["CONTROLLER", "WEBHOOK", "REPOSITORY", "EXIT", "VALUES"], [[stringValue(remote.controllerReady), stringValue(record(remote.webhook).present), stringValue(remote.repository), stringValue(remote.exitCode, "0"), "false"]]),
|
||||
...(Object.keys(preflight).length === 0 ? [] : [
|
||||
"",
|
||||
"PREFLIGHT",
|
||||
...table(["GITEA_REPOSITORY", "HTTP", "EXISTS"], [[stringValue(preflight.repository), stringValue(preflight.httpStatus), boolText(preflight.repositoryExists)]]),
|
||||
]),
|
||||
...(result.ok === false && errorText.length > 0 ? ["", `ERROR ${errorText}`] : []),
|
||||
...(remote.error === "gitea-repository-missing" ? ["HELP bun scripts/cli.ts platform-infra pipelines-as-code help platform-bootstrap"] : []),
|
||||
"",
|
||||
"NEXT",
|
||||
` status: ${stringValue(record(result.next).status)}`,
|
||||
@@ -2038,6 +2128,43 @@ function renderApply(result: Record<string, unknown>): RenderedCliResult {
|
||||
return rendered(result, "platform-infra pipelines-as-code apply", lines);
|
||||
}
|
||||
|
||||
function renderBootstrap(result: Record<string, unknown>): RenderedCliResult {
|
||||
const target = record(result.target);
|
||||
const consumer = record(result.consumer);
|
||||
const mirror = record(result.mirrorRepository);
|
||||
const steps = arrayRecords(result.steps);
|
||||
const pacApply = record(result.pipelinesAsCodeApply);
|
||||
const pacRemote = record(pacApply.remote);
|
||||
const giteaBootstrap = record(result.giteaBootstrap);
|
||||
const next = record(result.next);
|
||||
const errorValue = [pacRemote.error, giteaBootstrap.error, pacApply.error]
|
||||
.find((value): value is string => typeof value === "string" && value.length > 0) ?? "";
|
||||
const error = errorValue.length === 0 ? "" : compactLine(errorValue);
|
||||
const lines = [
|
||||
"PLATFORM-INFRA PAC / TEKTON / GITOPS BOOTSTRAP",
|
||||
...table(["TARGET", "CONSUMER", "MODE", "MUTATION", "OK"], [[stringValue(target.id), stringValue(consumer.id), stringValue(result.mode), boolText(result.mutation), boolText(result.ok)]]),
|
||||
"",
|
||||
"SOURCE AUTHORITY",
|
||||
...table(["GITEA_KEY", "GITEA_REPOSITORY", "UPSTREAM", "BRANCH"], [[stringValue(mirror.key), `${stringValue(mirror.owner)}/${stringValue(mirror.repo)}`, stringValue(mirror.upstreamRepository), stringValue(mirror.upstreamBranch)]]),
|
||||
"",
|
||||
"STEPS",
|
||||
...table(["STEP", "OK", "MODE", "MUTATION", "DETAIL"], steps.map((step) => [
|
||||
stringValue(step.id),
|
||||
boolText(step.ok),
|
||||
stringValue(step.mode),
|
||||
boolText(step.mutation),
|
||||
step.repositoryMissing === true ? "repository-will-be-created-on-confirm" : "ready",
|
||||
])),
|
||||
...(error.length === 0 ? [] : ["", `ERROR ${error}`]),
|
||||
"",
|
||||
"NEXT",
|
||||
...(result.mode === "dry-run"
|
||||
? [` confirm: ${stringValue(next.confirm)}`, ` boundary: ${stringValue(next.boundary)}`]
|
||||
: [` delivery: ${stringValue(next.deliveryTrigger)}`, ` investigate: ${stringValue(next.investigate)}`]),
|
||||
];
|
||||
return rendered(result, "platform-infra pipelines-as-code bootstrap", lines);
|
||||
}
|
||||
|
||||
function renderStatus(result: Record<string, unknown>): RenderedCliResult {
|
||||
const summary = record(result.summary);
|
||||
const consumer = record(result.consumer);
|
||||
|
||||
Reference in New Issue
Block a user