feat: split backend-core artifact ci cd

This commit is contained in:
Codex
2026-05-18 15:54:01 +00:00
parent d21453cf46
commit 803a695d0a
15 changed files with 713 additions and 56 deletions
+79 -3
View File
@@ -34,6 +34,12 @@ interface CiOptions {
waitMs: number;
}
interface CiPublishBackendCoreOptions {
repoUrl: string;
commit: string;
waitMs: number;
}
interface CiDevE2EOptions {
repoUrl: string;
desiredRef: string;
@@ -92,6 +98,12 @@ function requireRevision(value: string | null): string {
return value;
}
function requireFullCommit(value: string | null, option = "--commit"): string {
const commit = value?.toLowerCase() ?? "";
if (!/^[0-9a-f]{40}$/u.test(commit)) throw new Error(`${option} requires a full 40-character pushed Git commit SHA`);
return commit;
}
function requireDesiredRef(value: string | null): string {
const ref = value ?? "master";
if (!/^[A-Za-z0-9._/-]{1,160}$/u.test(ref) || ref.startsWith("-") || ref.includes("..")) {
@@ -474,6 +486,35 @@ spec:
`;
}
function backendCoreArtifactPipelineRunManifest(options: CiPublishBackendCoreOptions): string {
const safeSuffix = new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14).toLowerCase();
return `apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: backend-core-artifact-${safeSuffix}-
namespace: unidesk-ci
labels:
app.kubernetes.io/name: unidesk-backend-core-artifact-publish
app.kubernetes.io/part-of: unidesk
unidesk.ai/service-id: backend-core
unidesk.ai/revision: ${JSON.stringify(options.commit)}
spec:
pipelineRef:
name: unidesk-backend-core-artifact-publish
taskRunTemplate:
serviceAccountName: unidesk-ci-runner
params:
- name: repo-url
value: ${JSON.stringify(options.repoUrl)}
- name: revision
value: ${JSON.stringify(options.commit)}
workspaces:
- name: shared-workspace
persistentVolumeClaim:
claimName: unidesk-ci-cache
`;
}
async function remoteCreatePipelineRun(manifest: string): Promise<string> {
const encoded = Buffer.from(manifest, "utf8").toString("base64");
const token = randomUUID().replace(/-/gu, "").slice(0, 12);
@@ -540,6 +581,29 @@ async function run(options: CiOptions): Promise<Record<string, unknown>> {
};
}
async function publishBackendCoreArtifact(options: CiPublishBackendCoreOptions): Promise<Record<string, unknown>> {
const name = await remoteCreatePipelineRun(backendCoreArtifactPipelineRunManifest(options));
const wait = await waitForPipelineRun(name, options.waitMs);
const waitSucceeded = wait === null || wait.exitCode === 0 || wait.stdout.trimStart().startsWith("True\tSucceeded\t");
return {
ok: waitSucceeded,
pipelineRun: name,
namespace: "unidesk-ci",
repoUrl: options.repoUrl,
commit: options.commit,
artifact: `127.0.0.1:5000/unidesk/backend-core:${options.commit}`,
boundary: "CI publishes the image to D601 registry; CD must pull it and must not build backend-core",
wait: wait === null ? null : {
stdoutTail: wait.stdout.slice(-6000),
stderrTail: wait.stderr.slice(-6000),
},
next: [
`bun scripts/cli.ts ci logs ${name}`,
`bun scripts/cli.ts artifact-registry deploy-backend-core --commit ${options.commit}`,
],
};
}
async function runRemoteDevE2ELauncher(options: CiDevE2EOptions): Promise<DispatchResult> {
const scriptTimeoutMs = Math.max(options.scriptTimeoutMs, options.waitMs, 60_000);
const remoteTimeoutMs = 45_000;
@@ -788,11 +852,12 @@ async function logs(name: string): Promise<Record<string, unknown>> {
export function ciHelp(): Record<string, unknown> {
return {
command: "ci install|status|run|run-dev-e2e|logs",
description: "Manage the D601 k3s Tekton CI gate. This intentionally does not deploy CD.",
command: "ci install|status|run|publish-backend-core|run-dev-e2e|logs",
description: "Manage the D601 k3s Tekton CI gate. CI may publish backend-core image artifacts, but it intentionally does not deploy CD.",
examples: [
"bun scripts/cli.ts ci install",
"bun scripts/cli.ts ci run --revision <commit>",
"bun scripts/cli.ts ci publish-backend-core --commit <full-sha>",
"bun scripts/cli.ts ci run-dev-e2e --wait-ms 600000",
"bun scripts/cli.ts ci logs <runId>",
],
@@ -805,6 +870,11 @@ export function ciHelp(): Record<string, unknown> {
interceptors: tektonTriggersInterceptorsUrl,
},
},
backendCoreArtifact: {
producer: "D601 CI",
registry: "127.0.0.1:5000/unidesk/backend-core:<commit>",
cdCommand: "bun scripts/cli.ts artifact-registry deploy-backend-core --commit <full-sha>",
},
runDevE2E: {
defaultTriggerMode: "commit-pinned-ssh-launcher",
desiredState: "origin/master:deploy.json#environments.dev",
@@ -831,6 +901,12 @@ export async function runCiCommand(_config: UniDeskConfig, args: string[]): Prom
const waitMs = numberOption(args, "--wait-ms", 0);
return run({ repoUrl, revision, waitMs });
}
if (action === "publish-backend-core") {
const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
const commit = requireFullCommit(stringOption(args, "--commit") ?? stringOption(args, "--revision"));
const waitMs = numberOption(args, "--wait-ms", 0);
return publishBackendCoreArtifact({ repoUrl, commit, waitMs });
}
if (action === "run-dev-e2e") {
const repoUrl = stringOption(args, "--repo") ?? stringOption(args, "--repo-url") ?? "https://github.com/pikasTech/unidesk";
const desiredRef = requireDesiredRef(stringOption(args, "--desired-ref") ?? stringOption(args, "--deploy-branch"));
@@ -852,7 +928,7 @@ export async function runCiCommand(_config: UniDeskConfig, args: string[]): Prom
});
}
if (action === "logs") return logs(nameArg ?? "");
throw new Error("ci command must be one of: install, status, run, run-dev-e2e, logs");
throw new Error("ci command must be one of: install, status, run, publish-backend-core, run-dev-e2e, logs");
}
export function startCiInstallJob(): Record<string, unknown> {