fix(official-sync): 清理未被引用的孤儿 staging 目录

同版本失败 staging 复用已由 060b7d7 处理,但跨版本失败、或被 trim/去重挤出
failed_versions 记录后残留的 .staging/<id> 目录永不被复用也无人清理,长期 daemon
场景下多 GB 孤儿目录会累积耗尽磁盘。

新增 gc_orphan_staging:枚举 .staging 下子目录,只保留 in_progress_version 与
failed_versions 引用的 <id>,删除其余(跳过普通文件与 symlink,staging 根不存在
时无操作)。在同步成功发布后(读取最新版本状态)和 clean-stable(后台已停止)两处
调用;GC 失败仅告警,不影响发布结果。

对应 issue #18 维护清单 1-4。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 07:29:37 -07:00
co-authored by Claude Fable 5
parent 4d818f512c
commit 3cb3637749
3 changed files with 151 additions and 9 deletions
+11 -6
View File
@@ -1,11 +1,11 @@
use bat_adapters::official::yostar_jp::PatchPlatform;
use bat_infrastructure::{
lexical_absolute, open_append_file, read_file_no_symlink, read_version_state, redact_proxy_url,
resolve_curl_proxy, validate_output_root, validate_runtime_state_dir, write_file_atomic,
CurlProxyConfig, CurlProxyMode, OfficialFailedVersionRecord, OfficialServerInfoSource,
OfficialUpdateConfig, OfficialUpdateProgress, OfficialUpdateReport, OfficialUpdateService,
OfficialUpdateStatus, OfficialVerificationSummary, OfficialVersionRecord, OfficialVersionState,
PRIVATE_FILE_MODE,
gc_orphan_staging, lexical_absolute, open_append_file, read_file_no_symlink,
read_version_state, redact_proxy_url, resolve_curl_proxy, validate_output_root,
validate_runtime_state_dir, write_file_atomic, CurlProxyConfig, CurlProxyMode,
OfficialFailedVersionRecord, OfficialServerInfoSource, OfficialUpdateConfig,
OfficialUpdateProgress, OfficialUpdateReport, OfficialUpdateService, OfficialUpdateStatus,
OfficialVerificationSummary, OfficialVersionRecord, OfficialVersionState, PRIVATE_FILE_MODE,
};
use serde::{Deserialize, Serialize};
use std::env;
@@ -2979,6 +2979,11 @@ fn run_clean_stable_command(options: &CliOptions) -> anyhow::Result<()> {
removed_paths.push(proxy_secret_path);
}
// 后台已停止,清理未被版本状态引用的孤儿 staging 目录。
if let Some(state) = read_version_state(&options.config.version_state_path())? {
removed_paths.extend(gc_orphan_staging(&options.config.output_root, &state)?);
}
let report = CleanStableReport {
command: "clean-stable",
status: if skipped_paths.is_empty() {