fix(release): 保留空目录到 ZIP 产物
移除 count_files 和 dir_has_included_files 两个预检函数,不再在打包时 过滤空目录。所有遍历到的子目录(包括空目录和仅含 .gitkeep 的目录) 现在都会写入 ZIP 条目,确保项目目录结构完整保留。 新增三个测试覆盖:空子目录、仅 .gitkeep 目录、emod-package 匹配的 空目录。
This commit is contained in:
@@ -214,10 +214,6 @@ fn add_directory_to_zip(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if count_files(src_dir, project_dir, ignore)? == 0 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = SimpleFileOptions::default();
|
let options = SimpleFileOptions::default();
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
@@ -243,9 +239,7 @@ fn add_directory_to_zip(
|
|||||||
f.read_to_end(&mut buffer)?;
|
f.read_to_end(&mut buffer)?;
|
||||||
zip.write_all(&buffer)?;
|
zip.write_all(&buffer)?;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
} else if !relative_path.as_os_str().is_empty()
|
} else if !relative_path.as_os_str().is_empty() {
|
||||||
&& count_files(path, project_dir, ignore)? > 0
|
|
||||||
{
|
|
||||||
zip.add_directory(&path_str, options)?;
|
zip.add_directory(&path_str, options)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,7 +323,7 @@ fn add_with_package(
|
|||||||
if !is_dir {
|
if !is_dir {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 当前目录未命中且无被包含的子孙时,无需写出目录条目。
|
// 当前目录未命中,不写出目录条目;WalkDir 仍会继续遍历子孙。
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,9 +337,7 @@ fn add_with_package(
|
|||||||
.map_err(|e| file::io_error("读取打包文件", path, e))?;
|
.map_err(|e| file::io_error("读取打包文件", path, e))?;
|
||||||
zip.write_all(&buffer)?;
|
zip.write_all(&buffer)?;
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
} else if !relative_path.as_os_str().is_empty()
|
} else if !relative_path.as_os_str().is_empty() {
|
||||||
&& dir_has_included_files(path, project_dir, package, ignore)?
|
|
||||||
{
|
|
||||||
zip.add_directory(&path_str, options)?;
|
zip.add_directory(&path_str, options)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,57 +345,6 @@ fn add_with_package(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dir_has_included_files(
|
|
||||||
dir: &Path,
|
|
||||||
project_dir: &Path,
|
|
||||||
package: &EmodPackage,
|
|
||||||
ignore: &EmodIgnore,
|
|
||||||
) -> Result<bool> {
|
|
||||||
for entry in walkdir::WalkDir::new(dir) {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
if !path.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if path.display().to_string().ends_with(".gitkeep") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let relative_path = path
|
|
||||||
.strip_prefix(project_dir)
|
|
||||||
.map_err(|e| crate::error::CliError::InvalidData(e.to_string()))?;
|
|
||||||
if ignore.is_ignored(relative_path, false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !package.matches(relative_path, false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_files(dir: &Path, project_dir: &Path, ignore: &EmodIgnore) -> Result<usize> {
|
|
||||||
let mut count = 0;
|
|
||||||
for entry in walkdir::WalkDir::new(dir) {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
if !path.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let relative_path = path
|
|
||||||
.strip_prefix(project_dir)
|
|
||||||
.map_err(|e| crate::error::CliError::InvalidData(e.to_string()))?;
|
|
||||||
|
|
||||||
if !path.display().to_string().ends_with(".gitkeep")
|
|
||||||
&& !ignore.is_ignored(relative_path, false)
|
|
||||||
{
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EmodIgnore {
|
struct EmodIgnore {
|
||||||
rules: Vec<IgnoreRule>,
|
rules: Vec<IgnoreRule>,
|
||||||
}
|
}
|
||||||
@@ -803,6 +744,68 @@ mod tests {
|
|||||||
fs::remove_dir_all(project_dir).unwrap();
|
fs::remove_dir_all(project_dir).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn package_project_keeps_empty_subdirectory() {
|
||||||
|
let project_dir = temp_project_dir();
|
||||||
|
let release_info = release_info();
|
||||||
|
|
||||||
|
fs::create_dir_all(project_dir.join("behavior_pack_abcdefgh/empty_dir")).unwrap();
|
||||||
|
fs::create_dir_all(project_dir.join("resource_pack_ijklmnop")).unwrap();
|
||||||
|
|
||||||
|
let pack_dirs = pack_dirs(&project_dir, &release_info);
|
||||||
|
let zip_path =
|
||||||
|
package_project_with_artifacts_env_unset(&project_dir, &pack_dirs, "1.0.0").unwrap();
|
||||||
|
let entries = zip_entries(&zip_path);
|
||||||
|
|
||||||
|
assert!(entries.contains(&"behavior_pack_abcdefgh/empty_dir/".to_string()));
|
||||||
|
|
||||||
|
fs::remove_dir_all(project_dir).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn package_project_keeps_dir_with_only_gitkeep() {
|
||||||
|
let project_dir = temp_project_dir();
|
||||||
|
let release_info = release_info();
|
||||||
|
|
||||||
|
write_file(
|
||||||
|
&project_dir.join("behavior_pack_abcdefgh/only_gitkeep/.gitkeep"),
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
fs::create_dir_all(project_dir.join("resource_pack_ijklmnop")).unwrap();
|
||||||
|
|
||||||
|
let pack_dirs = pack_dirs(&project_dir, &release_info);
|
||||||
|
let zip_path =
|
||||||
|
package_project_with_artifacts_env_unset(&project_dir, &pack_dirs, "1.0.0").unwrap();
|
||||||
|
let entries = zip_entries(&zip_path);
|
||||||
|
|
||||||
|
assert!(entries.contains(&"behavior_pack_abcdefgh/only_gitkeep/".to_string()));
|
||||||
|
assert!(!entries.contains(&"behavior_pack_abcdefgh/only_gitkeep/.gitkeep".to_string()));
|
||||||
|
|
||||||
|
fs::remove_dir_all(project_dir).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn emod_package_keeps_matched_empty_subdirectory() {
|
||||||
|
let project_dir = temp_project_dir();
|
||||||
|
let release_info = release_info();
|
||||||
|
|
||||||
|
write_file(
|
||||||
|
&project_dir.join(".emod-package"),
|
||||||
|
"behavior_pack_abcdefgh/empty_dir/\n",
|
||||||
|
);
|
||||||
|
fs::create_dir_all(project_dir.join("behavior_pack_abcdefgh/empty_dir")).unwrap();
|
||||||
|
fs::create_dir_all(project_dir.join("resource_pack_ijklmnop")).unwrap();
|
||||||
|
|
||||||
|
let pack_dirs = pack_dirs(&project_dir, &release_info);
|
||||||
|
let zip_path =
|
||||||
|
package_project_with_artifacts_env_unset(&project_dir, &pack_dirs, "1.0.0").unwrap();
|
||||||
|
let entries = zip_entries(&zip_path);
|
||||||
|
|
||||||
|
assert!(entries.contains(&"behavior_pack_abcdefgh/empty_dir/".to_string()));
|
||||||
|
|
||||||
|
fs::remove_dir_all(project_dir).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn package_project_includes_world_pack_json_in_default_mode() {
|
fn package_project_includes_world_pack_json_in_default_mode() {
|
||||||
let project_dir = temp_project_dir();
|
let project_dir = temp_project_dir();
|
||||||
|
|||||||
Reference in New Issue
Block a user