修复发行包遗漏项目素材

构建项目发布时补入 dist 未包含的项目根 assets 文件,并优先保留 dist 同路径产物。

增加 Phaser 发行包素材回归测试并同步发行契约文档。
This commit is contained in:
2026-10-02 18:33:36 +08:00
parent db344426f2
commit c8e4ca2512
3 changed files with 60 additions and 1 deletions
@@ -738,6 +738,20 @@ pub(crate) fn collect_project_export_package_files(
.ok_or("构建产物路径非法")?
);
}
// AGC 上传的素材登记在项目根 `assets/`,部分 Phaser 工程直接以
// `assets/uploads/...` 作为运行 URL,没有通过 Vite public 目录复制到 dist。
// 构建产物优先;只有 dist 没有同路径时才补入根目录素材,避免重复 ZIP 条目。
let assets_root = resolve_local_project_path(root, "assets")?;
if assets_root.exists() {
let mut root_assets = Vec::new();
collect_project_export_package_dir_files(root, "assets", &mut root_assets)?;
for entry in root_assets {
let dist_path = format!("game/{}", entry.0);
if !files.iter().any(|(path, _, _)| path == &dist_path) {
files.push(entry);
}
}
}
} else {
collect_project_export_package_dir_files(root, "game", &mut files)?;
}
@@ -4356,6 +4356,51 @@ async fn publish_export_reports_actionable_error_without_entry_or_build_script()
fs::remove_dir_all(root).ok();
}
#[test]
fn built_project_export_includes_uploaded_assets_and_prefers_dist_assets() {
let root = unique_project_path();
init_existing_html_project_at(&root, "project-built-assets", "发行素材项目")
.expect("project init");
fs::create_dir_all(root.join("game/dist/assets/uploads")).expect("create dist assets");
fs::create_dir_all(root.join("assets/uploads")).expect("create uploaded assets");
fs::write(
root.join("game/dist/index.html"),
"<!doctype html><html><body><canvas></canvas><script>window.started=true;</script></body></html>",
)
.expect("write built entry");
fs::write(root.join("assets/uploads/food.png"), b"root-food").expect("write uploaded food");
fs::write(root.join("assets/uploads/shared.png"), b"root-shared")
.expect("write root shared asset");
fs::write(
root.join("game/dist/assets/uploads/shared.png"),
b"dist-shared",
)
.expect("write dist shared asset");
fs::create_dir_all(root.join("exports")).expect("create exports");
fs::write(root.join("exports/README.md"), "publish notes").expect("write readme");
let exported = export_local_project_package_at(&root).expect("export built project");
let payload = read_local_project_export_package_at(&root, &exported.package_relative_path)
.expect("normalize publish package");
let mut archive = zip::ZipArchive::new(std::io::Cursor::new(payload.package_bytes))
.expect("read normalized package");
let mut food = Vec::new();
archive
.by_name("assets/uploads/food.png")
.expect("uploaded asset must be packaged")
.read_to_end(&mut food)
.expect("read uploaded asset");
assert_eq!(food, b"root-food");
let mut shared = Vec::new();
archive
.by_name("assets/uploads/shared.png")
.expect("shared asset must be unique")
.read_to_end(&mut shared)
.expect("read shared asset");
assert_eq!(shared, b"dist-shared");
fs::remove_dir_all(root).ok();
}
#[test]
fn local_project_export_package_publish_payload_contains_bytes_and_file_digests() {
let root = unique_project_path();