aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-18 09:40:28 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-18 09:40:28 +0800
commita7adc172b3cc58e0555b1cddb45d6301c733be81 (patch)
tree88fd311937e710bf3660da9e5753ae17143d817f
parentff3293f123a8768fc3b24715b6404ff3a0ab63da (diff)
refactor(ci-new): flatten collect directory layout
Store result files directly in `collect/` as `{task}.{platform}.ok` and `{task}.{platform}.{package}.err` instead of nesting under task/platform subdirectories.
-rw-r--r--mingling_ci/src/reporter.rs50
-rw-r--r--mingling_ci/src/res/collect_logs.rs82
2 files changed, 72 insertions, 60 deletions
diff --git a/mingling_ci/src/reporter.rs b/mingling_ci/src/reporter.rs
index 1cd5b2b..ef626d9 100644
--- a/mingling_ci/src/reporter.rs
+++ b/mingling_ci/src/reporter.rs
@@ -104,7 +104,8 @@ pub fn export_on(package: &str, platform: ReportPlatform, result: ReportResult)
}
}
-/// Writes buffered successes to `{task}/{platform}/ok`, one package per line.
+/// Writes buffered successes to `collect/{task}.{platform}.ok`, one package per
+/// line.
///
/// # Panics
///
@@ -116,38 +117,43 @@ pub fn flush() {
};
let buffered = std::mem::take(&mut *OK_BUFFER.lock().unwrap());
+ if buffered.is_empty() {
+ return;
+ }
+
+ if let Err(e) = fs::create_dir_all(COLLECT_DIR) {
+ eprintln!("reporter: failed to create {COLLECT_DIR}: {e}");
+ return;
+ }
+
for (platform, packages) in buffered {
- let dir = Path::new(COLLECT_DIR).join(&task).join(platform.dir_name());
- if let Err(e) = fs::create_dir_all(&dir) {
- eprintln!("reporter: failed to create {}: {e}", dir.display());
- continue;
- }
let content = if packages.is_empty() {
String::new()
} else {
packages.join("\n") + "\n"
};
- let path = dir.join("ok");
+ let platform_name = platform.dir_name();
+ let path = Path::new(COLLECT_DIR).join(format!("{task}.{platform_name}.ok"));
if let Err(e) = fs::write(&path, content) {
eprintln!("reporter: failed to write {}: {e}", path.display());
}
}
}
-/// Writes a failure entry to `{task}/{platform}/{package}.err`.
+/// Writes a failure entry to `collect/{task}.{platform}.{package}.err`.
fn write_err(package: &str, platform: ReportPlatform, output: String) {
let Some(task) = CURRENT_TASK.lock().unwrap().clone() else {
eprintln!("reporter: no current task; call reporter::set_task first");
return;
};
- let dir = Path::new(COLLECT_DIR).join(task).join(platform.dir_name());
- if let Err(e) = fs::create_dir_all(&dir) {
- eprintln!("reporter: failed to create {}: {e}", dir.display());
+ if let Err(e) = fs::create_dir_all(COLLECT_DIR) {
+ eprintln!("reporter: failed to create {COLLECT_DIR}: {e}");
return;
}
- let path = dir.join(format!("{package}.err"));
+ let platform_name = platform.dir_name();
+ let path = Path::new(COLLECT_DIR).join(format!("{task}.{platform_name}.{package}.err"));
if let Err(e) = fs::write(&path, output) {
eprintln!("reporter: failed to write {}: {e}", path.display());
}
@@ -160,19 +166,23 @@ mod tests {
#[test]
fn export_writes_ok_and_err_files() {
set_task("reporter-test");
- let task_root = Path::new(COLLECT_DIR).join("reporter-test");
- let dir = task_root.join(current_platform().dir_name());
- fs::remove_dir_all(&task_root).ok();
+ let platform_name = current_platform().dir_name();
+ let ok_path = Path::new(COLLECT_DIR).join(format!("reporter-test.{platform_name}.ok"));
+ let err_path =
+ Path::new(COLLECT_DIR).join(format!("reporter-test.{platform_name}.pkg-b.err"));
+ fs::remove_file(&ok_path).ok();
+ fs::remove_file(&err_path).ok();
export("pkg-a", ReportResult::Ok);
export("pkg-b", ReportResult::Error("boom".to_string()));
flush();
- assert!(dir.join("ok").is_file());
- assert_eq!(fs::read_to_string(dir.join("ok")).unwrap(), "pkg-a\n");
- assert!(dir.join("pkg-b.err").is_file());
- assert_eq!(fs::read_to_string(dir.join("pkg-b.err")).unwrap(), "boom");
+ assert!(ok_path.is_file());
+ assert_eq!(fs::read_to_string(&ok_path).unwrap(), "pkg-a\n");
+ assert!(err_path.is_file());
+ assert_eq!(fs::read_to_string(&err_path).unwrap(), "boom");
- fs::remove_dir_all(&task_root).ok();
+ fs::remove_file(ok_path).ok();
+ fs::remove_file(err_path).ok();
}
}
diff --git a/mingling_ci/src/res/collect_logs.rs b/mingling_ci/src/res/collect_logs.rs
index df0af5b..7c5b375 100644
--- a/mingling_ci/src/res/collect_logs.rs
+++ b/mingling_ci/src/res/collect_logs.rs
@@ -26,54 +26,34 @@ pub struct ResCollectLogs {
}
impl ResCollectLogs {
- /// Reads `collect/{task}/{os}/` — the aggregate `ok` file (one package per
- /// line) and per-package `{package}.err` files — plus the git info.
+ /// Reads the flat `collect/` directory — aggregate `{task}.{os}.ok` files
+ /// (one package per line) and per-package `{task}.{os}.{package}.err`
+ /// files — plus the git info.
#[must_use]
pub fn read() -> Self {
let mut logs = Self::default();
- if let Ok(task_entries) = std::fs::read_dir(COLLECT_DIR) {
- for task_entry in task_entries.flatten() {
- if !task_entry.file_type().is_ok_and(|t| t.is_dir()) {
- continue;
- }
- let task = task_entry.file_name().to_string_lossy().into_owned();
-
- let Ok(os_entries) = std::fs::read_dir(task_entry.path()) else {
- continue;
- };
- for os_entry in os_entries.flatten() {
- if !os_entry.file_type().is_ok_and(|t| t.is_dir()) {
- continue;
- }
- let os = os_entry.file_name().to_string_lossy().into_owned();
-
- let Ok(files) = std::fs::read_dir(os_entry.path()) else {
- continue;
- };
- for file in files.flatten() {
- let file_name = file.file_name().to_string_lossy().into_owned();
- if file_name == "ok" {
- // Aggregate success file: one package name per line.
- if let Ok(content) = std::fs::read_to_string(file.path()) {
- for package in content.lines().filter(|l| !l.is_empty()) {
- logs.statuses
- .entry((task.clone(), package.to_string()))
- .or_default()
- .insert(os.clone(), true);
- }
- }
- } else if let Some(package) = file_name.strip_suffix(".err") {
- let package = package.to_string();
+ if let Ok(entries) = std::fs::read_dir(COLLECT_DIR) {
+ for entry in entries.flatten() {
+ let file_name = entry.file_name().to_string_lossy().into_owned();
+ if let Some((task, os)) = parse_ok_name(&file_name) {
+ // Aggregate success file: one package name per line.
+ if let Ok(content) = std::fs::read_to_string(entry.path()) {
+ for package in content.lines().filter(|l| !l.is_empty()) {
logs.statuses
- .entry((task.clone(), package.clone()))
+ .entry((task.clone(), package.to_string()))
.or_default()
- .insert(os.clone(), false);
- let err = std::fs::read_to_string(file.path()).unwrap_or_default();
- logs.err_outputs
- .insert((task.clone(), os.clone(), package), strip_ansi(&err));
+ .insert(os.clone(), true);
}
}
+ } else if let Some((task, os, package)) = parse_err_name(&file_name) {
+ logs.statuses
+ .entry((task.clone(), package.clone()))
+ .or_default()
+ .insert(os.clone(), false);
+ let err = std::fs::read_to_string(entry.path()).unwrap_or_default();
+ logs.err_outputs
+ .insert((task, os, package), strip_ansi(&err));
}
}
}
@@ -83,6 +63,28 @@ impl ResCollectLogs {
}
}
+/// Parses a `{task}.{os}.ok` file name.
+fn parse_ok_name(file_name: &str) -> Option<(String, String)> {
+ let name = file_name.strip_suffix(".ok")?;
+ let mut parts = name.rsplitn(2, '.');
+ let os = parts.next()?.to_string();
+ let task = parts.next()?.to_string();
+ Some((task, os))
+}
+
+/// Parses a `{task}.{os}.{package}.err` file name.
+///
+/// Split from the right: package names cannot contain dots (cargo forbids
+/// them), while task names may.
+fn parse_err_name(file_name: &str) -> Option<(String, String, String)> {
+ let name = file_name.strip_suffix(".err")?;
+ let mut parts = name.rsplitn(3, '.');
+ let package = parts.next()?.to_string();
+ let os = parts.next()?.to_string();
+ let task = parts.next()?.to_string();
+ Some((task, os, package))
+}
+
#[program_setup]
pub fn report_setup(p: &mut Program<ThisProgram>) {
p.with_resource(ResCollectLogs::read());