fix: add remote tool cache gc
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user