feat: extend k3s artifact consumers

This commit is contained in:
Codex
2026-05-20 02:58:41 +00:00
parent 15a1a8b21a
commit 595de3d320
28 changed files with 1003 additions and 76 deletions
+175 -9
View File
@@ -74,11 +74,14 @@ const defaultOptions: ArtifactRegistryOptions = {
const supportedArtifactConsumerServices = [
"backend-core",
"baidu-netdisk",
"claudeqq",
"code-queue",
"code-queue-mgr",
"decision-center",
"findjob",
"frontend",
"k3sctl-adapter",
"mdtodo",
"met-nonlinear",
"oa-event-flow",
"pipeline",
@@ -124,6 +127,7 @@ interface ArtifactConsumerTarget {
servicePort: number;
containerName: string;
healthPath: string;
extraDeployments?: Array<string | { name: string; containerName?: string }>;
applySelector?: string;
podLabelSelector?: string;
};
@@ -264,6 +268,116 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
},
},
},
"mdtodo": {
serviceId: "mdtodo",
environment: "prod",
kind: "d601-k3s",
registryRepository: "unidesk/mdtodo",
dockerfile: "src/components/microservices/mdtodo/Dockerfile",
prodLiveApply: "enabled",
targets: {
dev: {
targetImage: "unidesk-mdtodo:dev",
targetCommitImage: (commit: string) => `unidesk-mdtodo:${commit}`,
deployRef: "deploy.json#environments.dev.services.mdtodo",
k3s: {
namespace: "unidesk-dev",
manifestRepoPath: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-mdtodo.k8s.yaml",
deploymentName: "mdtodo-dev",
serviceName: "mdtodo-dev",
servicePort: 4267,
containerName: "mdtodo",
healthPath: "/health",
podLabelSelector: "app.kubernetes.io/name=mdtodo,unidesk.ai/environment=dev",
},
},
prod: {
targetImage: "unidesk-mdtodo:d601",
targetCommitImage: (commit: string) => `unidesk-mdtodo:${commit}`,
deployRef: "deploy.json#environments.prod.services.mdtodo",
k3s: {
namespace: "unidesk",
manifestRepoPath: "src/components/microservices/k3sctl-adapter/k3s/mdtodo.k8s.yaml",
deploymentName: "mdtodo",
serviceName: "mdtodo",
servicePort: 4267,
containerName: "mdtodo",
healthPath: "/health",
},
},
},
},
"claudeqq": {
serviceId: "claudeqq",
environment: "prod",
kind: "d601-k3s",
registryRepository: "unidesk/claudeqq",
dockerfile: "claudeqq/Dockerfile",
sourceRepo: "https://gitee.com/lyon1998/agent_skills",
prodLiveApply: "enabled",
targets: {
dev: {
targetImage: "unidesk-claudeqq:dev",
targetCommitImage: (commit: string) => `unidesk-claudeqq:${commit}`,
deployRef: "deploy.json#environments.dev.services.claudeqq",
k3s: {
namespace: "unidesk-dev",
manifestRepoPath: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-claudeqq.k8s.yaml",
deploymentName: "claudeqq-dev",
serviceName: "claudeqq-dev",
servicePort: 3290,
containerName: "claudeqq",
healthPath: "/health",
podLabelSelector: "app.kubernetes.io/name=claudeqq,unidesk.ai/environment=dev",
},
},
prod: {
targetImage: "unidesk-claudeqq:d601",
targetCommitImage: (commit: string) => `unidesk-claudeqq:${commit}`,
deployRef: "deploy.json#environments.prod.services.claudeqq",
k3s: {
namespace: "unidesk",
manifestRepoPath: "src/components/microservices/k3sctl-adapter/k3s/claudeqq.k8s.yaml",
deploymentName: "claudeqq",
serviceName: "claudeqq",
servicePort: 3290,
containerName: "claudeqq",
healthPath: "/health",
},
},
},
},
"code-queue": {
serviceId: "code-queue",
environment: "dev",
kind: "d601-k3s",
registryRepository: "unidesk/code-queue",
dockerfile: "src/components/microservices/code-queue/Dockerfile",
prodLiveApply: "unsupported",
prodLiveBlockReason: "code-queue is dev-only for artifact consumer validation and has no prod artifact deploy, rollout, or manifest mutation target.",
targets: {
dev: {
targetImage: "unidesk-code-queue:dev",
targetCommitImage: (commit: string) => `unidesk-code-queue:${commit}`,
deployRef: "deploy.json#environments.dev.services.code-queue",
k3s: {
namespace: "unidesk-dev",
manifestRepoPath: "src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-code-queue.k8s.yaml",
deploymentName: "code-queue-scheduler-dev",
extraDeployments: [
{ name: "d601-dev-provider-egress-proxy", containerName: "provider-egress-proxy" },
"code-queue-read-dev",
"code-queue-write-dev",
],
serviceName: "code-queue-scheduler-dev",
servicePort: 4222,
containerName: "code-queue",
healthPath: "/health",
podLabelSelector: "app.kubernetes.io/name=code-queue,app.kubernetes.io/component=scheduler,unidesk.ai/environment=dev",
},
},
},
},
"frontend": {
serviceId: "frontend",
environment: "prod",
@@ -809,6 +923,7 @@ function plan(options: ArtifactRegistryOptions): Record<string, unknown> {
"CI builds and publishes backend-core, frontend, and reviewed user-service artifacts on D601",
"CD only pulls, retags, recreates/imports artifacts, and verifies live commit",
"master server CD must not compile Rust or run docker compose build for artifact consumers",
"code-queue is dev-only for artifact consumer validation and has no prod artifact deploy target",
],
renderedPaths: bundle.paths,
artifactConsumerFlow: [
@@ -939,7 +1054,7 @@ function artifactConsumerSpec(serviceId: string, environment: ArtifactDeployEnvi
const explicit = artifactConsumerSpecs[key];
if (explicit !== undefined) return explicit;
const shared = artifactConsumerSpecs[serviceId];
return environment !== null && shared?.targets[environment] !== undefined ? shared : null;
return shared ?? null;
}
function supportedArtifactConsumers(): Array<{ environment: ArtifactDeployEnvironment; serviceId: SupportedArtifactConsumerService; kind: ArtifactConsumerSpec["kind"] }> {
@@ -1682,7 +1797,16 @@ function legacyDeployBackendCoreResult(options: ArtifactRegistryOptions): Record
function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: ArtifactConsumerSpec, target: ArtifactConsumerTarget, commit: string): Record<string, unknown> {
const environment = options.environment ?? "prod";
const verificationBlocked = spec.runtimeVerification === "blocked";
const livePolicy = environment === "prod" ? spec.prodLiveApply : "enabled";
const sourceImage = artifactImageRef(options, spec, commit);
const k3sDeployments = target.k3s === undefined
? []
: [
{ name: target.k3s.deploymentName, containerName: target.k3s.containerName },
...(target.k3s.extraDeployments ?? []).map((deployment) => typeof deployment === "string"
? { name: deployment, containerName: target.k3s!.containerName }
: { name: deployment.name, containerName: deployment.containerName ?? target.k3s!.containerName }),
];
const common = {
ok: !verificationBlocked,
supported: !verificationBlocked,
@@ -1707,9 +1831,9 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
},
boundary: `${environment} CD is artifact-consumer only: verify commit-pinned registry image, pull/import, deploy, then verify live commit/image/health; it never builds source on the runtime target`,
liveApply: {
policy: spec.prodLiveApply,
policy: livePolicy,
allowed: !verificationBlocked && (environment !== "prod" || spec.prodLiveApply === "enabled"),
reason: spec.runtimeVerificationBlockReason ?? spec.prodLiveBlockReason ?? null,
reason: spec.runtimeVerificationBlockReason ?? (environment === "prod" ? spec.prodLiveBlockReason ?? null : null),
},
};
if (spec.kind === "compose" || spec.kind === "d601-compose") {
@@ -1752,6 +1876,7 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
kind: "d601-k3s",
namespace: target.k3s?.namespace,
deployment: target.k3s?.deploymentName,
deployments: k3sDeployments,
service: target.k3s?.serviceName,
stableImage: target.targetImage,
runtimeImage: target.targetCommitImage(commit),
@@ -1803,6 +1928,13 @@ function d601K3sArtifactDeployScript(options: ArtifactRegistryOptions, spec: Art
const sourceRepo = sourceRepoFor(options, spec);
const commitImage = target.targetCommitImage(commit);
const k3s = target.k3s;
const deploymentSpecs = [
{ name: k3s.deploymentName, containerName: k3s.containerName },
...(k3s.extraDeployments ?? []).map((deployment) => typeof deployment === "string"
? { name: deployment, containerName: k3s.containerName }
: { name: deployment.name, containerName: deployment.containerName ?? k3s.containerName }),
];
const deploymentSpecsBase64 = Buffer.from(JSON.stringify(deploymentSpecs), "utf8").toString("base64");
const labels = [
`unidesk.ai/deploy-service-id=${spec.serviceId}`,
`unidesk.ai/deploy-ref=${deployRefFor(options, spec)}`,
@@ -1827,6 +1959,7 @@ function d601K3sArtifactDeployScript(options: ArtifactRegistryOptions, spec: Art
`namespace=${shellQuote(k3s.namespace)}`,
`deployment=${shellQuote(k3s.deploymentName)}`,
`container_name=${shellQuote(k3s.containerName)}`,
`deployment_specs_b64=${shellQuote(deploymentSpecsBase64)}`,
`service_name=${shellQuote(k3s.serviceName)}`,
`service_port=${shellQuote(String(k3s.servicePort))}`,
`health_path=${shellQuote(k3s.healthPath)}`,
@@ -1858,15 +1991,37 @@ function d601K3sArtifactDeployScript(options: ArtifactRegistryOptions, spec: Art
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F \"$stable_image\" >/dev/null",
"root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images ls | grep -F \"$commit_image\" >/dev/null",
"printf '%s' \"$manifest_b64\" | base64 -d > \"$manifest\"",
"grep -F \"name: $deployment\" \"$manifest\" >/dev/null",
"deployment_specs=$(mktemp /tmp/unidesk-artifact-k3s-deployments.XXXXXX.json)",
"printf '%s' \"$deployment_specs_b64\" | base64 -d > \"$deployment_specs\"",
"trap 'rm -f \"$archive\" \"$manifest\" \"$deployment_specs\" \"$health_tmp\"' EXIT",
"python3 - \"$deployment_specs\" \"$manifest\" <<'PY'",
"import json, sys",
"specs = json.load(open(sys.argv[1], encoding='utf-8'))",
"manifest = open(sys.argv[2], encoding='utf-8').read()",
"missing = [item['name'] for item in specs if f\"name: {item['name']}\" not in manifest]",
"if missing:",
" raise SystemExit('manifest missing deployment(s): ' + ','.join(missing))",
"PY",
"if [ -n \"$apply_selector\" ]; then kubectl apply -f \"$manifest\" -l \"$apply_selector\"; else kubectl apply -f \"$manifest\"; fi",
...(environment === "dev" && spec.serviceId === "frontend" ? [d601DevFrontendAuthPatchScript(readConfig())] : []),
"previous_commit=$(kubectl -n \"$namespace\" get deployment \"$deployment\" -o jsonpath='{.metadata.annotations.unidesk\\.ai/deploy-commit}' 2>/dev/null || true)",
"kubectl -n \"$namespace\" set image \"deployment/$deployment\" \"$container_name=$commit_image\"",
"kubectl -n \"$namespace\" set env \"deployment/$deployment\" \"UNIDESK_DEPLOY_SERVICE_ID=$service_id\" \"UNIDESK_DEPLOY_REF=$deploy_ref\" \"UNIDESK_DEPLOY_REPO=$source_repo\" \"UNIDESK_DEPLOY_COMMIT=$commit\" \"UNIDESK_DEPLOY_REQUESTED_COMMIT=$commit\"",
`kubectl -n "$namespace" annotate "deployment/$deployment" ${labels.map(shellQuote).join(" ")} --overwrite`,
"if [ -n \"$previous_commit\" ] && [ \"$previous_commit\" != \"$commit\" ]; then kubectl -n \"$namespace\" annotate \"deployment/$deployment\" \"unidesk.ai/deploy-previous-commit=$previous_commit\" --overwrite; fi",
"kubectl -n \"$namespace\" rollout status \"deployment/$deployment\" --timeout=180s",
"python3 - \"$deployment_specs\" <<'PY' | while IFS=$'\\t' read -r rollout_deployment rollout_container; do",
"import json, sys",
"for item in json.load(open(sys.argv[1], encoding='utf-8')):",
" print(f\"{item['name']}\\t{item['containerName']}\")",
"PY",
" kubectl -n \"$namespace\" set image \"deployment/$rollout_deployment\" \"$rollout_container=$commit_image\"",
" kubectl -n \"$namespace\" set env \"deployment/$rollout_deployment\" \"UNIDESK_DEPLOY_SERVICE_ID=$service_id\" \"UNIDESK_DEPLOY_REF=$deploy_ref\" \"UNIDESK_DEPLOY_REPO=$source_repo\" \"UNIDESK_DEPLOY_COMMIT=$commit\" \"UNIDESK_DEPLOY_REQUESTED_COMMIT=$commit\" \"CODE_QUEUE_DEPLOY_COMMIT=$commit\" \"CODE_QUEUE_DEPLOY_REQUESTED_COMMIT=$commit\"",
` kubectl -n "$namespace" annotate "deployment/$rollout_deployment" ${labels.map(shellQuote).join(" ")} --overwrite`,
" if [ -n \"$previous_commit\" ] && [ \"$previous_commit\" != \"$commit\" ]; then kubectl -n \"$namespace\" annotate \"deployment/$rollout_deployment\" \"unidesk.ai/deploy-previous-commit=$previous_commit\" --overwrite; fi",
"done",
"python3 - \"$deployment_specs\" <<'PY' | while IFS= read -r rollout_deployment; do",
"import json, sys",
"for item in json.load(open(sys.argv[1], encoding='utf-8')):",
" print(item['name'])",
"PY",
" kubectl -n \"$namespace\" rollout status \"deployment/$rollout_deployment\" --timeout=180s",
"done",
"deployment_commit=$(kubectl -n \"$namespace\" get deployment \"$deployment\" -o jsonpath='{.metadata.annotations.unidesk\\.ai/deploy-commit}')",
"deployment_requested_commit=$(kubectl -n \"$namespace\" get deployment \"$deployment\" -o jsonpath='{.metadata.annotations.unidesk\\.ai/deploy-requested-commit}')",
"test \"$deployment_commit\" = \"$commit\"",
@@ -2069,6 +2224,11 @@ function localHelp(): Record<string, unknown> {
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service pipeline --commit <full-sha> --dry-run [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service met-nonlinear --commit <full-sha> --dry-run [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service k3sctl-adapter --commit <full-sha> --dry-run [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service mdtodo --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service mdtodo --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service claudeqq --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service code-queue --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
],
firstStage: "install now writes the rendered systemd/Compose/config files and starts the registry",
artifactConsumers: {
@@ -2088,6 +2248,8 @@ function localHelp(): Record<string, unknown> {
"bun scripts/cli.ts deploy apply --env prod --service pipeline --dry-run",
"bun scripts/cli.ts deploy apply --env prod --service met-nonlinear --dry-run",
"bun scripts/cli.ts deploy apply --env prod --service k3sctl-adapter --dry-run",
"bun scripts/cli.ts deploy apply --env prod --service mdtodo",
"bun scripts/cli.ts deploy apply --env prod --service claudeqq",
],
devCommands: [
"bun scripts/cli.ts deploy apply --env dev --service frontend",
@@ -2099,7 +2261,11 @@ function localHelp(): Record<string, unknown> {
"bun scripts/cli.ts deploy apply --env dev --service findjob --dry-run",
"bun scripts/cli.ts deploy apply --env dev --service pipeline --dry-run",
"bun scripts/cli.ts deploy apply --env dev --service met-nonlinear --dry-run",
"bun scripts/cli.ts deploy apply --env dev --service mdtodo",
"bun scripts/cli.ts deploy apply --env dev --service claudeqq",
"bun scripts/cli.ts deploy apply --env dev --service code-queue",
],
devOnlyConsumers: ["code-queue"],
rollbackShape: "rerun the same artifact consumer with a previous commit-pinned image",
},
legacyEntrypoints: {