mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 06:34:54 +08:00
fix(sync): 对齐 localized 状态契约
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ Rust 侧官方日服资源链路已经从实验验证推进到正式入口:
|
|||||||
11. 官方同步会拒绝危险输出目录、路径逃逸和现有 symlink 路径组件;下载目标、`.part`、manifest、snapshot、PID、status、log 和控制锁文件不会跟随 symlink,daemon 状态类文件默认以 `0600` 权限创建。
|
11. 官方同步会拒绝危险输出目录、路径逃逸和现有 symlink 路径组件;下载目标、`.part`、manifest、snapshot、PID、status、log 和控制锁文件不会跟随 symlink,daemon 状态类文件默认以 `0600` 权限创建。
|
||||||
12. `<output>/official-version-state.json` 会明确保存当前已完成版本、正在拉取版本、上一个可用版本和失败版本;同一 app version、bundle version 和 Addressables root 的失败只保留最新一条,同一版本开始重新拉取或后续发布成功时会清理对应失败记录;`bat status` 会显示最后成功时间、下次检查时间、最后错误摘要、当前阶段、当前下载 URL 进度、版本状态摘要、最近历史失败版本和原因、结构化日志路径和轮转日志路径,人类可读输出不会把完整版本状态 JSON 内联打印。
|
12. `<output>/official-version-state.json` 会明确保存当前已完成版本、正在拉取版本、上一个可用版本和失败版本;同一 app version、bundle version 和 Addressables root 的失败只保留最新一条,同一版本开始重新拉取或后续发布成功时会清理对应失败记录;`bat status` 会显示最后成功时间、下次检查时间、最后错误摘要、当前阶段、当前下载 URL 进度、版本状态摘要、最近历史失败版本和原因、结构化日志路径和轮转日志路径,人类可读输出不会把完整版本状态 JSON 内联打印。
|
||||||
13. 资源导入链路已支持 CAS + `ResourceRepository` 索引写入,官方同步可用 `--import-repository` / `BAT_IMPORT_REPOSITORY=1` 在已校验 release 发布后触发导入,默认 CAS 路径为 `<output>/.cas`、SQLite 索引为 `<output>/resources.sqlite`,也可通过 `--import-cas-root`、`--import-resource-db`、`BAT_IMPORT_CAS_ROOT`、`BAT_IMPORT_RESOURCE_DB` 覆盖;`resource.index` RPC 可按类型、hash、路径模式分页查询现有索引,数据库不存在时返回 `available=false` 且不会创建空库。`Resource` metadata 已通过 `metadata_json` 兼容迁移保存 release、平台、bundle path、parse status、TextAsset 名称和 TextUnit 数量/格式;当前/上一个/结构变化 catalog、失败 staging 复用、403/404、hash mismatch、CRC、metadata 迁移与 UnityFS 边界校验均有离线回归 fixture 或单测覆盖。
|
13. 资源导入链路已支持 CAS + `ResourceRepository` 索引写入,官方同步可用 `--import-repository` / `BAT_IMPORT_REPOSITORY=1` 在已校验 release 发布后触发导入,默认 CAS 路径为 `<output>/.cas`、SQLite 索引为 `<output>/resources.sqlite`,也可通过 `--import-cas-root`、`--import-resource-db`、`BAT_IMPORT_CAS_ROOT`、`BAT_IMPORT_RESOURCE_DB` 覆盖;`resource.index` RPC 可按类型、hash、路径模式分页查询现有索引,数据库不存在时返回 `available=false` 且不会创建空库。`Resource` metadata 已通过 `metadata_json` 兼容迁移保存 release、平台、bundle path、parse status、TextAsset 名称和 TextUnit 数量/格式;当前/上一个/结构变化 catalog、失败 staging 复用、403/404、hash mismatch、CRC、metadata 迁移与 UnityFS 边界校验均有离线回归 fixture 或单测覆盖。
|
||||||
14. 非 dry-run 官方同步在校验完成并发布后,会先对比上一完整 release 与当前 release 的 `official-download-manifest.json`,在当前 release 下写入 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`;同一 destination 只有 size 或 BLAKE3 改变才算 modified,仅 URL/CDN 根变化但内容相同不会触发解析/翻译候选。随后刷新 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;删除资源只进入差异记录,不进入 TextUnit/Crowdin 队列。`parse.text_units` 和 `parse.errors` RPC/CLI 可按 destination、archive entry、path id、class id、field path 和 format 查询当前 release 的 TextUnit 明细与解析错误;TextUnit 已包含 class id、field path、字段 offset/byte size 等可追溯定位。Crowdin 当前仅预留本地离线队列,不发网络请求;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析。官方同步报告默认 `localized_release_status=not_localized`,含义是原版资源已经发布、汉化资源未发布;UnityFS TextAsset patch 发布成功后会写 `localized-patch-manifest.json`,校验 hash/size/diff/rollback 后才允许 `localized.status` 返回 `localized`。`bat` 首次启动会在二进制所在目录释放 `.env` 配置模板(`0600`),之后每次启动自动加载(不覆盖已存在的环境变量),支持 `BAT_OUTPUT`/`BAT_LOCALIZED_OUTPUT`/`BAT_IMPORT_REPOSITORY`/`BAT_IMPORT_CAS_ROOT`/`BAT_IMPORT_RESOURCE_DB`/`BAT_STATE_DIR`/`BAT_AUTO_DISCOVER`/`BAT_WATCH`/`BAT_DAEMON`/`BAT_PROXY` 等键,实现编辑 `.env` 后无参启动;优先级为命令行参数 > 进程环境变量 > `.env` > 内置默认值,`BAT_SKIP_ENV_FILE=1` 可整体禁用;Redis 键为预留。daemon 任务历史持久化在 `<state-dir>/bat-tasks.json`(版本化、`0600` 原子写),重启后任务经 `task.*` 仍可查,中断任务标记 `task_interrupted`(`BAT-ERR-700005`)。
|
14. 非 dry-run 官方同步在校验完成并发布后,会先对比上一完整 release 与当前 release 的 `official-download-manifest.json`,在当前 release 下写入 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`;同一 destination 只有 size 或 BLAKE3 改变才算 modified,仅 URL/CDN 根变化但内容相同不会触发解析/翻译候选。随后刷新 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json` 和 `crowdin-textunit-queue.json`;删除资源只进入差异记录,不进入 TextUnit/Crowdin 队列。`parse.text_units` 和 `parse.errors` RPC/CLI 可按 destination、archive entry、path id、class id、field path 和 format 查询当前 release 的 TextUnit 明细与解析错误;TextUnit 已包含 class id、field path、字段 offset/byte size 等可追溯定位。Crowdin 当前仅预留本地离线队列,不发网络请求;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析。官方同步报告默认 `localized_release_status=not_localized`,含义是原版资源已经发布、汉化资源未发布;UnityFS TextAsset patch 发布成功后会写 `localized-patch-manifest.json`,校验 hash/size/diff/rollback 后才允许 `localized.status` 返回 `status=published`、`status_code=localized.published` 和 `localized_release_status=localized`。`bat` 首次启动会在二进制所在目录释放 `.env` 配置模板(`0600`),之后每次启动自动加载(不覆盖已存在的环境变量),支持 `BAT_OUTPUT`/`BAT_LOCALIZED_OUTPUT`/`BAT_IMPORT_REPOSITORY`/`BAT_IMPORT_CAS_ROOT`/`BAT_IMPORT_RESOURCE_DB`/`BAT_STATE_DIR`/`BAT_AUTO_DISCOVER`/`BAT_WATCH`/`BAT_DAEMON`/`BAT_PROXY` 等键,实现编辑 `.env` 后无参启动;优先级为命令行参数 > 进程环境变量 > `.env` > 内置默认值,`BAT_SKIP_ENV_FILE=1` 可整体禁用;Redis 键为预留。daemon 任务历史持久化在 `<state-dir>/bat-tasks.json`(版本化、`0600` 原子写),重启后任务经 `task.*` 仍可查,中断任务标记 `task_interrupted`(`BAT-ERR-700005`)。
|
||||||
|
|
||||||
仍需明确:这不是完整产品完成。完整 AssetBundle 重打包、翻译、Web、以及 `bat-api` 的服务器联调/可选业务扩展仍是后续工作;G-008(产品级 Go 同步 CLI)已决策关闭。真实官方网络全量拉取 smoke 已固化(G-018 已关闭);真实大文件与运行报告默认在 `/tmp` 隔离目录,不纳入 Git。Go 细节见 `docs/reports/GO_STATUS.md`。
|
仍需明确:这不是完整产品完成。完整 AssetBundle 重打包、翻译、Web、以及 `bat-api` 的服务器联调/可选业务扩展仍是后续工作;G-008(产品级 Go 同步 CLI)已决策关闭。真实官方网络全量拉取 smoke 已固化(G-018 已关闭);真实大文件与运行报告默认在 `/tmp` 隔离目录,不纳入 Git。Go 细节见 `docs/reports/GO_STATUS.md`。
|
||||||
|
|
||||||
|
|||||||
@@ -199,9 +199,12 @@ offset 和 error。TypeTree-covered managed reference 字段会进入结构化
|
|||||||
`localized.status` 严格按 daemon / `.env` 中的 `BAT_LOCALIZED_OUTPUT` 或
|
`localized.status` 严格按 daemon / `.env` 中的 `BAT_LOCALIZED_OUTPUT` 或
|
||||||
`--localized-output` 查询汉化产物目录,不把 `./bat-resources` 与
|
`--localized-output` 查询汉化产物目录,不把 `./bat-resources` 与
|
||||||
`./bat-localized` 混用。当前支持未汉化发布状态和已汉化发布状态的只读报告。
|
`./bat-localized` 混用。当前支持未汉化发布状态和已汉化发布状态的只读报告。
|
||||||
返回 `localized` 的条件是:`localized-version-state.json` 的官方 release ID
|
`status` / `status_code` 使用生命周期短状态和稳定状态码,例如
|
||||||
匹配当前官方 release,`current` symlink 指向汉化发布根下对应的
|
`pending` / `localized.pending`、`stale` / `localized.stale`、`published` /
|
||||||
`versions/<id>`,并且该版本目录中的
|
`localized.published`;旧的 `localized` / `not_localized` 业务标签放在
|
||||||
|
`localized_release_status`。返回 `localized_release_status=localized` 的条件是:
|
||||||
|
`localized-version-state.json` 的官方 release ID 匹配当前官方 release,
|
||||||
|
`current` symlink 指向汉化发布根下对应的 `versions/<id>`,并且该版本目录中的
|
||||||
`localized-patch-manifest.json` 存在且 release ID 匹配。响应会返回
|
`localized-patch-manifest.json` 存在且 release ID 匹配。响应会返回
|
||||||
`patch_manifest_path`、`patch_manifest_available`、
|
`patch_manifest_path`、`patch_manifest_available`、
|
||||||
`patch_manifest_matches_release`、`patch_file_count`、
|
`patch_manifest_matches_release`、`patch_file_count`、
|
||||||
|
|||||||
@@ -359,7 +359,7 @@
|
|||||||
- 官方同步报告新增 `localized_release_status=not_localized`,明确表示原版资源已发布、汉化资源未发布。
|
- 官方同步报告新增 `localized_release_status=not_localized`,明确表示原版资源已发布、汉化资源未发布。
|
||||||
- `LocalizedPatchService` 已具备将给定汉化文件按官方相对路径发布到 `--localized-output` / `BAT_LOCALIZED_OUTPUT` 指定目录下的 `.staging/<id>`、校验后移动到 `versions/<id>`、原子切换 `current` 并写入 `localized-version-state.json` 的基础能力。
|
- `LocalizedPatchService` 已具备将给定汉化文件按官方相对路径发布到 `--localized-output` / `BAT_LOCALIZED_OUTPUT` 指定目录下的 `.staging/<id>`、校验后移动到 `versions/<id>`、原子切换 `current` 并写入 `localized-version-state.json` 的基础能力。
|
||||||
- `LocalizedPatchService` 已写入结构化 `localized-patch-manifest.json`,记录 TextAsset 操作、原始/汉化 hash、size、byte delta 和 rollback 信息;发布前后会校验 manifest hash/size 与 current symlink,失败时清理 staging / 未完成 version。
|
- `LocalizedPatchService` 已写入结构化 `localized-patch-manifest.json`,记录 TextAsset 操作、原始/汉化 hash、size、byte delta 和 rollback 信息;发布前后会校验 manifest hash/size 与 current symlink,失败时清理 staging / 未完成 version。
|
||||||
- `localized.status` RPC 会读取 `.env` / daemon 配置中的汉化输出目录,校验汉化状态是否匹配当前官方 release,且要求 patch manifest 存在并匹配 release,避免写死 `./bat-localized` 或误报手工状态。
|
- `localized.status` RPC 会读取 `.env` / daemon 配置中的汉化输出目录,校验汉化状态是否匹配当前官方 release,且要求 patch manifest 存在并匹配 release,避免写死 `./bat-localized` 或误报手工状态;其中 `status` / `status_code` 返回生命周期状态,`localized_release_status` 保留 `localized` / `not_localized` 发布标签。
|
||||||
|
|
||||||
仍未完成:
|
仍未完成:
|
||||||
|
|
||||||
|
|||||||
@@ -3055,7 +3055,7 @@ fn build_localized_status_report(
|
|||||||
let state_path = localized_root.join(LOCALIZED_VERSION_STATE_FILE);
|
let state_path = localized_root.join(LOCALIZED_VERSION_STATE_FILE);
|
||||||
let current_path = localized_root.join(LOCALIZED_CURRENT_LINK);
|
let current_path = localized_root.join(LOCALIZED_CURRENT_LINK);
|
||||||
let state = read_localized_version_state(&localized_root)?;
|
let state = read_localized_version_state(&localized_root)?;
|
||||||
let mut status = "not_localized";
|
let mut localized_release_status = "not_localized";
|
||||||
let mut published_version_path = None;
|
let mut published_version_path = None;
|
||||||
let mut matches_current_official_release = false;
|
let mut matches_current_official_release = false;
|
||||||
let mut current_points_to_published_version = false;
|
let mut current_points_to_published_version = false;
|
||||||
@@ -3099,14 +3099,14 @@ fn build_localized_status_report(
|
|||||||
&& current_points_to_published_version
|
&& current_points_to_published_version
|
||||||
&& patch_manifest_matches_release
|
&& patch_manifest_matches_release
|
||||||
{
|
{
|
||||||
status = "localized";
|
localized_release_status = "localized";
|
||||||
flow_status_code = ReleaseFlowStatusCode::LocalizedPublished;
|
flow_status_code = ReleaseFlowStatusCode::LocalizedPublished;
|
||||||
published_version_path = Some(candidate);
|
published_version_path = Some(candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (_, status_code, status_phase, status_terminal, status_retryable) =
|
let (status, status_code, status_phase, status_terminal, status_retryable) =
|
||||||
flow_status_fields(flow_status_code);
|
flow_status_fields(flow_status_code);
|
||||||
|
|
||||||
Ok(serde_json::json!({
|
Ok(serde_json::json!({
|
||||||
@@ -3116,6 +3116,7 @@ fn build_localized_status_report(
|
|||||||
"status_phase": status_phase,
|
"status_phase": status_phase,
|
||||||
"status_terminal": status_terminal,
|
"status_terminal": status_terminal,
|
||||||
"status_retryable": status_retryable,
|
"status_retryable": status_retryable,
|
||||||
|
"localized_release_status": localized_release_status,
|
||||||
"official_current_version_id": official_version_id,
|
"official_current_version_id": official_version_id,
|
||||||
"localized_output_root": localized_root,
|
"localized_output_root": localized_root,
|
||||||
"state_path": state_path,
|
"state_path": state_path,
|
||||||
@@ -10913,8 +10914,9 @@ mod tests {
|
|||||||
let value = serde_json::to_value(&envelope).unwrap();
|
let value = serde_json::to_value(&envelope).unwrap();
|
||||||
assert_eq!(value["ok"], true);
|
assert_eq!(value["ok"], true);
|
||||||
assert_eq!(value["data"]["available"], true);
|
assert_eq!(value["data"]["available"], true);
|
||||||
assert_eq!(value["data"]["status"], "localized");
|
assert_eq!(value["data"]["status"], "published");
|
||||||
assert_eq!(value["data"]["status_code"], "localized.published");
|
assert_eq!(value["data"]["status_code"], "localized.published");
|
||||||
|
assert_eq!(value["data"]["localized_release_status"], "localized");
|
||||||
assert_eq!(value["data"]["official_current_version_id"], "v-current");
|
assert_eq!(value["data"]["official_current_version_id"], "v-current");
|
||||||
assert_eq!(value["data"]["matches_current_official_release"], true);
|
assert_eq!(value["data"]["matches_current_official_release"], true);
|
||||||
assert_eq!(value["data"]["current_points_to_published_version"], true);
|
assert_eq!(value["data"]["current_points_to_published_version"], true);
|
||||||
@@ -10972,8 +10974,9 @@ mod tests {
|
|||||||
let value = serde_json::to_value(&envelope).unwrap();
|
let value = serde_json::to_value(&envelope).unwrap();
|
||||||
assert_eq!(value["ok"], true);
|
assert_eq!(value["ok"], true);
|
||||||
assert_eq!(value["data"]["available"], true);
|
assert_eq!(value["data"]["available"], true);
|
||||||
assert_eq!(value["data"]["status"], "not_localized");
|
assert_eq!(value["data"]["status"], "stale");
|
||||||
assert_eq!(value["data"]["status_code"], "localized.stale");
|
assert_eq!(value["data"]["status_code"], "localized.stale");
|
||||||
|
assert_eq!(value["data"]["localized_release_status"], "not_localized");
|
||||||
assert_eq!(value["data"]["matches_current_official_release"], false);
|
assert_eq!(value["data"]["matches_current_official_release"], false);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
value["data"]["published_version_path"],
|
value["data"]["published_version_path"],
|
||||||
|
|||||||
Reference in New Issue
Block a user