feat: extend main-server artifact consumers
This commit is contained in:
@@ -38,7 +38,7 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文
|
||||
- `bun scripts/cli.ts microservice list/status/health/diagnostics/tunnel-self-test/proxy`:管理和验证挂载在主 server、计算节点 Docker 或 k3s 控制面上的用户服务,`proxy` 支持受控 JSON body,OA Event Flow/Todo Note/Baidu Netdisk/Code Queue Manager on main-server、k3s Control/Code Queue 执行面/MDTODO/Decision Center/FindJob/Pipeline/MET Nonlinear on D601 的规则见 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts decision upload/list/show/health`:通过 backend-core 用户服务代理上传会议记录/决议 Markdown、列出记录和查看详情;Decision Center 运行在 D601 k3s,规则见 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts decision diary import/list/months/show`:把带日期标题的工作日志 Markdown 拆成 `YYYY-MM/YYYY-MM-DD.md` 日记条目并写入 PostgreSQL,规则见 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts deploy check/plan/apply [--file deploy.json|--env dev|prod] [--service <id>]`:按根目录 `deploy.json` 或 `origin/master:deploy.json#environments.<env>` 的服务 repo 和 commit 期望状态校验或更新用户服务;`--env dev` 当前开放 D601 `backend-core`/`frontend` persistent dev rollout 和 `baidu-netdisk` artifact-consumer validation,规则见 `docs/reference/deploy.md` 与 `docs/reference/dev-environment.md`。
|
||||
- `bun scripts/cli.ts deploy check/plan/apply [--file deploy.json|--env dev|prod] [--service <id>]`:按根目录 `deploy.json` 或 `origin/master:deploy.json#environments.<env>` 的服务 repo 和 commit 期望状态校验或更新用户服务;`--env dev` 当前开放 D601 `backend-core` persistent dev rollout 以及 `frontend`/`baidu-netdisk`/`decision-center`/`project-manager`/`oa-event-flow`/`code-queue-mgr` artifact-consumer validation,`todo-note` 仅 dry-run,规则见 `docs/reference/deploy.md` 与 `docs/reference/dev-environment.md`。
|
||||
- `bun scripts/cli.ts dev-env validate [--manifest path] [--kubectl-dry-run]` / `dev-env prewarm-images`:离线校验 D601 `unidesk-dev` 生产隔离护栏,或把开发底座基础镜像预热到 D601 原生 k3s containerd,规则见 `docs/reference/deploy.md` 与 `docs/reference/microservices.md`。
|
||||
- `bun scripts/cli.ts artifact-registry plan|render|status|health|install|deploy-backend-core|deploy-service`:管理 D601 host-managed CNCF Distribution registry,并通过短生命周期 relay 或 D601 pull/import 做 commit-pinned pull-only artifact CD;`deploy-backend-core` 是 deprecated 兼容名,backend-core prod CD 标准入口是 `deploy apply --env prod --service backend-core`,规则见 `docs/reference/artifact-registry.md`。
|
||||
- `bun scripts/cli.ts ci install/status/run/publish-backend-core/publish-user-service/run-dev-e2e/logs`:在 D601 原生 k3s 上安装和运行 Tekton CI,支持每 commit 检查、Code Queue 只读性能门禁、backend-core 与 user-service commit-pinned 镜像发布和手动触发的 `origin/master:deploy.json#environments.dev` 临时 namespace e2e;`run-dev-e2e` 的 Git 控制 runner、短 launcher 和 no-CD 边界见 `docs/reference/dev-ci-runner.md`,Tekton 规则见 `docs/reference/ci.md`。
|
||||
|
||||
@@ -127,6 +127,33 @@
|
||||
"imageRef": "127.0.0.1:5000/unidesk/frontend:{{sourceCommit}}",
|
||||
"digestRef": "127.0.0.1:5000/unidesk/frontend@{{digest}}"
|
||||
},
|
||||
{
|
||||
"serviceId": "project-manager",
|
||||
"sourceRepo": "https://github.com/pikasTech/unidesk",
|
||||
"dockerfile": "src/components/microservices/project-manager/Dockerfile",
|
||||
"imageRepository": "unidesk/project-manager",
|
||||
"imageRef": "127.0.0.1:5000/unidesk/project-manager:{{sourceCommit}}",
|
||||
"digestRef": "127.0.0.1:5000/unidesk/project-manager@{{digest}}",
|
||||
"publishCommand": "bun scripts/cli.ts ci publish-user-service --service project-manager --commit <full-sha>"
|
||||
},
|
||||
{
|
||||
"serviceId": "oa-event-flow",
|
||||
"sourceRepo": "https://github.com/pikasTech/unidesk",
|
||||
"dockerfile": "src/components/microservices/oa-event-flow/Dockerfile",
|
||||
"imageRepository": "unidesk/oa-event-flow",
|
||||
"imageRef": "127.0.0.1:5000/unidesk/oa-event-flow:{{sourceCommit}}",
|
||||
"digestRef": "127.0.0.1:5000/unidesk/oa-event-flow@{{digest}}",
|
||||
"publishCommand": "bun scripts/cli.ts ci publish-user-service --service oa-event-flow --commit <full-sha>"
|
||||
},
|
||||
{
|
||||
"serviceId": "code-queue-mgr",
|
||||
"sourceRepo": "https://github.com/pikasTech/unidesk",
|
||||
"dockerfile": "src/components/microservices/code-queue-mgr/Dockerfile",
|
||||
"imageRepository": "unidesk/code-queue-mgr",
|
||||
"imageRef": "127.0.0.1:5000/unidesk/code-queue-mgr:{{sourceCommit}}",
|
||||
"digestRef": "127.0.0.1:5000/unidesk/code-queue-mgr@{{digest}}",
|
||||
"publishCommand": "bun scripts/cli.ts ci publish-user-service --service code-queue-mgr --commit <full-sha>"
|
||||
},
|
||||
{
|
||||
"serviceId": "backend-core",
|
||||
"sourceRepo": "https://github.com/pikasTech/unidesk",
|
||||
|
||||
+20
@@ -107,6 +107,26 @@
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "54c1f8e165f90fa8509fda1f0c01f8c3fa82cbee"
|
||||
},
|
||||
{
|
||||
"id": "todo-note",
|
||||
"repo": "https://gitee.com/Lyon1998/todo_note",
|
||||
"commitId": "a14ce0eb855a685fa17b47adacd54623e72cd2ff"
|
||||
},
|
||||
{
|
||||
"id": "project-manager",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "0c3cdb4ee06a23361ed511a2da033d67b53d16f4"
|
||||
},
|
||||
{
|
||||
"id": "oa-event-flow",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "0c3cdb4ee06a23361ed511a2da033d67b53d16f4"
|
||||
},
|
||||
{
|
||||
"id": "code-queue-mgr",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
"commitId": "22b02e7ce98a32647f8c3962dbf90aafabd53ff0"
|
||||
},
|
||||
{
|
||||
"id": "code-queue",
|
||||
"repo": "https://github.com/pikasTech/unidesk",
|
||||
|
||||
@@ -108,6 +108,11 @@ services:
|
||||
CODE_QUEUE_REMOTE_WORKDIR: "${UNIDESK_CODE_QUEUE_REMOTE_WORKDIR:-/home/ubuntu}"
|
||||
LOG_FILE: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_code-queue-mgr.jsonl"
|
||||
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}"
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REF: "${UNIDESK_CODE_QUEUE_MGR_DEPLOY_REF:-deploy.json#environments.prod.services.code-queue-mgr}"
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_SERVICE_ID: "${UNIDESK_CODE_QUEUE_MGR_DEPLOY_SERVICE_ID:-code-queue-mgr}"
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO: "${UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO:-}"
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT: "${UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT:-}"
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT: "${UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT:-}"
|
||||
volumes:
|
||||
- ${UNIDESK_LOG_DIR}:/var/log/unidesk
|
||||
healthcheck:
|
||||
@@ -144,6 +149,11 @@ services:
|
||||
TODO_NOTE_REMINDER_SCAN_INTERVAL_MS: "${UNIDESK_TODO_NOTE_REMINDER_SCAN_INTERVAL_MS:-30000}"
|
||||
TODO_NOTE_REMINDER_CLAUDEQQ_TIMEOUT_MS: "${UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_TIMEOUT_MS:-15000}"
|
||||
TODO_NOTE_REMINDER_CLAUDEQQ_SEND_ATTEMPTS: "${UNIDESK_TODO_NOTE_REMINDER_CLAUDEQQ_SEND_ATTEMPTS:-3}"
|
||||
UNIDESK_DEPLOY_REF: "${UNIDESK_TODO_NOTE_DEPLOY_REF:-deploy.json#environments.prod.services.todo-note}"
|
||||
UNIDESK_DEPLOY_SERVICE_ID: "${UNIDESK_TODO_NOTE_DEPLOY_SERVICE_ID:-todo-note}"
|
||||
UNIDESK_DEPLOY_REPO: "${UNIDESK_TODO_NOTE_DEPLOY_REPO:-}"
|
||||
UNIDESK_DEPLOY_COMMIT: "${UNIDESK_TODO_NOTE_DEPLOY_COMMIT:-}"
|
||||
UNIDESK_DEPLOY_REQUESTED_COMMIT: "${UNIDESK_TODO_NOTE_DEPLOY_REQUESTED_COMMIT:-}"
|
||||
volumes:
|
||||
- ${UNIDESK_LOG_DIR}:/var/log/unidesk
|
||||
healthcheck:
|
||||
@@ -176,6 +186,11 @@ services:
|
||||
PIPELINE_OA_BRIDGE_RUN_LIMIT: "${UNIDESK_PIPELINE_OA_BRIDGE_RUN_LIMIT:-50}"
|
||||
LOG_FILE: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_oa-event-flow.jsonl"
|
||||
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}"
|
||||
UNIDESK_DEPLOY_REF: "${UNIDESK_OA_EVENT_FLOW_DEPLOY_REF:-deploy.json#environments.prod.services.oa-event-flow}"
|
||||
UNIDESK_DEPLOY_SERVICE_ID: "${UNIDESK_OA_EVENT_FLOW_DEPLOY_SERVICE_ID:-oa-event-flow}"
|
||||
UNIDESK_DEPLOY_REPO: "${UNIDESK_OA_EVENT_FLOW_DEPLOY_REPO:-}"
|
||||
UNIDESK_DEPLOY_COMMIT: "${UNIDESK_OA_EVENT_FLOW_DEPLOY_COMMIT:-}"
|
||||
UNIDESK_DEPLOY_REQUESTED_COMMIT: "${UNIDESK_OA_EVENT_FLOW_DEPLOY_REQUESTED_COMMIT:-}"
|
||||
volumes:
|
||||
- ${UNIDESK_LOG_DIR}:/var/log/unidesk
|
||||
healthcheck:
|
||||
@@ -202,6 +217,11 @@ services:
|
||||
DATABASE_POOL_MAX: "${UNIDESK_PROJECT_MANAGER_DATABASE_POOL_MAX:-1}"
|
||||
LOG_FILE: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_project-manager.jsonl"
|
||||
UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}"
|
||||
UNIDESK_DEPLOY_REF: "${UNIDESK_PROJECT_MANAGER_DEPLOY_REF:-deploy.json#environments.prod.services.project-manager}"
|
||||
UNIDESK_DEPLOY_SERVICE_ID: "${UNIDESK_PROJECT_MANAGER_DEPLOY_SERVICE_ID:-project-manager}"
|
||||
UNIDESK_DEPLOY_REPO: "${UNIDESK_PROJECT_MANAGER_DEPLOY_REPO:-}"
|
||||
UNIDESK_DEPLOY_COMMIT: "${UNIDESK_PROJECT_MANAGER_DEPLOY_COMMIT:-}"
|
||||
UNIDESK_DEPLOY_REQUESTED_COMMIT: "${UNIDESK_PROJECT_MANAGER_DEPLOY_REQUESTED_COMMIT:-}"
|
||||
volumes:
|
||||
- ${UNIDESK_LOG_DIR}:/var/log/unidesk
|
||||
healthcheck:
|
||||
|
||||
@@ -123,7 +123,7 @@ docker compose -p unidesk-artifact-registry -f /home/ubuntu/.unidesk/artifact-re
|
||||
6. Compose runtime retag 为 Compose 使用的镜像名,并执行 `docker compose up -d --no-build --no-deps --force-recreate <service>`;k3s runtime 设置 Deployment image/env/annotations 并等待 rollout。
|
||||
7. 部署后通过 image label、runtime env、health payload 验证 live commit。
|
||||
|
||||
Baidu Netdisk is the first main-server direct user-service sample in this flow. Its dev validation command and prod CD command both consume `127.0.0.1:5000/unidesk/baidu-netdisk:<commit>` and must not build source on the master server. Frontend is the standard UniDesk UI artifact sample: CI publishes `127.0.0.1:5000/unidesk/frontend:<commit>`, production CD consumes that artifact into the master-server Compose `frontend` service, and dev CD consumes the same artifact into D601 native k3s `frontend-dev`. Neither path may use `server rebuild frontend`, dirty source, mutable `latest`, or target-side frontend source builds as release truth. Decision Center follows the same artifact-consumer pattern in both dev and prod, except the runtime target is native k3s on D601 instead of the master-server Compose stack. The consumer must check the registry manifest, pull the commit-pinned image, import it into `/run/k3s/containerd/containerd.sock`, set the Deployment image to the commit tag, stamp `UNIDESK_DEPLOY_*` env/annotations, and reject an old healthy revision if the live commit or requested commit does not match.
|
||||
Baidu Netdisk is the first main-server direct user-service sample in this flow. Its dev validation command and prod CD command both consume `127.0.0.1:5000/unidesk/baidu-netdisk:<commit>` and must not build source on the master server. Frontend is the standard UniDesk UI artifact sample: CI publishes `127.0.0.1:5000/unidesk/frontend:<commit>`, production CD consumes that artifact into the master-server Compose `frontend` service, and dev CD consumes the same artifact into D601 native k3s `frontend-dev`. Project Manager and OA Event Flow use the same master-server Compose artifact-consumer shape as Baidu Netdisk, with `project-manager-backend` and `oa-event-flow-backend` as the runtime containers. Code Queue Manager is supported as an artifact consumer for validation, but prod live apply is supervisor-gated. Todo Note currently returns a structured runtime-verification block until the checked-in health contract proves `deploy.commit` and `deploy.requestedCommit`. Neither path may use `server rebuild frontend`, dirty source, mutable `latest`, or target-side frontend source builds as release truth. Decision Center follows the same artifact-consumer pattern in both dev and prod, except the runtime target is native k3s on D601 instead of the master-server Compose stack. The consumer must check the registry manifest, pull the commit-pinned image, import it into `/run/k3s/containerd/containerd.sock`, set the Deployment image to the commit tag, stamp `UNIDESK_DEPLOY_*` env/annotations, and reject an old healthy revision if the live commit or requested commit does not match.
|
||||
|
||||
这个 CD 路径必须满足:
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ The root `deploy.json` is the single desired-state source for both prod and dev.
|
||||
|
||||
The optional non-service execution declaration under `environments.dev` is intentionally not specified here. The only currently allowed declaration is `ci`, and its authoritative `repo`, `scriptPath`, `timeoutMs`, short launcher, host fetch boundary and no-CD rules are defined only in `docs/reference/dev-ci-runner.md`.
|
||||
|
||||
Environment mode never reads the local dirty working tree manifest. `deploy check --env ...`, `deploy plan --env ...` and `deploy apply --env ...` fetch `origin/master`, read `origin/master:deploy.json`, select `environments.<env>`, and report the manifest commit/blob, service commit IDs, target namespace, database fingerprint and Provider identity. `deploy apply --env dev` is currently enabled for persistent D601 dev `backend-core` target-side rollout and for `frontend`/`baidu-netdisk`/`decision-center` artifact consumers; all other D601 services remain rejected before runtime mutation. `deploy apply --env prod` exposes only reviewed D601 registry artifact consumers (`backend-core`, `frontend`, `baidu-netdisk`, `decision-center`). Production backend-core artifact CD is a separate executor because its build target is D601 CI while its runtime target is the master server. The default user-service delivery policy, including CI build, registry publication, dev validation, production CD and manual acceptance, is documented in `docs/reference/user-service-delivery.md`.
|
||||
Environment mode never reads the local dirty working tree manifest. `deploy check --env ...`, `deploy plan --env ...` and `deploy apply --env ...` fetch `origin/master`, read `origin/master:deploy.json`, select `environments.<env>`, and report the manifest commit/blob, service commit IDs, target namespace, database fingerprint and Provider identity. `deploy apply --env dev` is currently enabled for persistent D601 dev `backend-core` target-side rollout and for reviewed artifact consumers `frontend`, `baidu-netdisk`, `decision-center`, `project-manager`, `oa-event-flow`, and `code-queue-mgr`; `todo-note` remains dry-run only until runtime verification is proven. `deploy apply --env prod` exposes only reviewed registry artifact consumers (`backend-core`, `frontend`, `baidu-netdisk`, `decision-center`, `project-manager`, `oa-event-flow`), while `code-queue-mgr` remains supervisor-gated and `todo-note` stays blocked. Production backend-core artifact CD is a separate executor because its build target is D601 CI while its runtime target is the master server. The default user-service delivery policy, including CI build, registry publication, dev validation, production CD and manual acceptance, is documented in `docs/reference/user-service-delivery.md`.
|
||||
|
||||
For services with reviewed production artifact consumers, local-manifest `deploy apply --file ...` is not a production fallback. The CLI blocks `backend-core`, `frontend`, `baidu-netdisk` and `decision-center` before source materialization or Docker build and directs operators to `deploy apply --env prod --service <id> --commit <full-sha>`. This prevents a dirty worktree, local manifest or target-side source build from bypassing the pull-only artifact CD guardrails. The broader precheck and legacy-path classification live in `docs/reference/cicd-standardization.md`.
|
||||
|
||||
@@ -85,7 +85,7 @@ Phase 3 introduces the dev backend/frontend manifest at `src/components/microser
|
||||
|
||||
`backend-core-dev` must use `unidesk-dev-runtime-config` and `unidesk-dev-runtime-secrets`, connect to `postgres-dev.../unidesk_dev`, expose HTTP on 8080 and provider ingress on 8081, and write logs under `/var/log/unidesk-dev`. `frontend-dev` must set `CORE_INTERNAL_URL=http://backend-core-dev.unidesk-dev.svc.cluster.local:8080` and must not proxy to production backend-core.
|
||||
|
||||
The manifest keeps placeholder image tags and deploy commit values in source control. The controlled `deploy apply --env dev --service backend-core` path fetches `origin/master:deploy.json`, materializes the requested source commit on D601, narrows the dev core control manifest to the selected Service/Deployment pair, replaces placeholders with the requested commit and dev image tag, builds on D601, imports the image into native k3s containerd, applies only the `unidesk-dev` objects and stamps the Deployment. `deploy apply --env dev --service frontend` uses the same selected dev manifest objects but consumes the existing D601 registry artifact `127.0.0.1:5000/unidesk/frontend:<commit>` instead of building frontend source on the target. Decision Center uses the same dev namespace but follows the D601 registry artifact consumer path instead of a source build: it verifies the commit-pinned image in D601 registry, imports it into native k3s containerd, applies the dev Decision Center manifest, stamps the Deployment and verifies live commit/requestedCommit. Client dry-run and static validation remain useful checks before controlled apply:
|
||||
The manifest keeps placeholder image tags and deploy commit values in source control. The controlled `deploy apply --env dev --service backend-core` path fetches `origin/master:deploy.json`, materializes the requested source commit on D601, narrows the dev core control manifest to the selected Service/Deployment pair, replaces placeholders with the requested commit and dev image tag, builds on D601, imports the image into native k3s containerd, applies only the `unidesk-dev` objects and stamps the Deployment. `deploy apply --env dev --service frontend`, `decision-center`, `project-manager` and `oa-event-flow` use the same selected dev manifest objects but consume the existing D601 registry artifact instead of building source on the target. `code-queue-mgr` uses the same dev manifest validation path for Compose artifact consumer checks; live prod apply remains supervisor-gated. `todo-note` currently returns a structured runtime-verification block until the checked-in health contract proves `deploy.commit` and `deploy.requestedCommit`. Client dry-run and static validation remain useful checks before controlled apply:
|
||||
|
||||
- `bun scripts/cli.ts dev-env validate --manifest src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k8s.yaml`
|
||||
- `KUBECONFIG=/etc/rancher/k3s/k3s.yaml kubectl apply --dry-run=client --validate=false -f src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k8s.yaml`
|
||||
@@ -108,7 +108,7 @@ Maintenance-channel direct D601 apply must not deploy dev Code Queue; the CLI re
|
||||
|
||||
`bun scripts/cli.ts deploy plan --env dev [--service <id>]` reads `origin/master:deploy.json#environments.dev` and prints a dry-run environment plan without checking or mutating live runtime resources. `deploy check --env dev` uses the same dry-run environment plan. `--env prod` is available for parity as a dry-run planning path; it reads `origin/master:deploy.json#environments.prod` and must not use a dirty local `deploy.json`.
|
||||
|
||||
`bun scripts/cli.ts deploy apply [--file deploy.json | --env dev|prod] [--service <id>] [--commit <full-sha>] [--dry-run] [--force]` starts an asynchronous job only for supported targets. Use `bun scripts/cli.ts job status <jobId> --tail-bytes 30000` to observe progress. `--dry-run` resolves the same plan but does not build or replace runtime objects. `--force` rebuilds even when the live commit matches. Environment apply is not the dev e2e trigger; use `bun scripts/cli.ts ci run-dev-e2e` for the Git-controlled temporary namespace smoke flow. `--env dev` apply is enabled for persistent D601 `backend-core` target-side rollout and for `frontend`/`baidu-netdisk`/`decision-center` artifact consumers. `--env prod` apply exposes the D601 registry artifact consumer for `backend-core`, `frontend`, `baidu-netdisk`, and `decision-center`. Unsupported prod services return a structured `unsupported` payload instead of silently falling back to a maintenance-channel source build.
|
||||
`bun scripts/cli.ts deploy apply [--file deploy.json | --env dev|prod] [--service <id>] [--commit <full-sha>] [--dry-run] [--force]` starts an asynchronous job only for supported targets. Use `bun scripts/cli.ts job status <jobId> --tail-bytes 30000` to observe progress. `--dry-run` resolves the same plan but does not build or replace runtime objects. `--force` rebuilds even when the live commit matches. Environment apply is not the dev e2e trigger; use `bun scripts/cli.ts ci run-dev-e2e` for the Git-controlled temporary namespace smoke flow. `--env dev` apply is enabled for persistent D601 `backend-core` target-side rollout and for `frontend`/`baidu-netdisk`/`decision-center`/`project-manager`/`oa-event-flow`/`code-queue-mgr` artifact consumers, while `todo-note` remains dry-run only until runtime verification is proven. `--env prod` apply exposes the D601 registry artifact consumer for `backend-core`, `frontend`, `baidu-netdisk`, `decision-center`, `project-manager`, and `oa-event-flow`; `code-queue-mgr` prod live apply is supervisor-gated and `todo-note` remains blocked. Unsupported prod services return a structured `unsupported` payload instead of silently falling back to a maintenance-channel source build.
|
||||
|
||||
All deploy commands output JSON. Long operations must use `.state/jobs/` and bounded log tails; no deploy path may succeed with missing progress output.
|
||||
|
||||
@@ -174,8 +174,8 @@ Main server targets may build without a proxy unless a service explicitly requir
|
||||
|
||||
The reconciler selects the executor from `config.json`:
|
||||
|
||||
- `deployment.mode=unidesk-direct` on `main-server`: the legacy/local manifest executor builds the image on the main server, then uses the fixed UniDesk Compose project and `up -d --no-build --no-deps --force-recreate <service>`. Reviewed artifact-consumer services such as `frontend` and `baidu-netdisk` use the D601 registry pull-only path for `--env dev` and `--env prod` instead.
|
||||
- `deployment.mode=internal-sidecar` on `main-server`: use the same main-server target-side source export, Docker build, image label stamping, fixed Compose project replacement and live commit verification as direct Compose services. This class is for private sidecars such as `code-queue-mgr`; it is still versioned by `deploy.json.commitId`, not by the operator's current worktree.
|
||||
- `deployment.mode=unidesk-direct` on `main-server`: the legacy/local manifest executor builds the image on the main server, then uses the fixed UniDesk Compose project and `up -d --no-build --no-deps --force-recreate <service>`. Reviewed artifact-consumer services such as `frontend`, `baidu-netdisk`, `project-manager` and `oa-event-flow` use the D601 registry pull-only path for `--env dev` and `--env prod` instead.
|
||||
- `deployment.mode=internal-sidecar` on `main-server`: use the same main-server target-side source export, Docker build, image label stamping, fixed Compose project replacement and live commit verification as direct Compose services. This class is for private sidecars such as `code-queue-mgr`; it is still versioned by `deploy.json.commitId`, not by the operator's current worktree, and prod live apply remains supervisor-gated.
|
||||
- `deployment.mode=unidesk-direct` on a provider: this executor is disabled for D601 service deployment. The historical behavior dispatched `host.ssh` to the provider, built on the provider, then used the service's provider-local compose file and project; that shape must not remain a second deployment control plane.
|
||||
- Control bridges that UniDesk needs in order to inspect or repair an orchestrator must stay in this direct class. In particular, `k3sctl-adapter` is a UniDesk-managed bridge to native k3s and must remain outside k3s; Docker packaging on Docker Desktop/WSL must create an explicit host-local bridge, currently an adapter-container SSH local tunnel, to reach `/etc/rancher/k3s/k3s.yaml` and WSL `127.0.0.1:6443`.
|
||||
- `deployment.mode=k3sctl-managed`: the target behavior is to build on the active control target unless the service has a reviewed artifact-consumer exception, verify native k3s on the host OS/WSL distro, import the image into native k3s/containerd, apply the existing Kubernetes manifest, stamp the Deployment and wait for rollout. On D601, persistent dev apply is currently allowed for `backend-core` target-side build plus `frontend` and `decision-center` artifact consumption in `unidesk-dev`; normal production services still cannot use a maintenance-channel direct rollout. The executor must use the native kubeconfig and containerd socket, for example `/etc/rancher/k3s/k3s.yaml` and `/run/k3s/containerd/containerd.sock`; running k3s itself in Docker is forbidden for both control-plane and worker nodes. A `rancher/k3s` image or legacy container may only be used as a temporary artifact source during migration, and any active containerized k3s control plane must be stopped before verification succeeds. The executor must preload a valid `rancher/mirrored-pause:3.6` sandbox image into native k3s containerd through the provider-gateway one-shot egress path, verify its entrypoint is `/pause`, and reject fake or sleep-based replacement images. Code Queue's k3s migration executor must also stop/remove the legacy direct Docker `code-queue-backend` after k3s rollout, so there is never a second scheduler running beside the native k3s scheduler.
|
||||
|
||||
@@ -30,7 +30,7 @@ CLI 会优先使用 `docker compose` v2 plugin;当 v2 plugin 不存在时才
|
||||
|
||||
Compose v2 安装后仍然必须遵守 UniDesk 的服务控制入口:全栈生命周期用 `server start` / `server stop`,单服务重建用 `server rebuild <service>`。不要因为 v2 可用就直接在生产栈上手工执行未纳入 CLI 的 `up --build`、`down -v` 或跨项目清理命令;所有会影响容器的动作都应保持 job 可观测、Compose project 固定、database named volume 保留。主 server Compose 命令必须从 `providerGateway.upgrade.hostProjectRoot` 指定的 canonical UniDesk 根目录运行,临时 worktree、Code Queue 导出目录或实验分支不得复用生产 `-p unidesk` 和固定 `container_name` 去替换生产容器。
|
||||
|
||||
版本化用户服务部署优先使用 `bun scripts/cli.ts deploy apply` 已支持的受控路径;D601 persistent dev apply 当前支持 `backend-core` target-side rollout、`frontend` artifact consumer,`baidu-netdisk` dev 验证也使用 artifact consumer,dev desired-state smoke 使用 `ci run-dev-e2e`。`deploy.json` 只声明服务 `id`、`repo` 和 `commitId`;目标节点、Dockerfile、Compose、Kubernetes manifest、健康检查和代理路径继续来自 `config.json` 与现有 manifest。主 server 直管微服务和内部 sidecar,例如 `code-queue-mgr`,也必须支持这一路径:`deploy apply --service code-queue-mgr` 从 `deploy.json` 指定 commit 导出源码、构建镜像、替换固定 Compose service 并验证运行中镜像/健康信息的 commit。部署默认遵循 target-side build:服务部署到哪台 target,就在哪台 target 从 remote commit 导出源码、一次性代理构建镜像并部署;不得把中心构建镜像作为默认分发路径,也不得用 `docker commit` 或脏 worktree 作为部署输入。production backend-core 是明确例外;`frontend` 是用户服务 UI / 前端镜像化样板,`baidu-netdisk` 是主 server 直管微服务的镜像化样板:D601 CI 构建并推送 commit-pinned 镜像到 D601 artifact registry,dev/prod CD 只拉取、retag 或导入、recreate/rollout 和验证,不在 master server 或 dev target 执行 frontend Compose build。完整规则见 `docs/reference/deploy.md`,D601 dev/Rust 边界见 `docs/reference/dev-environment.md`,artifact registry 见 `docs/reference/artifact-registry.md`。
|
||||
版本化用户服务部署优先使用 `bun scripts/cli.ts deploy apply` 已支持的受控路径;D601 persistent dev apply 当前支持 `backend-core` target-side rollout,以及 `frontend`、`baidu-netdisk`、`decision-center`、`project-manager`、`oa-event-flow` 和 `code-queue-mgr` 的 artifact consumer validation,`todo-note` 仅能做 runtime-verification dry-run。`deploy.json` 只声明服务 `id`、`repo` 和 `commitId`;目标节点、Dockerfile、Compose、Kubernetes manifest、健康检查和代理路径继续来自 `config.json` 与现有 manifest。主 server 直管微服务和内部 sidecar,例如 `code-queue-mgr`,也必须支持这一路径:`deploy apply --service code-queue-mgr` 从 `deploy.json` 指定 commit 导出源码、构建镜像、替换固定 Compose service 并验证运行中镜像/健康信息的 commit,但 prod live apply 仍需 supervisor 确认。部署默认遵循 target-side build:服务部署到哪台 target,就在哪台 target 从 remote commit 导出源码、一次性代理构建镜像并部署;不得把中心构建镜像作为默认分发路径,也不得用 `docker commit` 或脏 worktree 作为部署输入。production backend-core 是明确例外;`frontend` 是用户服务 UI / 前端镜像化样板,`baidu-netdisk` 是主 server 直管微服务的镜像化样板:D601 CI 构建并推送 commit-pinned 镜像到 D601 artifact registry,dev/prod CD 只拉取、retag 或导入、recreate/rollout 和验证,不在 master server 或 dev target 执行 frontend Compose build。完整规则见 `docs/reference/deploy.md`,D601 dev/Rust 边界见 `docs/reference/dev-environment.md`,artifact registry 见 `docs/reference/artifact-registry.md`。
|
||||
|
||||
## Main Server Swap
|
||||
|
||||
@@ -46,7 +46,7 @@ swap 管理不能被强塞进所有热路径。`server start/status` 可以暴
|
||||
|
||||
## Single Service Rebuild
|
||||
|
||||
前端、本机 provider-gateway、dev-frontend-proxy 或主 server 承载的 Todo Note/Code Queue Manager/Project Manager/Baidu Netdisk/OA Event Flow 用户服务需要非版本化本地重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core`、`frontend`、`dev-frontend-proxy`、`provider-gateway`、`todo-note`、`code-queue-mgr`、`project-manager`、`baidu-netdisk` 或 `oa-event-flow`。需要按 commit 上线或恢复到 desired-state 时必须改用 `bun scripts/cli.ts deploy apply --service <id>`、backend-core artifact CD、`deploy apply --env dev|prod --service frontend` artifact consumer 或 `deploy apply --env dev|prod --service baidu-netdisk` artifact consumer;直管微服务也不能把脏工作树或手工重建作为部署真相。Rust backend-core 迭代不得在 master server 用 `server rebuild backend-core` 编译,生产 backend-core 也不得用该命令完成 Rust 构建,必须走 D601 dev deploy/CI 或 D601 artifact registry CD。`server rebuild frontend` 和 `server rebuild baidu-netdisk` 只作为维护/非标准路径保留,不得作为标准发布完成证据。D601 Code Queue 执行面、File Browser、FindJob、Pipeline、MET Nonlinear 和 ClaudeQQ 部署在计算节点,不属于主 server Compose 可重建服务;其中 D601 Code Queue 执行面不得再通过 `codex deploy` 或维护通道直连 D601 部署;未来正式 CD 必须经受控 target-side 路径执行 build-first、rollout 和 live commit 验证。
|
||||
前端、本机 provider-gateway、dev-frontend-proxy 或主 server 承载的 Todo Note/Code Queue Manager/Project Manager/Baidu Netdisk/OA Event Flow 用户服务需要非版本化本地重建时,统一使用 `bun scripts/cli.ts server rebuild <service>`,其中 `<service>` 只能是 `backend-core`、`frontend`、`dev-frontend-proxy`、`provider-gateway`、`todo-note`、`code-queue-mgr`、`project-manager`、`baidu-netdisk` 或 `oa-event-flow`。需要按 commit 上线或恢复到 desired-state 时必须改用 `bun scripts/cli.ts deploy apply --service <id>`、backend-core artifact CD、`deploy apply --env dev|prod --service frontend` artifact consumer 或 `deploy apply --env dev|prod --service baidu-netdisk` artifact consumer;直管微服务也不能把脏工作树或手工重建作为部署真相。`server rebuild frontend`、`server rebuild baidu-netdisk`、`server rebuild project-manager`、`server rebuild oa-event-flow`、`server rebuild todo-note` 和 `server rebuild code-queue-mgr` 都只作为维护/非标准路径保留,不得作为标准发布完成证据。Rust backend-core 迭代不得在 master server 用 `server rebuild backend-core` 编译,生产 backend-core 也不得用该命令完成 Rust 构建,必须走 D601 dev deploy/CI 或 D601 artifact registry CD。D601 Code Queue 执行面、File Browser、FindJob、Pipeline、MET Nonlinear 和 ClaudeQQ 部署在计算节点,不属于主 server Compose 可重建服务;其中 D601 Code Queue 执行面不得再通过 `codex deploy` 或维护通道直连 D601 部署;未来正式 CD 必须经受控 target-side 路径执行 build-first、rollout 和 live commit 验证。
|
||||
|
||||
frontend 改动必须明确上线到公网:修改 `src/components/frontend/src/`、`src/components/frontend/public/style.css`、frontend 使用的共享 TSX/TS 模块或 WebUI 导航后,标准发布顺序是先把 pushed commit 交给 `bun scripts/cli.ts ci publish-user-service --service frontend --commit <full-sha>`,再用 `bun scripts/cli.ts deploy apply --env dev --service frontend` 和 `bun scripts/cli.ts deploy apply --env prod --service frontend` 消费同一个 commit-pinned artifact。公网 WebUI 的 `/app.js` 是 `unidesk-frontend` 镜像内运行时 bundle;只改工作区文件、只跑 `bun run check`、只跑 `Bun.build`、只刷新浏览器或只 `server rebuild frontend` 都不能作为标准版本发布证据。
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ UniDesk 用户服务是挂载到 UniDesk 核心服务上的、面向用户使用
|
||||
- 代理路径:只允许 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`,用于保持 Todo Note 原有清单创建/删除、任务增删改、提醒、展开/收起、移动、撤销/重做等功能。
|
||||
- UniDesk 前端:`用户服务 / Todo Note` React 页面负责展示清单列表、树形任务、筛选、提醒、拖放/上移下移、撤销/重做、字号控制和显式原始 JSON 按钮。
|
||||
|
||||
Todo Note 在 UniDesk 语境中按纯后端服务管理:不得继续公开 Todo Note 自身 Vite/Web 前端,也不得把 `4211` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/todo-note/...` 同源代理访问 Todo Note 后端。
|
||||
Todo Note 在 UniDesk 语境中按纯后端服务管理:不得继续公开 Todo Note 自身 Vite/Web 前端,也不得把 `4211` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/todo-note/...` 同源代理访问 Todo Note 后端;当前 live prod artifact CD 仍被阻塞,直到 checked-in health contract 能证明 `deploy.commit` 和 `deploy.requestedCommit`。
|
||||
|
||||
Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网执行 `/root/todo_note/scripts/migrate-json-to-pg.ts`,并显式指向主 PostgreSQL:`docker run --rm --network unidesk_default -v /root/todo_note:/app -w /app -e DATABASE_URL='postgres://unidesk:unidesk_dev_password@database:5432/unidesk' oven/bun:1-alpine bun scripts/migrate-json-to-pg.ts`。迁移脚本必须输出 `importedInstances: 5`、`totalTodos: 100`、`completedTodos: 54` 这一类可审计摘要,不能只依赖前端页面观察。
|
||||
|
||||
@@ -76,7 +76,7 @@ Todo Note 数据迁移后必须验证:`microservice proxy todo-note /api/insta
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`。
|
||||
- UniDesk 前端:`用户服务 / OA Event Flow` React 页面负责展示服务健康、事件表、tag 过滤、live stream 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮。
|
||||
|
||||
OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得暴露公网端口,不得把事件或统计权威状态写入 `.state/`;Code Queue 与 Pipeline 都必须通过该服务发布事实事件、订阅 tag stream 和读取统计中心。共享事件流、统计中心和完成门禁见 `docs/reference/oa-event-flow.md`。
|
||||
OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得暴露公网端口,不得把事件或统计权威状态写入 `.state/`;标准发布为 `bun scripts/cli.ts ci publish-user-service --service oa-event-flow --commit <full-sha>`,dev/prod 都通过 `bun scripts/cli.ts deploy apply --env dev|prod --service oa-event-flow` 消费同一 commit-pinned artifact 并验证 `/health.deploy.commit`;Code Queue 与 Pipeline 都必须通过该服务发布事实事件、订阅 tag stream 和读取统计中心。共享事件流、统计中心和完成门禁见 `docs/reference/oa-event-flow.md`。
|
||||
|
||||
### Code Queue Manager On Main Server
|
||||
|
||||
@@ -85,6 +85,7 @@ OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得
|
||||
- 职责:队列 CRUD、任务提交、批量提交、任务移动、queued prompt edit、已读状态、历史摘要、overview、stats、summary、prompt、output/transcript/trace 的轻量 PostgreSQL 读取。
|
||||
- 非职责:不运行 Codex/OpenCode,不包含 Playwright/Chromium,不持有 Docker socket,不创建 dev-container,不执行 judge,不管理 active run steer/interrupt,不做任务调度或 runner。
|
||||
- 资源边界:目标常驻内存不超过 100 MB,默认 PostgreSQL pool 为 `CODE_QUEUE_MGR_DATABASE_POOL_MAX=2` 与 `CODE_QUEUE_TRACE_DATABASE_POOL_MAX=1`,`/health` 必须暴露 `role=master-control-plane`、`schemaReady`、连接池上限和 `noRunnerDependencies=true`。
|
||||
- 标准发布:`bun scripts/cli.ts ci publish-user-service --service code-queue-mgr --commit <full-sha>` 会在 D601 registry 发布 `127.0.0.1:5000/unidesk/code-queue-mgr:<commit>`;dev 可以做 `bun scripts/cli.ts deploy apply --env dev --service code-queue-mgr` 验证,prod 只允许 `bun scripts/cli.ts deploy apply --env prod --service code-queue-mgr --dry-run` 或经 supervisor 单独确认后的 live apply。
|
||||
- Runtime healthcheck:生产镜像是 Rust slim runtime,不包含 Bun;Docker Compose healthcheck 必须使用镜像内 `code-queue-mgr --healthcheck` 对 `127.0.0.1:4278/health` 做带超时探针,或使用等价的 Rust binary 子命令。不得把 TypeScript/Bun `fetch` 探针用于 `code-queue-mgr`,否则 `server rebuild code-queue-mgr` 的 post-up validation 会误判失败。
|
||||
- 路由:CLI/WebUI 仍只访问 `/api/microservices/code-queue/proxy/...`;backend-core 在内部把控制/读取路径转到 `code-queue-mgr`,把 active run、judge、dev-container、执行面健康和 scheduler 相关路径转到 D601 执行面。
|
||||
- 行为兼容:提交与 queued prompt edit 必须保留 Code Queue 环境提示注入、`--reference-task-id`/引用输入解析和引用任务上下文注入,避免 master 控制面路径与 D601 原写服务语义分叉。
|
||||
@@ -102,7 +103,7 @@ OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得
|
||||
- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`PUT`、`DELETE`。
|
||||
- UniDesk 前端:`用户服务 / Project Manager` React 页面负责展示主 server 仓库引用、私有后端映射、项目指标、项目表格、筛选搜索、编辑表单、Excel 导入和 Excel 导出;完整原始 JSON 只能通过显式 `查看原始JSON` 打开。
|
||||
|
||||
Project Manager 在 UniDesk 语境中按纯后端服务管理:不得将 `4233` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/project-manager/health` 和 `/api/microservices/project-manager/proxy/...` 同源代理访问项目管理后端。
|
||||
Project Manager 的标准发布是 `bun scripts/cli.ts ci publish-user-service --service project-manager --commit <full-sha>`,随后用 `bun scripts/cli.ts deploy apply --env dev --service project-manager` 和 `bun scripts/cli.ts deploy apply --env prod --service project-manager` 消费同一 commit-pinned artifact 并验证 live commit / requestedCommit。Project Manager 在 UniDesk 语境中按纯后端服务管理:不得将 `4233` 映射为公网端口。浏览器只能通过 UniDesk frontend 的 `/api/microservices/project-manager/health` 和 `/api/microservices/project-manager/proxy/...` 同源代理访问项目管理后端。
|
||||
|
||||
### Baidu Netdisk On Main Server
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ This policy does not apply to:
|
||||
- `release/v1` governance;
|
||||
- one-off infrastructure repair actions.
|
||||
|
||||
`todo-note` is a special-case main-server service: it can participate in deploy validation, but live prod artifact CD stays blocked until the checked-in health contract proves `deploy.commit` and `deploy.requestedCommit`.
|
||||
|
||||
## Default Release Flow
|
||||
|
||||
The default release flow for a user-service change is:
|
||||
@@ -74,6 +76,45 @@ Baidu Netdisk is the canonical main-server direct user-service sample.
|
||||
- `server rebuild baidu-netdisk` remains a maintenance/local rebuild path only. It is not the standard versioned release truth for Baidu Netdisk.
|
||||
- Production acceptance must explicitly verify `microservice health baidu-netdisk`, `microservice proxy baidu-netdisk /api/transfers`, private `4244` exposure, and live commit / artifact information.
|
||||
|
||||
## Project Manager
|
||||
|
||||
Project Manager follows the same commit-pinned artifact flow as Baidu Netdisk, but the runtime target is the master-server Compose service `project-manager`.
|
||||
|
||||
- The minimal standard artifact command is `bun scripts/cli.ts ci publish-user-service --service project-manager --commit <full-sha> --wait-ms 1200000`.
|
||||
- The expected artifact is `127.0.0.1:5000/unidesk/project-manager:<commit>` plus its registry digest from the CI output.
|
||||
- Dev CD consumes the same artifact with `bun scripts/cli.ts deploy apply --env dev --service project-manager`; prod CD consumes it with `bun scripts/cli.ts deploy apply --env prod --service project-manager`.
|
||||
- `server rebuild project-manager` remains a maintenance/local rebuild path only. It is not the standard versioned release truth for Project Manager.
|
||||
- Production acceptance must explicitly verify `microservice health project-manager`, `microservice proxy project-manager /api/projects`, private `4233` exposure, and live commit / artifact information.
|
||||
|
||||
## OA Event Flow
|
||||
|
||||
OA Event Flow follows the same master-server Compose artifact flow as Project Manager.
|
||||
|
||||
- The minimal standard artifact command is `bun scripts/cli.ts ci publish-user-service --service oa-event-flow --commit <full-sha> --wait-ms 1200000`.
|
||||
- The expected artifact is `127.0.0.1:5000/unidesk/oa-event-flow:<commit>` plus its registry digest from the CI output.
|
||||
- Dev CD consumes the same artifact with `bun scripts/cli.ts deploy apply --env dev --service oa-event-flow`; prod CD consumes it with `bun scripts/cli.ts deploy apply --env prod --service oa-event-flow`.
|
||||
- `server rebuild oa-event-flow` remains a maintenance/local rebuild path only. It is not the standard versioned release truth for OA Event Flow.
|
||||
- Production acceptance must explicitly verify `microservice health oa-event-flow`, `microservice proxy oa-event-flow /api/diagnostics`, private `4255` exposure, and live commit / artifact information.
|
||||
|
||||
## Code Queue Manager
|
||||
|
||||
`code-queue-mgr` is the master-server internal sidecar control plane, so it follows artifact consumer validation but not unrestricted prod automation.
|
||||
|
||||
- The minimal standard artifact command is `bun scripts/cli.ts ci publish-user-service --service code-queue-mgr --commit <full-sha> --wait-ms 1200000`.
|
||||
- The expected artifact is `127.0.0.1:5000/unidesk/code-queue-mgr:<commit>` plus its registry digest from the CI output.
|
||||
- Dev validation uses `bun scripts/cli.ts deploy apply --env dev --service code-queue-mgr`; prod dry-run uses `bun scripts/cli.ts deploy apply --env prod --service code-queue-mgr --dry-run`.
|
||||
- Live prod apply requires explicit supervisor confirmation and is not the worker default.
|
||||
- `server rebuild code-queue-mgr` remains a maintenance/local rebuild path only. It is not the standard versioned release truth for Code Queue Manager.
|
||||
- Production acceptance must explicitly verify `microservice health code-queue-mgr`, the Compose container image label, and the live health payload.
|
||||
|
||||
## Todo Note
|
||||
|
||||
Todo Note is the current blocker case.
|
||||
|
||||
- The expected source reference remains `https://gitee.com/Lyon1998/todo_note`.
|
||||
- Validation may use `bun scripts/cli.ts deploy apply --env dev --service todo-note --dry-run` or the equivalent prod dry-run, but live artifact CD is blocked until the checked-in runtime contract proves `deploy.commit` and `deploy.requestedCommit`.
|
||||
- `server rebuild todo-note` remains a maintenance/local rebuild path only. It is not the standard versioned release truth for Todo Note.
|
||||
|
||||
## Decision Center
|
||||
|
||||
Decision Center is the canonical example of a user service that doubles as a product workflow for requirements, decisions, and daily work diaries.
|
||||
|
||||
@@ -69,7 +69,7 @@ const defaultOptions: ArtifactRegistryOptions = {
|
||||
sourceRepo: "https://github.com/pikasTech/unidesk",
|
||||
deployRef: null,
|
||||
};
|
||||
const supportedArtifactConsumerServices = ["backend-core", "baidu-netdisk", "decision-center", "frontend"] as const;
|
||||
const supportedArtifactConsumerServices = ["backend-core", "baidu-netdisk", "code-queue-mgr", "decision-center", "frontend", "oa-event-flow", "project-manager", "todo-note"] as const;
|
||||
type SupportedArtifactConsumerService = typeof supportedArtifactConsumerServices[number];
|
||||
const legacyDeployBackendCoreDisabled = true;
|
||||
|
||||
@@ -80,6 +80,10 @@ interface ArtifactConsumerSpec {
|
||||
registryRepository: string;
|
||||
dockerfile: string;
|
||||
targets: Partial<Record<ArtifactDeployEnvironment, ArtifactConsumerTarget>>;
|
||||
prodLiveApply: "enabled" | "supervisor-only" | "unsupported";
|
||||
prodLiveBlockReason?: string;
|
||||
runtimeVerification?: "strict" | "blocked";
|
||||
runtimeVerificationBlockReason?: string;
|
||||
}
|
||||
|
||||
interface ArtifactConsumerTarget {
|
||||
@@ -113,6 +117,7 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/backend-core",
|
||||
dockerfile: "src/components/backend-core/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
prod: {
|
||||
targetImage: "unidesk-backend-core",
|
||||
@@ -134,7 +139,20 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/baidu-netdisk",
|
||||
dockerfile: "src/components/microservices/baidu-netdisk/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "baidu-netdisk",
|
||||
targetCommitImage: (commit: string) => `baidu-netdisk:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.baidu-netdisk",
|
||||
compose: {
|
||||
serviceName: "baidu-netdisk",
|
||||
containerName: "baidu-netdisk-backend",
|
||||
deployEnvPrefix: "UNIDESK_BAIDU_NETDISK_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4244/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "baidu-netdisk",
|
||||
targetCommitImage: (commit: string) => `baidu-netdisk:${commit}`,
|
||||
@@ -149,12 +167,48 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"code-queue-mgr": {
|
||||
serviceId: "code-queue-mgr",
|
||||
environment: "prod",
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/code-queue-mgr",
|
||||
dockerfile: "src/components/microservices/code-queue-mgr/Dockerfile",
|
||||
prodLiveApply: "supervisor-only",
|
||||
prodLiveBlockReason: "code-queue-mgr is the main-server Code Queue control-plane sidecar; live production apply requires explicit supervisor confirmation.",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "code-queue-mgr",
|
||||
targetCommitImage: (commit: string) => `code-queue-mgr:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.code-queue-mgr",
|
||||
compose: {
|
||||
serviceName: "code-queue-mgr",
|
||||
containerName: "code-queue-mgr-backend",
|
||||
deployEnvPrefix: "UNIDESK_CODE_QUEUE_MGR_DEPLOY",
|
||||
healthProbeCommand: "code-queue-mgr --print-health",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "code-queue-mgr",
|
||||
targetCommitImage: (commit: string) => `code-queue-mgr:${commit}`,
|
||||
deployRef: "deploy.json#environments.prod.services.code-queue-mgr",
|
||||
compose: {
|
||||
serviceName: "code-queue-mgr",
|
||||
containerName: "code-queue-mgr-backend",
|
||||
deployEnvPrefix: "UNIDESK_CODE_QUEUE_MGR_DEPLOY",
|
||||
healthProbeCommand: "code-queue-mgr --print-health",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"decision-center": {
|
||||
serviceId: "decision-center",
|
||||
environment: "prod",
|
||||
kind: "d601-k3s",
|
||||
registryRepository: "unidesk/decision-center",
|
||||
dockerfile: "src/components/microservices/decision-center/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "unidesk-decision-center:dev",
|
||||
@@ -193,6 +247,7 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/frontend",
|
||||
dockerfile: "src/components/frontend/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
prod: {
|
||||
targetImage: "unidesk-frontend",
|
||||
@@ -208,12 +263,118 @@ const artifactConsumerSpecs: Record<string, ArtifactConsumerSpec> = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"oa-event-flow": {
|
||||
serviceId: "oa-event-flow",
|
||||
environment: "prod",
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/oa-event-flow",
|
||||
dockerfile: "src/components/microservices/oa-event-flow/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "oa-event-flow",
|
||||
targetCommitImage: (commit: string) => `oa-event-flow:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.oa-event-flow",
|
||||
compose: {
|
||||
serviceName: "oa-event-flow",
|
||||
containerName: "oa-event-flow-backend",
|
||||
deployEnvPrefix: "UNIDESK_OA_EVENT_FLOW_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4255/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "oa-event-flow",
|
||||
targetCommitImage: (commit: string) => `oa-event-flow:${commit}`,
|
||||
deployRef: "deploy.json#environments.prod.services.oa-event-flow",
|
||||
compose: {
|
||||
serviceName: "oa-event-flow",
|
||||
containerName: "oa-event-flow-backend",
|
||||
deployEnvPrefix: "UNIDESK_OA_EVENT_FLOW_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4255/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"project-manager": {
|
||||
serviceId: "project-manager",
|
||||
environment: "prod",
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/project-manager",
|
||||
dockerfile: "src/components/microservices/project-manager/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "project-manager",
|
||||
targetCommitImage: (commit: string) => `project-manager:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.project-manager",
|
||||
compose: {
|
||||
serviceName: "project-manager",
|
||||
containerName: "project-manager-backend",
|
||||
deployEnvPrefix: "UNIDESK_PROJECT_MANAGER_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4233/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "project-manager",
|
||||
targetCommitImage: (commit: string) => `project-manager:${commit}`,
|
||||
deployRef: "deploy.json#environments.prod.services.project-manager",
|
||||
compose: {
|
||||
serviceName: "project-manager",
|
||||
containerName: "project-manager-backend",
|
||||
deployEnvPrefix: "UNIDESK_PROJECT_MANAGER_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4233/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"todo-note": {
|
||||
serviceId: "todo-note",
|
||||
environment: "prod",
|
||||
kind: "compose",
|
||||
registryRepository: "unidesk/todo-note",
|
||||
dockerfile: "Dockerfile",
|
||||
prodLiveApply: "unsupported",
|
||||
prodLiveBlockReason: "todo-note source is external to this repository and the current checked-in contract cannot prove /api/health deploy.commit/deploy.requestedCommit support.",
|
||||
runtimeVerification: "blocked",
|
||||
runtimeVerificationBlockReason: "todo-note source is external to this repository and the current checked-in contract cannot prove /api/health deploy.commit/deploy.requestedCommit support.",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "todo-note",
|
||||
targetCommitImage: (commit: string) => `todo-note:${commit}`,
|
||||
deployRef: "deploy.json#environments.dev.services.todo-note",
|
||||
compose: {
|
||||
serviceName: "todo-note",
|
||||
containerName: "todo-note-backend",
|
||||
deployEnvPrefix: "UNIDESK_TODO_NOTE_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4211/api/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
prod: {
|
||||
targetImage: "todo-note",
|
||||
targetCommitImage: (commit: string) => `todo-note:${commit}`,
|
||||
deployRef: "deploy.json#environments.prod.services.todo-note",
|
||||
compose: {
|
||||
serviceName: "todo-note",
|
||||
containerName: "todo-note-backend",
|
||||
deployEnvPrefix: "UNIDESK_TODO_NOTE_DEPLOY",
|
||||
healthProbeCommand: "bun -e \"fetch('http://127.0.0.1:4211/api/health').then(async r=>{const text=await r.text(); console.log(text); process.exit(r.ok?0:1)}).catch(e=>{console.error(e); process.exit(1)})\"",
|
||||
requireHealthCommit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"dev:frontend": {
|
||||
serviceId: "frontend",
|
||||
environment: "dev",
|
||||
kind: "d601-k3s",
|
||||
registryRepository: "unidesk/frontend",
|
||||
dockerfile: "src/components/frontend/Dockerfile",
|
||||
prodLiveApply: "enabled",
|
||||
targets: {
|
||||
dev: {
|
||||
targetImage: "unidesk-frontend:dev",
|
||||
@@ -649,6 +810,58 @@ function unsupportedEnvironment(spec: ArtifactConsumerSpec, options: ArtifactReg
|
||||
};
|
||||
}
|
||||
|
||||
function artifactConsumerLiveBlock(spec: ArtifactConsumerSpec, options: ArtifactRegistryOptions): Record<string, unknown> | null {
|
||||
const environment = options.environment ?? "prod";
|
||||
if (spec.runtimeVerification === "blocked") {
|
||||
return {
|
||||
ok: false,
|
||||
supported: false,
|
||||
liveApplyAllowed: false,
|
||||
error: "runtime-verification-blocked",
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: spec.runtimeVerificationBlockReason ?? `${spec.serviceId} does not yet satisfy strict runtime deploy commit verification.`,
|
||||
requiredBeforeLiveApply: [
|
||||
"runtime Compose env injects deploy commit/requestedCommit metadata",
|
||||
"service health reports deploy.commit and deploy.requestedCommit for strict verification",
|
||||
],
|
||||
policy: "artifact CD must not accept a healthy old service or silently fall back to legacy rebuild paths",
|
||||
};
|
||||
}
|
||||
if (environment !== "prod" || spec.prodLiveApply === "enabled") return null;
|
||||
if (spec.prodLiveApply === "supervisor-only") {
|
||||
return {
|
||||
ok: false,
|
||||
supported: true,
|
||||
liveApplyAllowed: false,
|
||||
error: "supervisor-confirmation-required",
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: spec.prodLiveBlockReason ?? `${spec.serviceId} production artifact apply requires supervisor confirmation.`,
|
||||
dryRunCommandShape: `bun scripts/cli.ts artifact-registry deploy-service --env prod --service ${spec.serviceId} --commit <full-sha> --dry-run`,
|
||||
policy: "worker automation must not perform live production apply for this infrastructure control-plane service",
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
supported: false,
|
||||
liveApplyAllowed: false,
|
||||
error: "artifact-consumer-blocked",
|
||||
serviceId: spec.serviceId,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
reason: spec.prodLiveBlockReason ?? `${spec.serviceId} does not yet satisfy the artifact consumer runtime verification contract.`,
|
||||
requiredBeforeLiveApply: [
|
||||
"CI can publish a commit-pinned image with matching service id, source commit, and Dockerfile labels",
|
||||
"runtime Compose env injects deploy commit/requestedCommit metadata",
|
||||
"service health reports deploy.commit and deploy.requestedCommit for strict verification",
|
||||
],
|
||||
policy: "do not silently fall back to server rebuild, dirty worktrees, mutable tags, or source builds on the runtime target",
|
||||
};
|
||||
}
|
||||
|
||||
function artifactImageRef(options: ArtifactRegistryOptions, spec: ArtifactConsumerSpec, commit: string): string {
|
||||
return `127.0.0.1:${options.port}/${spec.registryRepository}:${commit}`;
|
||||
}
|
||||
@@ -1040,7 +1253,9 @@ async function deployComposeArtifactNow(options: ArtifactRegistryOptions, spec:
|
||||
"cat \"$health_json\"",
|
||||
...(target.compose.requireHealthCommit ? [
|
||||
"health_commit=$(python3 -c 'import json,sys; print(((json.load(open(sys.argv[1])).get(\"deploy\") or {}).get(\"commit\") or \"\"))' \"$health_json\")",
|
||||
"health_requested_commit=$(python3 -c 'import json,sys; print(((json.load(open(sys.argv[1])).get(\"deploy\") or {}).get(\"requestedCommit\") or \"\"))' \"$health_json\")",
|
||||
`test "$health_commit" = ${shellQuote(commit)}`,
|
||||
`test "$health_requested_commit" = ${shellQuote(commit)}`,
|
||||
] : []),
|
||||
].join("\n");
|
||||
const deploy = runCommand(["bash", "-lc", composeLockScript(upScript)], repoRoot, { timeoutMs: Math.max(options.timeoutMs, 300_000) });
|
||||
@@ -1072,8 +1287,10 @@ async function deployComposeArtifactNow(options: ArtifactRegistryOptions, spec:
|
||||
running: running.stdout.trim(),
|
||||
validation: {
|
||||
liveCommit: commit,
|
||||
liveRequestedCommit: target.compose.requireHealthCommit ? commit : "not-required",
|
||||
imageLabelCommit: commit,
|
||||
serviceHealthCommit: target.compose.requireHealthCommit ? commit : "not-required",
|
||||
serviceHealthRequestedCommit: target.compose.requireHealthCommit ? commit : "not-required",
|
||||
healthyOldVersionAccepted: false,
|
||||
},
|
||||
rollback: {
|
||||
@@ -1125,12 +1342,14 @@ 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 sourceImage = artifactImageRef(options, spec, commit);
|
||||
const common = {
|
||||
ok: true,
|
||||
supported: true,
|
||||
ok: !verificationBlocked,
|
||||
supported: !verificationBlocked,
|
||||
dryRun: true,
|
||||
mutation: false,
|
||||
error: verificationBlocked ? "runtime-verification-blocked" : undefined,
|
||||
environment,
|
||||
providerId: options.providerId,
|
||||
serviceId: spec.serviceId,
|
||||
@@ -1148,6 +1367,11 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
url: `http://127.0.0.1:${options.port}/v2/${spec.registryRepository}/manifests/${commit}`,
|
||||
},
|
||||
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,
|
||||
allowed: !verificationBlocked && (environment !== "prod" || spec.prodLiveApply === "enabled"),
|
||||
reason: spec.runtimeVerificationBlockReason ?? spec.prodLiveBlockReason ?? null,
|
||||
},
|
||||
};
|
||||
if (spec.kind === "compose") {
|
||||
if (target.compose === undefined) throw new Error(`${spec.serviceId} missing compose artifact consumer config`);
|
||||
@@ -1166,7 +1390,9 @@ function dryRunArtifactConsumerPlan(options: ArtifactRegistryOptions, spec: Arti
|
||||
"D601 registry /v2 manifest exists for the commit tag",
|
||||
"loaded image labels match service id, source commit, and Dockerfile",
|
||||
"running Compose container image label matches the requested commit",
|
||||
target.compose.requireHealthCommit
|
||||
verificationBlocked
|
||||
? `blocked: ${spec.runtimeVerificationBlockReason}`
|
||||
: target.compose.requireHealthCommit
|
||||
? `${spec.serviceId} /health succeeds and reports deploy.commit matching the artifact commit`
|
||||
: `${spec.serviceId} /health succeeds for the recreated container`,
|
||||
],
|
||||
@@ -1430,6 +1656,8 @@ async function deployServiceNow(options: ArtifactRegistryOptions): Promise<Recor
|
||||
const target = artifactConsumerTarget(spec, options.environment);
|
||||
if (target === null) return unsupportedEnvironment(spec, options);
|
||||
if (options.dryRun) return dryRunArtifactConsumerPlan(options, spec, target, options.commit);
|
||||
const liveBlock = artifactConsumerLiveBlock(spec, options);
|
||||
if (liveBlock !== null) return liveBlock;
|
||||
if (spec.kind === "compose") return deployComposeArtifactNow(options, spec, target);
|
||||
return deployD601K3sArtifactNow(options, spec, target);
|
||||
}
|
||||
@@ -1442,6 +1670,8 @@ function deployServiceJob(args: string[], options: ArtifactRegistryOptions): Rec
|
||||
const target = artifactConsumerTarget(spec, options.environment);
|
||||
if (target === null) return unsupportedEnvironment(spec, options);
|
||||
if (options.dryRun) return dryRunArtifactConsumerPlan(options, spec, target, options.commit);
|
||||
const liveBlock = artifactConsumerLiveBlock(spec, options);
|
||||
if (liveBlock !== null) return liveBlock;
|
||||
const runArgs = args.includes("--run-now") ? args : [...args, "--run-now"];
|
||||
const command = [process.execPath, rootPath("scripts", "cli.ts"), "artifact-registry", ...runArgs];
|
||||
const job = startJob("artifact_registry_service_cd", command, `Pull and deploy ${options.environment ?? "prod"} ${options.serviceId} artifact ${options.commit} from D601 registry`);
|
||||
@@ -1478,6 +1708,10 @@ function localHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --service frontend --env dev --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env dev --service decision-center --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service decision-center --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service project-manager --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service oa-event-flow --commit <full-sha> [--dry-run] [--run-now] [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service code-queue-mgr --commit <full-sha> --dry-run [--provider-id D601]",
|
||||
"bun scripts/cli.ts artifact-registry deploy-service --env prod --service todo-note --commit <full-sha> --dry-run [--provider-id D601]",
|
||||
],
|
||||
firstStage: "install now writes the rendered systemd/Compose/config files and starts the registry",
|
||||
artifactConsumers: {
|
||||
@@ -1489,10 +1723,17 @@ function localHelp(): Record<string, unknown> {
|
||||
"bun scripts/cli.ts deploy apply --env prod --service baidu-netdisk",
|
||||
"bun scripts/cli.ts deploy apply --env prod --service frontend",
|
||||
"bun scripts/cli.ts deploy apply --env prod --service decision-center",
|
||||
"bun scripts/cli.ts deploy apply --env prod --service project-manager",
|
||||
"bun scripts/cli.ts deploy apply --env prod --service oa-event-flow",
|
||||
"bun scripts/cli.ts deploy apply --env prod --service code-queue-mgr --dry-run",
|
||||
],
|
||||
devCommands: [
|
||||
"bun scripts/cli.ts deploy apply --env dev --service frontend",
|
||||
"bun scripts/cli.ts deploy apply --env dev --service decision-center",
|
||||
"bun scripts/cli.ts deploy apply --env dev --service project-manager --dry-run",
|
||||
"bun scripts/cli.ts deploy apply --env dev --service oa-event-flow --dry-run",
|
||||
"bun scripts/cli.ts deploy apply --env dev --service code-queue-mgr --dry-run",
|
||||
"bun scripts/cli.ts deploy apply --env dev --service todo-note --dry-run",
|
||||
],
|
||||
rollbackShape: "rerun the same artifact consumer with a previous commit-pinned image",
|
||||
},
|
||||
|
||||
+14
-3
@@ -29,6 +29,14 @@ const ciRuntimeImages = [
|
||||
"alpine/git:2.45.2",
|
||||
ciCodeQueueImage,
|
||||
];
|
||||
const publishUserServiceArtifactAllowedServiceIds = new Set([
|
||||
"baidu-netdisk",
|
||||
"code-queue-mgr",
|
||||
"decision-center",
|
||||
"frontend",
|
||||
"oa-event-flow",
|
||||
"project-manager",
|
||||
]);
|
||||
|
||||
interface CiOptions {
|
||||
repoUrl: string;
|
||||
@@ -213,7 +221,10 @@ function requireSupportedUserService(config: UniDeskConfig, serviceId: string):
|
||||
const isMainServerDirectService = service.providerId === "main-server"
|
||||
&& service.development.providerId === "main-server"
|
||||
&& service.deployment.mode === "unidesk-direct";
|
||||
if (!isD601K3sService && !isMainServerDirectService) {
|
||||
const isMainServerInternalSidecar = service.providerId === "main-server"
|
||||
&& service.development.providerId === "main-server"
|
||||
&& service.deployment.mode === "internal-sidecar";
|
||||
if (!isD601K3sService && !isMainServerDirectService && !isMainServerInternalSidecar) {
|
||||
throw new Error(`ci publish-user-service supports only reviewed k3sctl-managed D601 services or main-server unidesk-direct services; ${serviceId} is ${service.providerId}/${service.deployment.mode}`);
|
||||
}
|
||||
return service;
|
||||
@@ -1539,8 +1550,8 @@ export async function runCiCommand(config: UniDeskConfig, args: string[]): Promi
|
||||
const waitMs = numberOption(args, "--wait-ms", 0);
|
||||
const dryRun = boolFlag(args, "--dry-run");
|
||||
const dockerfile = requireRepoRelativePath(target.dockerfile, serviceId === "frontend" ? "frontend.dockerfile" : `microservices.${serviceId}.repository.dockerfile`);
|
||||
if (!["baidu-netdisk", "decision-center", "frontend"].includes(serviceId)) {
|
||||
throw new Error("ci publish-user-service currently allows only baidu-netdisk, decision-center, and frontend until each user-service Dockerfile contract is reviewed");
|
||||
if (!publishUserServiceArtifactAllowedServiceIds.has(serviceId)) {
|
||||
throw new Error(`ci publish-user-service currently allows only ${Array.from(publishUserServiceArtifactAllowedServiceIds).join(", ")} until each user-service Dockerfile contract is reviewed`);
|
||||
}
|
||||
return publishUserServiceArtifact(config, {
|
||||
repoUrl: target.repoUrl,
|
||||
|
||||
+56
-13
@@ -136,9 +136,13 @@ const nativeK3sCtrAddress = "/run/k3s/containerd/containerd.sock";
|
||||
const unideskRepoUrl = "https://github.com/pikasTech/unidesk";
|
||||
const d601MaintenanceDeployAllowedServiceIds = new Set<string>(["backend-core", "k3sctl-adapter", "code-queue"]);
|
||||
const devApplySupportedServiceIds = new Set<string>(["backend-core"]);
|
||||
const devArtifactConsumerServiceIds = new Set<string>(["baidu-netdisk", "decision-center", "frontend"]);
|
||||
const prodArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "decision-center", "frontend"]);
|
||||
const prodForbiddenTargetSideBuildServiceIds = prodArtifactConsumerServiceIds;
|
||||
const devArtifactConsumerServiceIds = new Set<string>(["baidu-netdisk", "code-queue-mgr", "decision-center", "frontend", "oa-event-flow", "project-manager", "todo-note"]);
|
||||
const devArtifactConsumerProdDesiredFallbackServiceIds = new Set<string>(["code-queue-mgr", "oa-event-flow", "project-manager", "todo-note"]);
|
||||
const prodArtifactConsumerServiceIds = new Set<string>(["backend-core", "baidu-netdisk", "code-queue-mgr", "decision-center", "frontend", "oa-event-flow", "project-manager", "todo-note"]);
|
||||
const prodArtifactLiveApplyBlockedServiceIds = new Map<string, string>([
|
||||
["code-queue-mgr", "code-queue-mgr is the main-server Code Queue control-plane sidecar; live production apply requires explicit supervisor confirmation."],
|
||||
["todo-note", "todo-note source is external to this repository and the current checked-in contract cannot prove /api/health deploy.commit/deploy.requestedCommit support."],
|
||||
]);
|
||||
const deployEnvironmentTargets: Record<DeployEnvironment, DeployEnvironmentTarget> = {
|
||||
dev: {
|
||||
environment: "dev",
|
||||
@@ -201,7 +205,7 @@ export function deployHelp(action: string | undefined = undefined): Record<strin
|
||||
},
|
||||
options: [
|
||||
{ name: "--file <path>", default: defaultDeployFile, description: "Desired-state manifest path relative to the repo root. JSON and ESM JS manifests are supported, for example deploy.json or develop.js. Local manifest apply allows k3sctl-adapter and explicit production code-queue controlled rollout on D601." },
|
||||
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply supports backend-core target-side rollout plus reviewed artifact consumers for frontend, baidu-netdisk, and decision-center; prod apply uses the D601 registry artifact consumer for backend-core, frontend, baidu-netdisk, and decision-center." },
|
||||
{ name: "--env <dev|prod>", description: "Read the named environment from origin/master:deploy.json. Dev apply supports backend-core target-side rollout plus reviewed artifact consumers for frontend, baidu-netdisk, decision-center, project-manager, oa-event-flow, code-queue-mgr validation, while todo-note remains dry-run only. Prod apply uses the D601 registry artifact consumer for reviewed services; code-queue-mgr live apply is supervisor-gated and todo-note remains blocked." },
|
||||
{ name: "--service <id>", description: "Limit reconcile to one service from the manifest." },
|
||||
{ name: "--commit <full-sha>", description: "Prod artifact rollback/apply override for a selected service; the image must already exist in D601 registry." },
|
||||
{ name: "--dry-run", description: "Prepare and validate without mutating the target service." },
|
||||
@@ -569,6 +573,11 @@ function readEnvironmentDeployManifest(environment: DeployEnvironment): { manife
|
||||
};
|
||||
}
|
||||
|
||||
function readProdDeployManifestSnapshot(): DeployManifest {
|
||||
const raw = runGitOrThrow(["show", `${deployEnvironmentTargets.prod.gitRef}:deploy.json`], repoRoot, `failed to read ${deployEnvironmentTargets.prod.gitRef}:deploy.json`).stdout;
|
||||
return parseDeployManifest(JSON.parse(raw) as unknown, `${deployEnvironmentTargets.prod.gitRef}:deploy.json#environments.prod`, "prod");
|
||||
}
|
||||
|
||||
async function readDeployManifest(file: string): Promise<DeployManifest> {
|
||||
const path = resolve(repoRoot, file);
|
||||
if (!existsSync(path)) throw new Error(`deploy manifest not found: ${path}`);
|
||||
@@ -2607,6 +2616,15 @@ function environmentDryRunPlan(
|
||||
reason: "No standardized dev D601 registry artifact consumer is implemented for this service; legacy maintenance-channel deployment is not allowed.",
|
||||
}
|
||||
: undefined,
|
||||
liveApply: environment === "prod" && prodArtifactLiveApplyBlockedServiceIds.has(service.id)
|
||||
? {
|
||||
allowed: false,
|
||||
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id),
|
||||
dryRunOnly: true,
|
||||
}
|
||||
: environment === "prod"
|
||||
? { allowed: prodArtifactConsumerServiceIds.has(service.id) }
|
||||
: undefined,
|
||||
})),
|
||||
unsupported: environment === "prod" ? prodArtifactUnsupported : devUnsupported,
|
||||
};
|
||||
@@ -2615,6 +2633,7 @@ function environmentDryRunPlan(
|
||||
function unsupportedDevApplyServices(manifest: DeployManifest, serviceId: string | null): string[] {
|
||||
if (manifest.environment !== "dev") return [];
|
||||
const services = serviceId === null ? manifest.services : manifest.services.filter((service) => service.id === serviceId);
|
||||
if (serviceId !== null && services.length === 0 && devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId)) return [];
|
||||
return services.map((service) => service.id).filter((id) => !devApplySupportedServiceIds.has(id) && !devArtifactConsumerServiceIds.has(id));
|
||||
}
|
||||
|
||||
@@ -2636,6 +2655,7 @@ function selectedDevArtifactServices(manifest: DeployManifest, serviceId: string
|
||||
|
||||
function selectedDevTargetServices(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
|
||||
if (manifest.environment !== "dev") return [];
|
||||
if (serviceId !== null && devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId) && !manifest.services.some((service) => service.id === serviceId)) return [];
|
||||
return selectedEnvironmentServices(manifest, serviceId)
|
||||
.filter((service) => devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id));
|
||||
}
|
||||
@@ -2646,6 +2666,14 @@ function selectedEnvironmentServices(manifest: DeployManifest, serviceId: string
|
||||
return services;
|
||||
}
|
||||
|
||||
function selectedDevArtifactServicesWithProdFallback(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
|
||||
if (manifest.environment !== "dev" || serviceId === null) return selectedDevArtifactServices(manifest, serviceId);
|
||||
const selected = manifest.services.filter((service) => service.id === serviceId && devArtifactConsumerServiceIds.has(service.id));
|
||||
if (selected.length > 0 || !devArtifactConsumerProdDesiredFallbackServiceIds.has(serviceId)) return selected;
|
||||
const prodManifest = readProdDeployManifestSnapshot();
|
||||
return prodManifest.services.filter((service) => service.id === serviceId);
|
||||
}
|
||||
|
||||
function devUnsupportedServices(manifest: DeployManifest, serviceId: string | null): DeployManifestService[] {
|
||||
if (manifest.environment !== "dev") return [];
|
||||
return selectedEnvironmentServices(manifest, serviceId).filter((service) => !devApplySupportedServiceIds.has(service.id) && !devArtifactConsumerServiceIds.has(service.id));
|
||||
@@ -2672,20 +2700,21 @@ function prodArtifactUnsupportedResult(services: DeployManifestService[]): Recor
|
||||
};
|
||||
}
|
||||
|
||||
function prodArtifactConsumerLocalManifestResult(services: DeployManifestService[]): Record<string, unknown> {
|
||||
function prodArtifactLiveApplyBlockedResult(services: DeployManifestService[]): Record<string, unknown> {
|
||||
return {
|
||||
ok: false,
|
||||
supported: false,
|
||||
error: "prod-artifact-consumer-local-manifest-blocked",
|
||||
supported: true,
|
||||
error: "live-prod-apply-blocked",
|
||||
services: services.map((service) => ({
|
||||
id: service.id,
|
||||
repo: service.repo,
|
||||
commitId: service.commitId,
|
||||
supported: true,
|
||||
reason: "This service has a reviewed artifact consumer; production source-build deploy from a local manifest is blocked.",
|
||||
liveApplyAllowed: false,
|
||||
reason: prodArtifactLiveApplyBlockedServiceIds.get(service.id) ?? "live production artifact apply is blocked by policy",
|
||||
})),
|
||||
policy: "prod artifact consumers must enter through deploy apply --env prod so CD consumes an existing commit-pinned registry image and never falls back to source build or a dirty worktree",
|
||||
commandShape: "bun scripts/cli.ts deploy apply --env prod --service <service-id> --commit <full-sha>",
|
||||
policy: "prod dry-run/plan is available, but worker automation must not run live production apply for these services",
|
||||
dryRunCommandShape: "bun scripts/cli.ts deploy apply --env prod --service <id> --dry-run",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2705,7 +2734,7 @@ async function runArtifactConsumerApplyNow(
|
||||
"--commit", commit,
|
||||
"--source-repo", service.repo,
|
||||
"--deploy-ref", `${deployEnvironmentTargets[environment].gitRef}:deploy.json#environments.${environment}.services.${service.id}`,
|
||||
...(environment === "prod" || service.id === "frontend" || service.id === "decision-center" ? ["--env", environment] : []),
|
||||
"--env", environment,
|
||||
"--timeout-ms", String(options.timeoutMs),
|
||||
"--run-now",
|
||||
...(options.dryRun ? ["--dry-run"] : []),
|
||||
@@ -2731,6 +2760,10 @@ async function runProdArtifactApplyNow(manifest: DeployManifest, options: Deploy
|
||||
const selected = selectedEnvironmentServices(manifest, options.serviceId);
|
||||
const unsupported = selected.filter((service) => !prodArtifactConsumerServiceIds.has(service.id));
|
||||
if (unsupported.length > 0) return prodArtifactUnsupportedResult(unsupported);
|
||||
if (!options.dryRun) {
|
||||
const blocked = selected.filter((service) => prodArtifactLiveApplyBlockedServiceIds.has(service.id));
|
||||
if (blocked.length > 0) return prodArtifactLiveApplyBlockedResult(blocked);
|
||||
}
|
||||
return runArtifactConsumerApplyNow(manifest, options, "prod", selected);
|
||||
}
|
||||
|
||||
@@ -2739,6 +2772,16 @@ function prodArtifactApplyJob(args: string[], options: DeployOptions): Record<st
|
||||
throw new Error("deploy apply --env prod --dry-run should run in the foreground so the plan is visible immediately");
|
||||
}
|
||||
const runArgs = args.includes("--run-now") ? args : [...args, "--run-now"];
|
||||
if (options.serviceId === null) {
|
||||
throw new Error("deploy apply --env prod without --service is not allowed for mixed artifact consumers; use --service to avoid applying supervisor-gated services accidentally");
|
||||
}
|
||||
if (prodArtifactLiveApplyBlockedServiceIds.has(options.serviceId)) {
|
||||
return prodArtifactLiveApplyBlockedResult([{
|
||||
id: options.serviceId,
|
||||
repo: "",
|
||||
commitId: options.commitOverride ?? "",
|
||||
}]);
|
||||
}
|
||||
const command = [process.execPath, rootPath("scripts", "cli.ts"), "deploy", ...runArgs];
|
||||
const source = `${deployEnvironmentTargets.prod.gitRef}:deploy.json#environments.prod`;
|
||||
const job = startJob("deploy_prod_artifact_apply", command, `Deploy prod artifact consumer from ${source}${options.serviceId === null ? "" : ` service=${options.serviceId}`}`);
|
||||
@@ -2826,9 +2869,9 @@ export async function runDeployCommand(config: UniDeskConfig | null, args: strin
|
||||
}
|
||||
const unsupported = unsupportedDevApplyServices(manifest, options.serviceId);
|
||||
if (unsupported.length > 0) {
|
||||
throw new Error(`deploy apply --env dev currently supports backend-core target-side rollout plus frontend/baidu-netdisk/decision-center artifact consumers; unsupported selected services: ${unsupported.join(", ")}. Use ci run-dev-e2e for smoke verification.`);
|
||||
throw new Error(`deploy apply --env dev currently supports backend-core target-side rollout plus frontend/baidu-netdisk/decision-center/project-manager/oa-event-flow/code-queue-mgr artifact consumers; unsupported selected services: ${unsupported.join(", ")}. todo-note is dry-run only until runtime verification is proven. Use ci run-dev-e2e for smoke verification.`);
|
||||
}
|
||||
const devArtifactServices = selectedDevArtifactServices(manifest, options.serviceId);
|
||||
const devArtifactServices = selectedDevArtifactServicesWithProdFallback(manifest, options.serviceId);
|
||||
const devTargetServices = selectedDevTargetServices(manifest, options.serviceId);
|
||||
if (devArtifactServices.length > 0 && devTargetServices.length > 0) {
|
||||
throw new Error("deploy apply --env dev cannot mix artifact consumer services with target-side rollout services in one invocation; pass --service");
|
||||
|
||||
@@ -116,6 +116,26 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
UNIDESK_FRONTEND_DEPLOY_REPO: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_REPO"),
|
||||
UNIDESK_FRONTEND_DEPLOY_COMMIT: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_COMMIT"),
|
||||
UNIDESK_FRONTEND_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_FRONTEND_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_PROJECT_MANAGER_DEPLOY_REF: runtimeSecret("UNIDESK_PROJECT_MANAGER_DEPLOY_REF"),
|
||||
UNIDESK_PROJECT_MANAGER_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_PROJECT_MANAGER_DEPLOY_SERVICE_ID") || "project-manager",
|
||||
UNIDESK_PROJECT_MANAGER_DEPLOY_REPO: runtimeSecret("UNIDESK_PROJECT_MANAGER_DEPLOY_REPO"),
|
||||
UNIDESK_PROJECT_MANAGER_DEPLOY_COMMIT: runtimeSecret("UNIDESK_PROJECT_MANAGER_DEPLOY_COMMIT"),
|
||||
UNIDESK_PROJECT_MANAGER_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_PROJECT_MANAGER_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_OA_EVENT_FLOW_DEPLOY_REF: runtimeSecret("UNIDESK_OA_EVENT_FLOW_DEPLOY_REF"),
|
||||
UNIDESK_OA_EVENT_FLOW_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_OA_EVENT_FLOW_DEPLOY_SERVICE_ID") || "oa-event-flow",
|
||||
UNIDESK_OA_EVENT_FLOW_DEPLOY_REPO: runtimeSecret("UNIDESK_OA_EVENT_FLOW_DEPLOY_REPO"),
|
||||
UNIDESK_OA_EVENT_FLOW_DEPLOY_COMMIT: runtimeSecret("UNIDESK_OA_EVENT_FLOW_DEPLOY_COMMIT"),
|
||||
UNIDESK_OA_EVENT_FLOW_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_OA_EVENT_FLOW_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REF: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_REF"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_SERVICE_ID") || "code-queue-mgr",
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_REPO"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_COMMIT"),
|
||||
UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_CODE_QUEUE_MGR_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_TODO_NOTE_DEPLOY_REF: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_REF"),
|
||||
UNIDESK_TODO_NOTE_DEPLOY_SERVICE_ID: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_SERVICE_ID") || "todo-note",
|
||||
UNIDESK_TODO_NOTE_DEPLOY_REPO: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_REPO"),
|
||||
UNIDESK_TODO_NOTE_DEPLOY_COMMIT: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_COMMIT"),
|
||||
UNIDESK_TODO_NOTE_DEPLOY_REQUESTED_COMMIT: runtimeSecret("UNIDESK_TODO_NOTE_DEPLOY_REQUESTED_COMMIT"),
|
||||
UNIDESK_AUTH_USERNAME: config.auth.username,
|
||||
UNIDESK_AUTH_PASSWORD: config.auth.password,
|
||||
UNIDESK_SESSION_SECRET: config.auth.sessionSecret,
|
||||
|
||||
+2
-2
@@ -270,8 +270,8 @@ function artifactRegistryHelp(): unknown {
|
||||
"registry endpoint is D601 loopback 127.0.0.1:5000 only",
|
||||
"service is host-managed by systemd + Docker Compose, not k3s-managed",
|
||||
"install writes the rendered host unit/config and starts the registry",
|
||||
"deploy-backend-core is a deprecated compatibility name; use deploy apply --env prod --service backend-core so standard artifact-consumer guardrails run first",
|
||||
"deploy-service currently supports backend-core, baidu-netdisk, prod/dev frontend, and decision-center as standardized consumers",
|
||||
"deploy-backend-core only pulls commit-pinned backend-core artifacts and does not build backend-core on the master server",
|
||||
"deploy-service currently supports backend-core, baidu-netdisk, prod/dev frontend, decision-center, project-manager, oa-event-flow, and code-queue-mgr as standardized consumers; todo-note is runtime-verification blocked",
|
||||
"status and health use provider-gateway Host SSH readonly checks",
|
||||
],
|
||||
legacyEntrypoints: {
|
||||
|
||||
@@ -2681,6 +2681,9 @@ fn main() {
|
||||
if env::args().any(|arg| arg == "--healthcheck") {
|
||||
std::process::exit(if run_healthcheck(config.port) { 0 } else { 1 });
|
||||
}
|
||||
if env::args().any(|arg| arg == "--print-health") {
|
||||
std::process::exit(if print_health(config.port) { 0 } else { 1 });
|
||||
}
|
||||
let state = AppState {
|
||||
config,
|
||||
started_at: now_iso(),
|
||||
@@ -2726,6 +2729,30 @@ fn run_healthcheck(port: u16) -> bool {
|
||||
stream.read_to_string(&mut body).is_ok() && body.contains("\"ok\": true")
|
||||
}
|
||||
|
||||
fn print_health(port: u16) -> bool {
|
||||
let Ok(mut stream) = TcpStream::connect(("127.0.0.1", port)) else {
|
||||
return false;
|
||||
};
|
||||
let _ = stream.set_read_timeout(Some(std::time::Duration::from_secs(3)));
|
||||
let _ = stream.set_write_timeout(Some(std::time::Duration::from_secs(3)));
|
||||
if stream
|
||||
.write_all(b"GET /health HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
|
||||
.is_err()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let mut response = String::new();
|
||||
if stream.read_to_string(&mut response).is_err() {
|
||||
return false;
|
||||
}
|
||||
if let Some((_, body)) = response.split_once("\r\n\r\n") {
|
||||
println!("{}", body.trim());
|
||||
} else {
|
||||
println!("{}", response.trim());
|
||||
}
|
||||
response.contains("\"ok\": true")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -16,6 +16,11 @@ interface RuntimeConfig {
|
||||
pipelineBridgeBaseUrl: string;
|
||||
pipelineBridgeIntervalMs: number;
|
||||
pipelineBridgeRunLimit: number;
|
||||
deployServiceId: string;
|
||||
deployRef: string;
|
||||
deployRepo: string;
|
||||
deployCommit: string;
|
||||
deployRequestedCommit: string;
|
||||
}
|
||||
|
||||
interface OaEventInput {
|
||||
@@ -184,6 +189,11 @@ function configFromEnv(): RuntimeConfig {
|
||||
pipelineBridgeBaseUrl: envString("PIPELINE_OA_BRIDGE_BASE_URL", "").replace(/\/+$/u, ""),
|
||||
pipelineBridgeIntervalMs: envNumber("PIPELINE_OA_BRIDGE_INTERVAL_MS", 15_000),
|
||||
pipelineBridgeRunLimit: envNumber("PIPELINE_OA_BRIDGE_RUN_LIMIT", 50),
|
||||
deployServiceId: envString("UNIDESK_DEPLOY_SERVICE_ID", "oa-event-flow"),
|
||||
deployRef: envString("UNIDESK_DEPLOY_REF", ""),
|
||||
deployRepo: envString("UNIDESK_DEPLOY_REPO", ""),
|
||||
deployCommit: envString("UNIDESK_DEPLOY_COMMIT", ""),
|
||||
deployRequestedCommit: envString("UNIDESK_DEPLOY_REQUESTED_COMMIT", ""),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1328,6 +1338,13 @@ async function healthRoute(): Promise<Response> {
|
||||
insertedCount: pipelineBridgeState.insertedCount,
|
||||
duplicateCount: pipelineBridgeState.duplicateCount,
|
||||
},
|
||||
deploy: {
|
||||
serviceId: config.deployServiceId,
|
||||
ref: config.deployRef,
|
||||
repo: config.deployRepo,
|
||||
commit: config.deployCommit,
|
||||
requestedCommit: config.deployRequestedCommit,
|
||||
},
|
||||
}, databaseReady ? 200 : 503);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,11 @@ interface RuntimeConfig {
|
||||
databaseUrl: string;
|
||||
logFile: string;
|
||||
databasePoolMax: number;
|
||||
deployServiceId: string;
|
||||
deployRef: string;
|
||||
deployRepo: string;
|
||||
deployCommit: string;
|
||||
deployRequestedCommit: string;
|
||||
}
|
||||
|
||||
interface ProjectRow {
|
||||
@@ -92,6 +97,11 @@ function configFromEnv(): RuntimeConfig {
|
||||
databaseUrl,
|
||||
logFile: process.env.LOG_FILE || "",
|
||||
databasePoolMax: Math.max(1, Math.min(8, Number(process.env.DATABASE_POOL_MAX || 1) || 1)),
|
||||
deployServiceId: process.env.UNIDESK_DEPLOY_SERVICE_ID || "project-manager",
|
||||
deployRef: process.env.UNIDESK_DEPLOY_REF || "",
|
||||
deployRepo: process.env.UNIDESK_DEPLOY_REPO || "",
|
||||
deployCommit: process.env.UNIDESK_DEPLOY_COMMIT || "",
|
||||
deployRequestedCommit: process.env.UNIDESK_DEPLOY_REQUESTED_COMMIT || "",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -569,6 +579,13 @@ async function health(): Promise<Response> {
|
||||
service: "project-manager",
|
||||
storage: { primary: "postgres", table: "project_manager_projects", projects: Number(rows[0]?.count ?? 0) },
|
||||
capabilities: ["crud", "excel-import", "excel-export"],
|
||||
deploy: {
|
||||
serviceId: config.deployServiceId,
|
||||
ref: config.deployRef,
|
||||
repo: config.deployRepo,
|
||||
commit: config.deployCommit,
|
||||
requestedCommit: config.deployRequestedCommit,
|
||||
},
|
||||
startedAt: serviceStartedAt,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user