Merge pull request #1369 from pikasTech/fix/1352-remote-tool-cache-gc

Fix remote GC tool cache cleanup
This commit is contained in:
Lyon
2026-07-01 12:07:57 +08:00
committed by GitHub
2 changed files with 59 additions and 0 deletions
+58
View File
@@ -13,6 +13,7 @@ interface RemoteGcOptions {
buildCacheUntil: string;
tmp: boolean;
tmpMinAgeHours: number;
toolCaches: boolean;
aptCache: boolean;
coreDumps: boolean;
coreDumpMinAgeHours: number;
@@ -39,6 +40,7 @@ const DEFAULT_REMOTE_OPTIONS: RemoteGcOptions = {
buildCacheUntil: "24h",
tmp: true,
tmpMinAgeHours: 24,
toolCaches: false,
aptCache: true,
coreDumps: true,
coreDumpMinAgeHours: 1,
@@ -144,6 +146,10 @@ function parseRemoteGcOptions(args: string[]): RemoteGcOptions {
options.buildCache = false;
} else if (arg === "--no-tmp") {
options.tmp = false;
} else if (arg === "--include-tool-caches") {
options.toolCaches = true;
} else if (arg === "--no-tool-caches") {
options.toolCaches = false;
} else if (arg === "--no-apt-cache") {
options.aptCache = false;
} else if (arg === "--no-core-dumps") {
@@ -280,6 +286,24 @@ CORE_DUMP_DIR_ALLOWLIST = set([
"/root/unidesk",
])
TOOL_CACHE_ALLOWLIST = [
{
"id": "npm-cacache",
"path": "/root/.npm/_cacache",
"description": "Delete npm content-addressable package cache; npm can rebuild it.",
},
{
"id": "npm-npx",
"path": "/root/.npm/_npx",
"description": "Delete npx package execution cache; npx can rebuild it.",
},
{
"id": "bun-install-cache",
"path": "/root/.bun/install/cache",
"description": "Delete Bun install package cache; bun can rebuild it.",
},
]
REGISTRY_REPOSITORY_ROOT = "/var/lib/hwlab/registry/docker/registry/v2/repositories"
REGISTRY_ROOT = "/var/lib/hwlab/registry"
REGISTRY_PROTECTED_TAGS = set([
@@ -1819,6 +1843,23 @@ def collect_candidates(observed_at):
"action": {"command": ["apt-get", "clean"]},
})
if OPTIONS.get("toolCaches", False):
for item in TOOL_CACHE_ALLOWLIST:
path = item["path"]
size = du_size(path, 8) or 0
if size <= 0:
continue
candidates.append({
"id": "tool-cache:%s" % item["id"],
"kind": "tool-cache-delete",
"risk": "medium",
"description": item["description"],
"path": path,
"sizeBytes": size,
"estimatedReclaimBytes": size,
"action": {"op": "rm-recursive", "allowlist": "remote-tool-cache"},
})
if OPTIONS.get("coreDumps", True):
cutoff = time.time() - float(OPTIONS.get("coreDumpMinAgeHours") or 1) * 3600
for root in sorted(CORE_DUMP_DIR_ALLOWLIST):
@@ -2037,6 +2078,14 @@ def assert_core_dump_candidate(path):
if fuser["exitCode"] == 0:
raise RuntimeError("refusing to remove core dump with active process reference: %s" % path)
def assert_tool_cache_candidate(path):
resolved = os.path.abspath(path)
allowed = set(item["path"] for item in TOOL_CACHE_ALLOWLIST)
if resolved not in allowed:
raise RuntimeError("refusing to remove tool cache outside allowlist: %s" % path)
if os.path.islink(resolved):
raise RuntimeError("refusing to remove symlink tool cache: %s" % path)
def execute(candidate):
kind = candidate.get("kind")
if kind == "journal-vacuum":
@@ -2065,6 +2114,15 @@ def execute(candidate):
raise RuntimeError((result["stderr"] or "apt-get clean failed").strip())
after = du_size("/var/cache/apt/archives") or 0
return {"reclaimedBytes": max(0, before - after), "commandOutput": bounded(result)}
if kind == "tool-cache-delete":
path = candidate.get("path") or ""
assert_tool_cache_candidate(path)
before = du_size(path, 8) or path_size(path)
if os.path.isdir(path):
shutil.rmtree(path, ignore_errors=True)
elif os.path.exists(path):
os.unlink(path)
return {"reclaimedBytes": before}
if kind == "tmp-path-delete":
path = candidate.get("path") or ""
assert_tmp_candidate(path)
+1
View File
@@ -350,6 +350,7 @@ function gcHelp(): unknown {
"--result-limit N": "number of per-candidate run results returned when --full is not set; default 50",
"--full|--raw": "return and run against all candidates rather than the default bounded page",
"--include-browser-cache": "also remove repo-local .state/playwright-browsers cache",
"--include-tool-caches": "local and remote explicit opt-in: remove rebuildable npm/npx/Bun package caches from fixed allowlisted paths",
"--include-state-artifacts": "manual local gc only: opt in to stale UniDesk .state artifact retention for allowlisted diagnostic files and deploy artifact direct directories",
"--state-artifact-keep-days N": "keep recent UniDesk .state artifacts for N days; default 14; must be a positive integer",
"--include-state-stale-scratch": "manual local gc only: opt in to YAML allowlisted stale .state scratch roots; roots and keepHours come from config/unidesk-cli.yaml#gc",