feat(bat): 补全工作流校验与调度过滤
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s

新增 parse clear-cache 和 i18n validate,补齐 schedule 的作用域过滤与单轮执行上限,并将列表过滤参数暴露给 bat-api dashboard。同步 RPC、OpenAPI、用户文档和回归测试。

Refs #43
This commit is contained in:
2026-08-03 23:47:59 +08:00
parent 0784d5b532
commit 1933d6acb0
19 changed files with 714 additions and 49 deletions
@@ -28,6 +28,38 @@ pub(super) fn run_parse_once(options: &CliOptions) -> anyhow::Result<()> {
)
}
pub(super) fn run_parse_clear_cache(options: &CliOptions) -> anyhow::Result<()> {
let (resource_root, release_id) = current_official_release(options)?;
let artifact_names = [
OFFICIAL_PARSE_CACHE_FILE,
OFFICIAL_TEXTUNIT_INDEX_FILE,
OFFICIAL_TEXTUNIT_TASK_QUEUE_FILE,
CROWDIN_TEXTUNIT_QUEUE_FILE,
];
let mut removed = Vec::new();
for name in artifact_names {
let path = resource_root.join(name);
if remove_regenerable_file(&path)? {
removed.push(path);
}
}
let data = serde_json::json!({
"official_release_id": release_id,
"resource_root": resource_root,
"removed": removed,
"translation_task_repository_preserved": true,
});
print_report(
options.output_format,
&CommandReport {
command: "parse-clear-cache",
status: "cleared",
message: "当前官方 release 的可再生解析缓存和翻译队列已清理",
data,
},
)
}
pub(super) fn run_translate_once(options: &CliOptions) -> anyhow::Result<()> {
let (resource_root, release_id) = current_official_release(options)?;
let queue = write_official_textunit_queues(&resource_root).map_err(anyhow::Error::msg)?;
@@ -64,6 +96,23 @@ pub(super) fn run_translate_once(options: &CliOptions) -> anyhow::Result<()> {
)
}
pub(super) fn run_translation_validate(options: &CliOptions) -> anyhow::Result<()> {
let path = options
.translation_file
.as_ref()
.ok_or_else(|| anyhow::anyhow!("i18n validate 必须指定 --translation-file"))?;
let (resource_root, release_id) = current_official_release(options)?;
let workbench = read_translation_workbench(path)?;
let validation = validate_translation_workbench(&resource_root, &release_id, &workbench)?;
let data = serde_json::json!({
"official_release_id": release_id,
"resource_root": resource_root,
"translation_file": path,
"validation": validation,
});
print_json_value(options.output_format, &data)
}
pub(super) fn run_translation_set(options: &CliOptions) -> anyhow::Result<()> {
let path = options
.translation_file
@@ -191,3 +240,25 @@ fn current_official_release(options: &CliOptions) -> anyhow::Result<(PathBuf, St
}
Ok((resource_root, release_id))
}
fn remove_regenerable_file(path: &Path) -> anyhow::Result<bool> {
let metadata = match fs::symlink_metadata(path) {
Ok(metadata) => metadata,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
Err(error) => return Err(error.into()),
};
if metadata.file_type().is_symlink() {
return Err(anyhow::anyhow!(
"拒绝删除符号链接形式的可再生文件:{}",
path.display()
));
}
if !metadata.is_file() {
return Err(anyhow::anyhow!(
"可再生缓存路径不是普通文件:{}",
path.display()
));
}
fs::remove_file(path)?;
Ok(true)
}