From f1094f1315b8af2ec542b305abaebc3537c60d18 Mon Sep 17 00:00:00 2001 From: pikastech Date: Mon, 20 Jul 2026 16:45:30 +0200 Subject: [PATCH] fix: deduplicate Sub2API host containers Co-Authored-By: Claude Opus 4.7 --- .../skills/unidesk-sub2api/references/operations.md | 2 ++ scripts/src/platform-infra/host-docker.ts | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.agents/skills/unidesk-sub2api/references/operations.md b/.agents/skills/unidesk-sub2api/references/operations.md index a71d2fe4..d38ec94d 100644 --- a/.agents/skills/unidesk-sub2api/references/operations.md +++ b/.agents/skills/unidesk-sub2api/references/operations.md @@ -81,6 +81,8 @@ PK01 没有 k3s control plane。`codex-pool sync --target PK01 --confirm` 和 `c PK01 host-Docker apply 仍必须由 `platform-infra sub2api apply --target PK01 --confirm` 受控执行。若 dry-run 或 apply 输出显示 `docker compose is absent; apply will use raw docker run fallback`,这表示 CLI 选择了 host-Docker fallback,不是裸手工 Docker 操作;只要 YAML image、env、ports、Caddy managed block 和 `status/validate` 最终对齐,可作为受控滚动升级证据。不要改用手工 `docker run`、手工 compose 文件或直接编辑 PK01 Caddyfile。 +`status` 同时兼容 Compose label 和受控 raw-Docker 名称发现,并按稳定容器身份合并结果;默认 `WORKLOADS` 和完整 summary 都必须保持一个实际容器一行。 + - 正式镜像升级优先使用 `platform-infra sub2api rollout`: - 同一入口组合镜像 presence、plan、apply dry-run、预拉、apply、status、validate 和既有消费配置 smoke; - `--confirm` 默认返回 async job,显式 `--wait` 只用于 job 内部或同步调试; diff --git a/scripts/src/platform-infra/host-docker.ts b/scripts/src/platform-infra/host-docker.ts index 0ed0efbf..08b4dfe8 100644 --- a/scripts/src/platform-infra/host-docker.ts +++ b/scripts/src/platform-infra/host-docker.ts @@ -579,12 +579,20 @@ def text(path, limit=4000): except FileNotFoundError: return "" -containers = [] +containers_by_key = {} for line in text(paths[2], 20000).splitlines(): try: - containers.append(json.loads(line)) + container = json.loads(line) + key = container.get("ID") or "\\0".join([ + str(container.get("Names") or container.get("names") or ""), + str(container.get("Image") or container.get("image") or ""), + str(container.get("CreatedAt") or container.get("createdAt") or ""), + ]) + if key not in containers_by_key: + containers_by_key[key] = container except json.JSONDecodeError: pass +containers = list(containers_by_key.values()) payload = { "ok": docker_rc == 0 and ps_rc == 0 and health_rc == 0,