14 KiB
CI/CD Standardization
This document defines the stable split between CI artifact producers, artifact catalog data, legacy guardrails and CD consumers. Detailed Tekton rules remain in docs/reference/ci.md; registry runtime and consumers remain in docs/reference/artifact-registry.md; user-service delivery order remains in docs/reference/user-service-delivery.md.
Target Shape
The standard release shape is:
- CI builds or locks the artifact from a pushed commit or verified upstream digest.
- The artifact is published to the D601 artifact registry or a controlled mirror.
- Dev validation consumes that same artifact.
- Production CD is pull-only and verifies live commit or upstream digest.
- Runtime metadata can be traced back to a live Git commit or upstream image digest.
backend-core and D601 code-queue may be validated only in dev in this phase. This document must not be used to introduce production deploy validation for either service.
Artifact Catalog
Root CI.json is the CI producer catalog. It is not a deployment manifest.
Allowed catalog data:
- stable
serviceId; - artifact
kind:source-buildorupstream-image; - producer command:
ci publish-backend-coreorci publish-user-service; - source repository URL, optional repo root and repo-relative Dockerfile path;
- image repository naming and commit tag policy;
- upstream image digest, upstream source revision and D601 mirror intent for image-only services;
- the required success summary contract.
Forbidden catalog data:
- provider IDs;
- runtime namespace, Compose service, Kubernetes Service or health path;
- ports, environment variables, replicas or volumes;
- desired deployment commits or rollout targets.
Runtime topology belongs to config.json, deploy.json, existing Kubernetes manifests and the artifact-registry executor.
Producer Contract
bun scripts/cli.ts ci publish-user-service --service <id> --commit <full-sha> reads source.repo, source.dockerfile and image repository naming from CI.json. It rejects command-line repo overrides. Successful source-build producers must label the image with:
unidesk.ai/service-id;unidesk.ai/source-commit;unidesk.ai/source-repo;unidesk.ai/dockerfile.
The successful artifactSummary must contain serviceId, sourceCommit, sourceRepo, dockerfile, imageRef, tag, digest and digestRef.
blocked catalog entries must return a structured blocked result. They must not silently skip, build from a dirty worktree, fall back to target-side maintenance deployment, or mutate production.
Current Coverage
Supported source-build artifact producers:
backend-corethroughci publish-backend-core;frontend,baidu-netdisk,decision-center,project-manager,oa-event-flow,todo-note,code-queue-mgr,findjob,pipeline,met-nonlinear,k3sctl-adapter,mdtodo,claudeqqthroughci publish-user-service.
Cataloged but blocked:
code-queue: source input is known, but this phase allows only dev image validation and not prod-oriented artifact publication.filebrowserandfilebrowser-d601: upstream image-only services pinned todocker.io/filebrowser/filebrowser@sha256:289c5dd677c56662440f26eeb44266ed9746fe563d2e9100f546bff558534d70; they need a future upstream mirror producer before CI can publish them.
code-queue-mgr is a supported CI producer because the source-build input is known and the remote consumer commit already added a reviewed artifact consumer shape. Its production live apply remains supervisor-gated by deploy/artifact-registry and is not authorized by CI.json.
Upstream Image Consumers
filebrowser and filebrowser-d601 are upstream-image consumers, not source-built UniDesk services.
| Service | Upstream image | Source revision | Catalog home | CI Dockerfile build | Digest / mirror strategy | CD validation |
|---|---|---|---|---|---|---|
filebrowser |
docker.io/filebrowser/filebrowser:v2.63.3 |
ca5e249e3c0c94159c2136a0cd431a424eb18472 |
CI.json.artifacts[] with kind=upstream-image plus config.json.microservices[].repository.artifactSource |
forbidden | resolve tag to docker.io/filebrowser/filebrowser@sha256:<manifest-digest>, then optionally mirror to 127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest> |
pull by digest or mirror digest, verify OCI labels, container image id/digest, and private proxy health |
filebrowser-d601 |
docker.io/filebrowser/filebrowser:v2.63.3 |
ca5e249e3c0c94159c2136a0cd431a424eb18472 |
CI.json.artifacts[] with kind=upstream-image plus config.json.microservices[].repository.artifactSource |
forbidden | same as filebrowser |
same as filebrowser |
The catalog records the resolved upstream digest for the current image. If a future tag refresh cannot resolve the registry manifest digest, rollout must remain blocked until a reachable registry path resolves the manifest digest and records the mirror digest. A local Docker image id is supporting evidence only and not a registry digest pin.
Upstream Image Evidence
The catalog expression is intentionally minimal and parseable:
| Evidence command | Required result shape |
|---|---|
| `jq '.artifacts[] | select(.kind=="upstream-image") |
bun scripts/cli.ts config show with the File Browser artifactSource projection |
both services parse as kind=upstream-image, digestPinRequired=true, mirrorRepository=127.0.0.1:5000/upstream/filebrowser/filebrowser, ciDockerfileBuild=false, and pullOnlyCd=true |
docker manifest inspect --verbose docker.io/filebrowser/filebrowser:v2.63.3 |
must resolve the upstream manifest digest before rollout; if the registry request times out, rollout remains blocked |
bun scripts/cli.ts ci publish-user-service --service filebrowser --commit <full-sha> --dry-run |
returns ok=false with status=blocked, upstream digest/mirror metadata, and no Dockerfile source build |
The digest/mirror dry-run contract is:
- Resolve
docker.io/filebrowser/filebrowser:v2.63.3todocker.io/filebrowser/filebrowser@sha256:<manifest-digest>. - Mirror only that resolved digest to
127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest>. - Record the digest ref used by CD; do not promote a mutable tag as release truth.
- Refuse pull-only CD while the digest is unknown or the mirror digest differs from the upstream digest.
Pull-only CD validation must be expressed as concrete checks:
| Check | Form |
|---|---|
| Pull source | docker pull docker.io/filebrowser/filebrowser@sha256:<manifest-digest> or docker pull 127.0.0.1:5000/upstream/filebrowser/filebrowser@sha256:<manifest-digest> |
| Runtime identity | docker inspect reports the expected image id/digest and OCI labels org.opencontainers.image.version=2.63.3 and org.opencontainers.image.revision=ca5e249e3c0c94159c2136a0cd431a424eb18472 |
| Service health | UniDesk private proxy health succeeds for filebrowser and filebrowser-d601; direct public exposure remains forbidden |
| Build absence | no docker build, docker compose up --build, CI Dockerfile producer, or source checkout is used for these services |
Legacy Path Classification
| Path | Classification | Current guardrail | Cleanup condition |
|---|---|---|---|
deploy apply --env prod for services without a reviewed artifact consumer |
Must stay disabled/degraded | returns structured unsupported and refuses maintenance-channel fallback | enable only after each service has a documented artifact producer, dev consumer, prod consumer and live verification |
Local-manifest deploy apply for prod artifact consumers (backend-core, frontend, baidu-netdisk, decision-center) |
Must stay disabled/degraded as a standard prod path | blocked before source build; operator is pointed to deploy apply --env prod |
remove only after all production deploys use env-ref artifact consumers and no recovery runbook depends on local manifests |
artifact-registry deploy-backend-core |
Legacy compatibility entry | returns structured deprecated result; replacement is deploy apply --env prod --service backend-core |
remove after callers and docs use only deploy apply --env prod |
server rebuild frontend and server rebuild baidu-netdisk |
Maintenance / non-standard | docs classify as maintenance-only; standard release requires CI artifact plus dev/prod artifact consumer | keep until recovery runbooks have equivalent pull-only repair commands |
server rebuild backend-core |
Diagnostic/recovery only; not Rust iteration or prod CD | docs forbid Rust iteration and prod backend-core artifact CD through this command | keep for bootstrap/recovery until backend-core artifact CD and rollback are proven under outage conditions |
server rebuild dev-frontend-proxy, code-queue-mgr, project-manager, oa-event-flow, todo-note, provider-gateway on main-server Compose |
Bootstrap / recovery / diagnostic | still serialized through Compose lock and post-up validation | reassess service by service after artifact consumers exist |
provider.upgrade mode=schedule for provider-gateway |
Must be retained | protected upgrade path with validation; Host SSH self-rebuild remains forbidden | do not delete; it is the provider-gateway recovery path |
D601 direct docker build, kubectl apply, docker compose up --build used by deploy executor for allowed dev/backend bootstrap and native k3s setup |
Bootstrap / recovery / controlled dev | limited to allowed target-side executor, native k3s initialization, and dev backend-core; Code Queue prod is not enabled | convert only after artifact consumers or native bootstrap replacements exist |
D601 direct Code Queue / old codex deploy |
Must stay disabled/degraded | compatibility command throws; docs classify direct deployment as forbidden | wait for controlled Code Queue dev/prod CD worker |
File Browser docker run image-only path |
Needs later worker | now cataloged as upstream-image consumer; no CI Dockerfile build allowed | implement digest-resolved pull-only/mirror CD before treating it as standard deployment |
Guardrails Added
- Upstream-image services are represented in
CI.json.artifacts[]withkind=upstream-imageand inconfig.json.microservices[].repository.artifactSource; they are explicitly outside source-build producers. ci publish-user-servicereturns a structured blocked result for registeredupstream-imageservices instead of trying to interpretrepository.dockerfileas a source Dockerfile.- Local-manifest production deploy for reviewed artifact consumers is blocked before source materialization/build, so prod cannot silently fall back to target-side source build or a dirty worktree.
artifact-registry deploy-backend-coreis demoted to a structured deprecated result; backend-core production CD must enter throughdeploy apply --env prod.
Guardrail Evidence Matrix
| Guardrail name / result key | Command evidence | Legacy path covered | Deletion status |
|---|---|---|---|
upstream-image CI publish rejection |
bun scripts/cli.ts ci publish-user-service --service filebrowser --commit <full-sha> --dry-run returns ok=false, status=blocked, and upstream metadata |
File Browser accidentally entering Dockerfile CI | keep; deletion of docker-run repair waits for digest/mirror CD |
prod-artifact-consumer-local-manifest-blocked |
bun scripts/cli.ts deploy apply --file deploy.json --service frontend --dry-run returns ok=false, this error key, and points to deploy apply --env prod --service <service-id> --commit <full-sha> |
prod source-build fallback for reviewed artifact consumers | keep; local manifest mode may still be needed for non-prod/recovery until runbooks are replaced |
artifact-registry deploy-backend-core deprecated result |
bun scripts/cli.ts artifact-registry deploy-backend-core --commit <full-sha> returns ok=false, deprecated=true, and replacement deploy apply --env prod --service backend-core --commit <full-sha> |
backend-core prod CD bypassing deploy reconciler guardrails | keep name only as compatibility until all callers stop using it |
| prod unsupported result for services without artifact consumers | deploy apply --env prod --service <unsupported-service> --dry-run must return unsupported instead of falling back to source build |
target-side source build/maintenance-channel prod deploy | keep disabled until service-specific artifact consumers exist |
| backend-core/code-queue prod boundary | docs and deploy support matrix allow backend-core and D601 Code Queue only in dev validation for this phase | accidental prod validation entrypoints for backend-core or Code Queue | do not add executable prod test/deploy validation in this precheck |
The guarded-but-not-deletable paths are: server rebuild backend-core, server rebuild frontend, server rebuild baidu-netdisk, provider-gateway protected upgrade, native k3s bootstrap, k3sctl-adapter bridge repair, File Browser provider-local docker-run repair, and D601 dev/backend target-side rollout. They remain because they are bootstrap, recovery, diagnostic, or controlled dev paths; deleting them requires replacement runbooks or reviewed artifact consumers.
Safety Boundary
CI may build images, push to the D601 loopback registry and report immutable digests. CI must not run production CD, call deploy apply for production, mutate production namespaces, recreate production Compose services or update deploy.json.
backend-core and D601 code-queue remain restricted to dev image validation in this phase. Any future production rollout for them must be implemented as an explicit CD consumer change, not as a CI producer side effect.
Validation Boundary
This precheck uses lightweight parsing and dry-run evidence only. It intentionally does not run full check, e2e, Playwright, or other broad browser/runtime test suites on the master server because those are outside the precheck scope and may exceed master-server resource limits. backend-core and D601 code-queue production validation are also out of scope; backend-core dev rollout can be attempted only through the existing D601 dev path, and a provider-offline result is an infrastructure blocker rather than permission to validate production.