aboutsummaryrefslogtreecommitdiff
path: root/mingling_ci/src/reporter.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-18 08:27:36 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-18 08:27:36 +0800
commit5d0918ff5e19ebc67e4698bc308f6562779b7110 (patch)
treea9d7f5fb54afd16056b0a4c24d118db2d95f8d78 /mingling_ci/src/reporter.rs
parent00cf68abdd2be1cfca8b5a268d087ea2ccedf1dd (diff)
feat: add new Mingling CI system crate (WIP)
Add `mingling_ci` as a standalone CI system built on Mingling 0.4.0 to validate the next version, with report collection and manifest listing commands.
Diffstat (limited to 'mingling_ci/src/reporter.rs')
-rw-r--r--mingling_ci/src/reporter.rs108
1 files changed, 108 insertions, 0 deletions
diff --git a/mingling_ci/src/reporter.rs b/mingling_ci/src/reporter.rs
new file mode 100644
index 0000000..00ce3e5
--- /dev/null
+++ b/mingling_ci/src/reporter.rs
@@ -0,0 +1,108 @@
+//! Minimal log exporter for CI reports.
+//!
+//! Writes per-package results into `collect/{task}/{platform}/{package}.{ok|err}`
+//! so that the [`crate::cmd::collect_results`] command can assemble the final
+//! report. The task name is set once per CI phase via [`set_task`].
+
+use std::fs;
+use std::path::Path;
+use std::sync::Mutex;
+
+/// Root of the collected CI logs (relative to the repo root).
+pub const COLLECT_DIR: &str = "./.temp/reports/collect";
+
+/// The platform a package check ran on.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ReportPlatform {
+ Windows,
+ Linux,
+ MacOS,
+}
+
+impl ReportPlatform {
+ /// Directory name used under the task folder.
+ const fn dir_name(self) -> &'static str {
+ match self {
+ Self::Windows => "Windows",
+ Self::Linux => "Linux",
+ Self::MacOS => "MacOS",
+ }
+ }
+}
+
+/// The outcome of a package check.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ReportResult {
+ /// Check passed.
+ Ok,
+ /// Check failed, with the captured output.
+ Error(String),
+}
+
+/// Current task name (e.g. `Build-All`); set via [`set_task`].
+static CURRENT_TASK: Mutex<Option<String>> = Mutex::new(None);
+
+/// Sets the task that subsequent [`export`] calls write under.
+///
+/// # Panics
+///
+/// Panics if the internal mutex is poisoned.
+pub fn set_task(task: &str) {
+ *CURRENT_TASK.lock().unwrap() = Some(task.to_string());
+}
+
+/// Exports one package result to `collect/{task}/{platform}/{package}.{ok|err}`.
+///
+/// Writes `{package}.ok` on success and `{package}.err` (with the output) on
+/// failure. Errors are reported to stderr and otherwise ignored.
+///
+/// # Panics
+///
+/// Panics if the internal task mutex is poisoned.
+pub fn export(package: &str, platform: ReportPlatform, result: ReportResult) {
+ 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());
+ return;
+ }
+
+ let (file_name, content) = match result {
+ ReportResult::Ok => (format!("{package}.ok"), String::new()),
+ ReportResult::Error(output) => (format!("{package}.err"), output),
+ };
+ let path = dir.join(file_name);
+ if let Err(e) = fs::write(&path, content) {
+ eprintln!("reporter: failed to write {}: {e}", path.display());
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[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("Linux");
+ fs::remove_dir_all(&task_root).ok();
+
+ export("pkg-a", ReportPlatform::Linux, ReportResult::Ok);
+ export(
+ "pkg-b",
+ ReportPlatform::Linux,
+ ReportResult::Error("boom".to_string()),
+ );
+
+ assert!(dir.join("pkg-a.ok").is_file());
+ assert!(dir.join("pkg-b.err").is_file());
+ assert_eq!(fs::read_to_string(dir.join("pkg-b.err")).unwrap(), "boom");
+
+ fs::remove_dir_all(&task_root).ok();
+ }
+}