From a23c954258722d93dc8f9c494b2a54ba516f480e Mon Sep 17 00:00:00 2001 From: pikastech Date: Tue, 21 Jul 2026 08:02:59 +0200 Subject: [PATCH] fix: add scoped BuildKit GC entry --- docs/reference/gc.md | 1 + scripts/src/gc-remote-runner.py | 22 ++++++++++++++++++++++ scripts/src/gc-remote.ts | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/docs/reference/gc.md b/docs/reference/gc.md index 27a533f2..46504f14 100644 --- a/docs/reference/gc.md +++ b/docs/reference/gc.md @@ -138,6 +138,7 @@ Worktree 清理由 `--include-merged-worktrees` 显式启用,只扫描 `config - systemd journal vacuum 到目标上限。 - Docker `json-file` 日志截断。 - Docker BuildKit cache prune 只清理 owning YAML `hostDockerGc.buildCachePrune.until` 声明年龄之外的 cache;显式 `--build-cache-until` 可覆盖本次计划,不清理 images、containers 或 volumes。 +- `--build-cache-only` 提供短连接 scoped plan/run,跳过 PVC、registry 和全盘 collectors;确认态仍要求 `--confirm` 并复用同一 YAML 筛选器。 - apt archive clean。 - allowlisted `/tmp` 诊断目录删除。 - 受限 core dump 删除。 diff --git a/scripts/src/gc-remote-runner.py b/scripts/src/gc-remote-runner.py index 297e1bc0..0957edf4 100644 --- a/scripts/src/gc-remote-runner.py +++ b/scripts/src/gc-remote-runner.py @@ -1071,6 +1071,25 @@ def parse_docker_build_cache(text): return {"sizeBytes": size, "reclaimableBytes": reclaim} return None +def build_cache_only_payload(observed_at): + disk_before = df_snapshot() + section = host_docker_gc_section("buildCachePrune") + until = str(OPTIONS.get("buildCacheUntil") or section.get("until") or "24h") + if not host_docker_gc_enabled("buildCachePrune"): + return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": host_docker_gc_reason("buildCachePrune", "BuildKit cache GC is disabled by YAML")} + system_df = command(["docker", "system", "df"], 8) + if system_df["exitCode"] != 0: + return {"ok": False, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "dryRun": ACTION == "plan", "mutation": False, "diagnosis": bounded(system_df)} + cache = parse_docker_build_cache(system_df["stdout"]) + candidate = None + if cache is not None and cache["reclaimableBytes"] > 0: + candidate = {"id": "docker-builder:prune", "kind": "docker-build-cache-prune", "risk": "low", "sizeBytes": cache["sizeBytes"], "estimatedReclaimBytes": cache["reclaimableBytes"], "action": {"command": ["docker", "builder", "prune", "--all", "--force", "--filter", "until=%s" % until]}} + payload = {"ok": True, "action": "gc remote %s" % ACTION, "providerId": PROVIDER_ID, "scope": "build-cache-only", "configSource": "config/unidesk-cli.yaml#gc.remote.targets.%s.hostDockerGc.buildCachePrune" % PROVIDER_ID, "dryRun": ACTION == "plan", "mutation": ACTION == "run" and candidate is not None, "observedAt": observed_at, "diskBefore": disk_before, "until": until, "candidates": [candidate] if candidate else []} + if ACTION == "run" and candidate is not None: + payload["result"] = execute(candidate) + payload["diskAfter"] = df_snapshot() + return payload + def docker_containers(): ps = command(["docker", "ps", "-qa", "--no-trunc"], 5) if ps["exitCode"] != 0 or not ps["stdout"].strip(): @@ -2776,6 +2795,9 @@ def main(): else: emit_json(start_memory_pressure_job(observed_at, visible), persist_large=True) return 0 + if bool(OPTIONS.get("buildCacheOnly")) and ACTION in set(["plan", "run"]): + emit_json(build_cache_only_payload(observed_at), persist_large=False) + return 0 if (bool(OPTIONS.get("registry")) or bool(OPTIONS.get("hwlabRegistry"))) and ACTION == "plan": emit_json(registry_plan_payload(observed_at), persist_large=True) return 0 diff --git a/scripts/src/gc-remote.ts b/scripts/src/gc-remote.ts index b9b52586..8014ac93 100644 --- a/scripts/src/gc-remote.ts +++ b/scripts/src/gc-remote.ts @@ -38,6 +38,7 @@ interface RemoteGcOptions { historyLimit: number; saveSnapshot: boolean; memoryPressureOnly: boolean; + buildCacheOnly: boolean; } const DEFAULT_REMOTE_OPTIONS: RemoteGcOptions = { @@ -69,6 +70,7 @@ const DEFAULT_REMOTE_OPTIONS: RemoteGcOptions = { historyLimit: 12, saveSnapshot: true, memoryPressureOnly: false, + buildCacheOnly: false, }; const GC_CONFIG_RELATIVE_PATH = "config/unidesk-cli.yaml"; @@ -298,6 +300,8 @@ function parseRemoteGcOptions(args: string[]): RemoteGcOptions { options.saveSnapshot = false; } else if (arg === "--memory-pressure-only") { options.memoryPressureOnly = true; + } else if (arg === "--build-cache-only") { + options.buildCacheOnly = true; } else { throw new Error(`unknown gc remote option: ${arg}`); }