105 lines
5.0 KiB
TypeScript
105 lines
5.0 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. pipelinerun-runtime module for scripts/src/ci.ts.
|
|
|
|
// Moved mechanically from scripts/src/ci.ts:1899-1968 for #903.
|
|
|
|
import { randomUUID } from "node:crypto";
|
|
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join, posix as posixPath } from "node:path";
|
|
import { blockedCatalogArtifactIds, catalogSummary, findCiCatalogArtifact, loadCiCatalog, supportedSourceBuildArtifactIds, type CiCatalogArtifact, type CiSourceBuildCatalogArtifact, type CiUpstreamImageCatalogArtifact } from "../ci-catalog";
|
|
import { runCommand } from "../command";
|
|
import { type UniDeskConfig, repoRoot, rootPath } from "../config";
|
|
import { ensureGithubSshIdentityForProvider, gitSshHttpConnectProxySource } from "../deploy-ssh-identity";
|
|
import { jobWithTail, listJobs, readJob, startJob } from "../jobs";
|
|
import { coreInternalFetch } from "../microservices";
|
|
import {
|
|
artifactRegistryReadonlyResultFromCommand,
|
|
buildArtifactRegistryReadonlyProbe,
|
|
parseArtifactRegistryOptions,
|
|
type ArtifactRegistryReadonlyProbe,
|
|
} from "../artifact-registry";
|
|
import { d601K3sGuardShellLines, d601NativeKubeconfig } from "../d601-k3s-guard";
|
|
import { runSshCommandCapture } from "../ssh";
|
|
|
|
import type { DispatchResult, PipelineRunCondition } from "./types";
|
|
import { status } from "./cleanup";
|
|
import { ciTargetGuardShellLines, shellQuote } from "./options";
|
|
import { dispatchSsh, runRemoteKubectl, runRemoteKubectlRaw, uploadRemoteBase64 } from "./remote";
|
|
import { ciTarget } from "./types";
|
|
|
|
export async function remoteCreatePipelineRun(manifest: string, target = ciTarget(null)): Promise<string> {
|
|
const encoded = Buffer.from(manifest, "utf8").toString("base64");
|
|
const token = randomUUID().replace(/-/gu, "").slice(0, 12);
|
|
const b64Path = `/tmp/unidesk-ci-pipelinerun-${token}.b64`;
|
|
const upload = await uploadRemoteBase64(b64Path, encoded, target);
|
|
if (!upload.ok) throw new Error(`failed to upload PipelineRun manifest: ${upload.stderr || upload.stdout}`);
|
|
const result = await runRemoteKubectl([
|
|
"tmp=$(mktemp /tmp/unidesk-ci-run.XXXXXX.yaml)",
|
|
`b64_path=${shellQuote(b64Path)}`,
|
|
"trap 'rm -f \"$tmp\" \"$b64_path\"' EXIT",
|
|
"base64 -d \"$b64_path\" > \"$tmp\"",
|
|
"kubectl create -f \"$tmp\" -o jsonpath='{.metadata.name}'",
|
|
].join("\n"), 60_000, 45_000, target);
|
|
return result.stdout.trim();
|
|
}
|
|
|
|
export async function waitForPipelineRun(name: string, waitMs: number, target = ciTarget(null)): Promise<DispatchResult | null> {
|
|
if (waitMs <= 0) return null;
|
|
const command = [
|
|
"set -euo pipefail",
|
|
...ciTargetGuardShellLines(target),
|
|
`printf 'waiting_pipelinerun=%s\\n' ${shellQuote(name)}`,
|
|
`deadline=$((SECONDS + ${Math.ceil(waitMs / 1000)}))`,
|
|
"while [ \"$SECONDS\" -lt \"$deadline\" ]; do",
|
|
` condition="$(kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o jsonpath='{range .status.conditions[?(@.type==\"Succeeded\")]}{.status}{\"\\t\"}{.reason}{\"\\t\"}{.message}{end}' 2>/dev/null || true)"`,
|
|
" case \"$condition\" in",
|
|
" True*)",
|
|
" printf 'condition=%s\\n' \"$condition\"",
|
|
` kubectl get taskrun -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} --no-headers 2>/dev/null || true`,
|
|
" exit 0",
|
|
" ;;",
|
|
" False*)",
|
|
" printf 'condition=%s\\n' \"$condition\"",
|
|
` kubectl get taskrun,pod -n unidesk-ci -l tekton.dev/pipelineRun=${shellQuote(name)} 2>/dev/null || true`,
|
|
" exit 1",
|
|
" ;;",
|
|
" esac",
|
|
" sleep 2",
|
|
"done",
|
|
`echo "Timed out waiting for pipelinerun/${name}" >&2`,
|
|
`kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o json`,
|
|
"exit 124",
|
|
].join("\n");
|
|
return dispatchSsh(command, waitMs + 30_000, waitMs + 20_000, true, target);
|
|
}
|
|
|
|
export async function readPipelineRunCondition(name: string, target = ciTarget(null)): Promise<PipelineRunCondition> {
|
|
const result = await runRemoteKubectlRaw([
|
|
"set -euo pipefail",
|
|
`condition="$(kubectl get pipelinerun/${shellQuote(name)} -n unidesk-ci -o jsonpath='{range .status.conditions[?(@.type==\"Succeeded\")]}{.status}{\"\\t\"}{.reason}{\"\\t\"}{.message}{end}' 2>/dev/null || true)"`,
|
|
"printf '%s\\n' \"$condition\"",
|
|
].join("\n"), 30_000, 15_000, target);
|
|
const [status = "", reason = "", ...messageParts] = result.stdout.trim().split("\t");
|
|
const message = messageParts.join("\t");
|
|
return {
|
|
ok: status === "True" ? true : status === "False" ? false : null,
|
|
status,
|
|
reason,
|
|
message,
|
|
query: {
|
|
ok: result.ok,
|
|
status: result.status,
|
|
exitCode: result.exitCode,
|
|
stdoutTail: result.stdout.slice(-1200),
|
|
stderrTail: result.stderr.slice(-1200),
|
|
},
|
|
};
|
|
}
|
|
|
|
export function pipelineRunWaitSucceeded(wait: DispatchResult | null, condition: PipelineRunCondition | null): boolean {
|
|
if (wait === null) return true;
|
|
if (condition?.ok === true) return true;
|
|
if (condition?.ok === false) return false;
|
|
return wait.ok || wait.exitCode === 0 || wait.stdout.includes("condition=True\tSucceeded\t");
|
|
}
|