Files
pikasTech-unidesk/scripts/src/ci/help.ts
T
2026-06-28 03:56:44 +00:00

166 lines
8.5 KiB
TypeScript

// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. help module for scripts/src/ci.ts.
// SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets.
// Moved mechanically from scripts/src/ci.ts:3273-3409 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 { g14CiPipelineManifest, tektonPipelineReleaseUrl, tektonPipelineVersion, tektonTriggersInterceptorsUrl, tektonTriggersReleaseUrl, tektonTriggersVersion } from "./types";
export function catalogArtifactDescriptor(artifact: CiCatalogArtifact): Record<string, unknown> {
if (artifact.kind === "source-build") {
return {
serviceId: artifact.serviceId,
kind: artifact.kind,
status: artifact.status,
producer: artifact.producer,
source: artifact.source,
image: artifact.image,
...(artifact.notes === undefined ? {} : { notes: artifact.notes }),
...(artifact.blockedReason === undefined ? {} : { blockedReason: artifact.blockedReason }),
};
}
return {
serviceId: artifact.serviceId,
kind: artifact.kind,
status: artifact.status,
producer: artifact.producer,
upstream: artifact.upstream,
blockedReason: artifact.blockedReason,
...(artifact.notes === undefined ? {} : { notes: artifact.notes }),
};
}
export function ciHelp(): Record<string, unknown> {
const catalog = loadCiCatalog();
return {
command: "ci install|status|github-ssh-identity|run|publish-backend-core|publish-user-service|run-dev-e2e|logs|cleanup-runs|cleanup-failed-pods",
description: "Manage native k3s Tekton CI on D601 or G14. CI may publish commit-pinned image artifacts, but it intentionally does not deploy CD.",
examples: [
"bun scripts/cli.ts ci install",
"bun scripts/cli.ts ci install --skip-prewarm",
"bun scripts/cli.ts ci install --skip-prewarm --skip-tekton-install",
"bun scripts/cli.ts ci install-status latest",
"bun scripts/cli.ts ci install --wait --skip-prewarm --skip-tekton-install",
"bun scripts/cli.ts ci plan --target D601",
"bun scripts/cli.ts ci github-ssh-identity plan --target D518",
"bun scripts/cli.ts ci github-ssh-identity ensure --target D518 --confirm",
"bun scripts/cli.ts ci install --target G14",
"bun scripts/cli.ts ci run --revision <commit>",
"bun scripts/cli.ts ci run --target G14 --revision <commit>",
"bun scripts/cli.ts ci publish-backend-core --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service baidu-netdisk --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service mdtodo --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service claudeqq --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service code-queue --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service decision-center --commit <full-sha>",
"bun scripts/cli.ts ci publish-user-service --service frontend --commit <full-sha>",
"bun scripts/cli.ts ci run-dev-e2e --wait-ms 600000",
"bun scripts/cli.ts ci logs <runId-or-pipelineRun> [--target G14] [--tail-lines 80]",
"bun scripts/cli.ts ci cleanup-runs --target D601 --min-age-minutes 60 --limit 50 --dry-run",
"bun scripts/cli.ts ci cleanup-runs --target D601 --min-age-minutes 60 --limit 50 --confirm",
"bun scripts/cli.ts ci cleanup-failed-pods --target D601 --namespace unidesk-ci --min-age-minutes 60 --dry-run",
"bun scripts/cli.ts ci cleanup-failed-pods --target D601 --namespace unidesk-ci --min-age-minutes 60 --confirm",
],
tekton: {
pipelineVersion: tektonPipelineVersion,
triggersVersion: tektonTriggersVersion,
sources: {
pipeline: tektonPipelineReleaseUrl,
triggers: tektonTriggersReleaseUrl,
interceptors: tektonTriggersInterceptorsUrl,
},
targets: {
default: "D601",
g14: {
providerId: "G14",
pipelineManifest: g14CiPipelineManifest,
nodeSelector: "unidesk.ai/node-id=G14",
},
},
},
install: {
defaultMode: "async-job",
waitFlag: "Use --wait only for explicit synchronous debugging; the default returns a .state/jobs job immediately with install-status follow-up.",
statusCommand: "bun scripts/cli.ts ci install-status <jobId|latest>",
prewarmDefault: true,
skipPrewarm: "Use --skip-prewarm only to refresh Tekton/CI manifests when runtime images are already present or prewarm is blocked by provider root/containerd permissions.",
skipTektonInstall: "Use --skip-tekton-install with --skip-prewarm only when Tekton is already installed and only UniDesk CI manifests/triggers need refreshing.",
},
backendCoreArtifact: {
producer: "D601 CI",
registry: "127.0.0.1:5000/unidesk/backend-core:<commit>",
cdCommand: "bun scripts/cli.ts deploy apply --env prod --service backend-core --commit <full-sha>",
},
userServiceArtifact: {
producer: "D601 CI",
command: "bun scripts/cli.ts ci publish-user-service --service <service-id> --commit <full-sha>",
transports: {
auto: "uses direct-docker for repo-owned code-queue artifacts and Tekton for the remaining services",
tekton: "D601 Tekton PipelineRun through backend-core/provider control plane",
directDocker: "repo-owned Docker artifact publish without backend-core/database dispatch; no deploy apply, rollout, restart, or active-task mutation",
},
supportedServices: supportedSourceBuildArtifactIds().filter((serviceId) => serviceId !== "backend-core"),
blockedServices: blockedCatalogArtifactIds(),
registry: "127.0.0.1:5000/unidesk/<service-id>:<commit>",
outputFields: ["serviceId", "sourceCommit", "sourceRepo", "dockerfile", "imageRef", "tag", "digest", "digestRef"],
summaryContract: catalog.summaryContract,
catalogSummary: catalogSummary(),
catalog: catalog.artifacts.map(catalogArtifactDescriptor),
boundary: "artifact producer only; no prod deploy and no production namespace mutation",
frontendNext: [
"bun scripts/cli.ts deploy apply --env dev --service frontend --commit <full-sha>",
"bun scripts/cli.ts deploy apply --env prod --service frontend --commit <full-sha>",
],
},
runDevE2E: {
defaultTriggerMode: "commit-pinned-ssh-launcher",
desiredState: "origin/master:deploy.json#environments.dev",
scriptSource: "origin/master:deploy.json#environments.dev.ci",
},
logs: {
defaultCapture: "ssh-stream",
dispatchTaskFallback: "--dispatch-task",
tailLines: "1..2000",
reason: "stream capture avoids backend-core task-result compactJson truncating CI log text before the CLI can extract failure hints",
},
cleanupRuns: {
defaultMode: "dry-run",
namespace: "unidesk-ci",
selector: "status.phase in Succeeded,Failed",
guard: "--confirm required for deletion; Running/Pending pods are never selected",
},
cleanupFailedPods: {
defaultNamespace: "unidesk-ci",
defaultMinAgeMinutes: 60,
defaultLimit: 50,
scope: "Kubernetes pods with status.phase=Failed only; does not touch Running/Pending pods, workloads, PVCs, Secrets, or Tekton resources",
defaultMode: "dry-run",
},
};
}
export function requireRunId(value: string): string {
if (!/^[a-z0-9]([-a-z0-9]{0,46}[a-z0-9])?$/u.test(value)) {
throw new Error("ci run-dev-e2e run id must be DNS-safe lowercase alnum/dash, max 48 chars");
}
return value;
}