aboutsummaryrefslogtreecommitdiff
path: root/mingling_ci/src
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_ci/src')
-rw-r--r--mingling_ci/src/task.rs1
-rw-r--r--mingling_ci/src/task/cmd_build.rs81
-rw-r--r--mingling_ci/src/task/cmd_clippy.rs86
-rw-r--r--mingling_ci/src/task/run.rs75
4 files changed, 113 insertions, 130 deletions
diff --git a/mingling_ci/src/task.rs b/mingling_ci/src/task.rs
index 1d389b0..07ffb3f 100644
--- a/mingling_ci/src/task.rs
+++ b/mingling_ci/src/task.rs
@@ -1,2 +1,3 @@
pub(crate) mod cmd_build;
pub(crate) mod cmd_clippy;
+pub(crate) mod run;
diff --git a/mingling_ci/src/task/cmd_build.rs b/mingling_ci/src/task/cmd_build.rs
index a72ab0b..44be592 100644
--- a/mingling_ci/src/task/cmd_build.rs
+++ b/mingling_ci/src/task/cmd_build.rs
@@ -1,79 +1,29 @@
+use std::ffi::OsString;
use std::path::Path;
-use just_progress::progress::{self, ProgressInfo};
use mingling::{
Grouped, Routable,
- macros::{buffer, command, r_println, renderer},
+ macros::{buffer, command, renderer},
+ res::ResExitCode,
};
use crate::Next;
-use crate::reporter::{self, ReportResult};
use crate::res::Manifests;
+use crate::task::run::run_parallel_checks;
#[command(node = "build-all")]
pub async fn build_all(manifests: &Manifests) -> Next {
- const TASK: &str = "Build-All";
-
- reporter::set_task(TASK);
-
- let total = manifests.package_dirs.len();
- progress::update(TASK, 0.0, ProgressInfo::Info("Building"));
-
- // Run one `cargo build` per manifest in parallel.
- let mut set = tokio::task::JoinSet::new();
- for (name, path) in &manifests.package_dirs {
- let (name, path) = (name.clone(), path.clone());
- set.spawn(async move { (name, run_cargo_build(&path).await) });
- }
-
- // Collect all outcomes first, then dump the report files in one round.
- let mut results: Vec<(String, ReportResult)> = Vec::new();
- let mut done = 0;
- while let Some(joined) = set.join_next().await {
- done += 1;
- let Ok((name, (ok, output))) = joined else {
- continue;
- };
- // The count is small, so the `usize -> f32` cast cannot lose precision.
- #[allow(clippy::cast_precision_loss)]
- let overall = done as f32 / total as f32;
- progress::update(TASK, overall, ProgressInfo::Info("Building"));
- results.push((
- name,
- if ok {
- ReportResult::Ok
- } else {
- ReportResult::Error(output)
- },
- ));
- }
-
- let fail_count = results
- .iter()
- .filter(|(_, r)| matches!(r, ReportResult::Error(_)))
- .count();
- for (name, result) in results {
- reporter::export(&name, result);
- }
-
+ let fail_count = run_parallel_checks("Build-All", "Building", build_args, manifests).await;
ResultBuildAll { fail_count }.to_chain()
}
-/// Runs `cargo build --manifest-path <path>`, returning success and output.
-async fn run_cargo_build(path: &Path) -> (bool, String) {
- let output = tokio::process::Command::new("cargo")
- .args(["build", "--manifest-path"])
- .arg(path)
- .output()
- .await;
- match output {
- Ok(output) => {
- let mut log = String::from_utf8_lossy(&output.stdout).into_owned();
- log.push_str(&String::from_utf8_lossy(&output.stderr));
- (output.status.success(), log)
- }
- Err(e) => (false, format!("failed to run cargo: {e}")),
- }
+/// `cargo build --manifest-path <path>`
+fn build_args(path: &Path) -> Vec<OsString> {
+ vec![
+ "build".into(),
+ "--manifest-path".into(),
+ path.as_os_str().to_os_string(),
+ ]
}
/// Number of packages that failed to build.
@@ -82,7 +32,10 @@ pub struct ResultBuildAll {
pub fail_count: usize,
}
+/// Silently sets a non-zero exit code when any build failed.
#[renderer(buffer)]
-pub fn render_build_all(r: ResultBuildAll) {
- r_println!("Build-All: {} package(s) failed", r.fail_count);
+pub fn render_build_all(r: ResultBuildAll, exit_code: &mut ResExitCode) {
+ if r.fail_count > 0 {
+ exit_code.exit_code = 1;
+ }
}
diff --git a/mingling_ci/src/task/cmd_clippy.rs b/mingling_ci/src/task/cmd_clippy.rs
index 8ec4236..11227a2 100644
--- a/mingling_ci/src/task/cmd_clippy.rs
+++ b/mingling_ci/src/task/cmd_clippy.rs
@@ -1,81 +1,32 @@
+use std::ffi::OsString;
use std::path::Path;
-use just_progress::progress::{self, ProgressInfo};
use mingling::{
Grouped, Routable,
- macros::{buffer, command, r_println, renderer},
+ macros::{buffer, command, renderer},
+ res::ResExitCode,
};
use crate::Next;
-use crate::reporter::{self, ReportResult};
use crate::res::Manifests;
+use crate::task::run::run_parallel_checks;
#[command(node = "clippy-all")]
pub async fn clippy_all(manifests: &Manifests) -> Next {
- const TASK: &str = "Clippy-All";
-
- reporter::set_task(TASK);
-
- let total = manifests.package_dirs.len();
- progress::update(TASK, 0.0, ProgressInfo::Info("Clippy"));
-
- // Run one `cargo clippy` per manifest in parallel.
- let mut set = tokio::task::JoinSet::new();
- for (name, path) in &manifests.package_dirs {
- let (name, path) = (name.clone(), path.clone());
- set.spawn(async move { (name, run_cargo_clippy(&path).await) });
- }
-
- // Collect all outcomes first, then dump the report files in one round.
- let mut results: Vec<(String, ReportResult)> = Vec::new();
- let mut done = 0;
- while let Some(joined) = set.join_next().await {
- done += 1;
- let Ok((name, (ok, output))) = joined else {
- continue;
- };
- // The count is small, so the `usize -> f32` cast cannot lose precision.
- #[allow(clippy::cast_precision_loss)]
- let overall = done as f32 / total as f32;
- progress::update(TASK, overall, ProgressInfo::Info("Clippy"));
- results.push((
- name,
- if ok {
- ReportResult::Ok
- } else {
- ReportResult::Error(output)
- },
- ));
- }
-
- let fail_count = results
- .iter()
- .filter(|(_, r)| matches!(r, ReportResult::Error(_)))
- .count();
- for (name, result) in results {
- reporter::export(&name, result);
- }
-
+ let fail_count = run_parallel_checks("Clippy-All", "Clippy", clippy_args, manifests).await;
ResultClippyAll { fail_count }.to_chain()
}
-/// Runs `cargo clippy --manifest-path <path> -- -D warnings`, returning success
-/// and output.
-async fn run_cargo_clippy(path: &Path) -> (bool, String) {
- let output = tokio::process::Command::new("cargo")
- .args(["clippy", "--manifest-path"])
- .arg(path)
- .args(["--", "-D", "warnings"])
- .output()
- .await;
- match output {
- Ok(output) => {
- let mut log = String::from_utf8_lossy(&output.stdout).into_owned();
- log.push_str(&String::from_utf8_lossy(&output.stderr));
- (output.status.success(), log)
- }
- Err(e) => (false, format!("failed to run cargo: {e}")),
- }
+/// `cargo clippy --manifest-path <path> -- -D warnings`
+fn clippy_args(path: &Path) -> Vec<OsString> {
+ vec![
+ "clippy".into(),
+ "--manifest-path".into(),
+ path.as_os_str().to_os_string(),
+ "--".into(),
+ "-D".into(),
+ "warnings".into(),
+ ]
}
/// Number of packages that failed clippy.
@@ -84,7 +35,10 @@ pub struct ResultClippyAll {
pub fail_count: usize,
}
+/// Silently sets a non-zero exit code when any clippy check failed.
#[renderer(buffer)]
-pub fn render_clippy_all(r: ResultClippyAll) {
- r_println!("Clippy-All: {} package(s) failed", r.fail_count);
+pub fn render_clippy_all(r: ResultClippyAll, exit_code: &mut ResExitCode) {
+ if r.fail_count > 0 {
+ exit_code.exit_code = 1;
+ }
}
diff --git a/mingling_ci/src/task/run.rs b/mingling_ci/src/task/run.rs
new file mode 100644
index 0000000..1017677
--- /dev/null
+++ b/mingling_ci/src/task/run.rs
@@ -0,0 +1,75 @@
+use std::ffi::OsString;
+use std::path::Path;
+
+use just_progress::progress::{self, ProgressInfo};
+
+use crate::reporter::{self, ReportResult};
+use crate::res::Manifests;
+
+/// Runs one `cargo` subcommand per manifest in parallel, reporting each
+/// outcome via `reporter` after the whole round finishes.
+///
+/// Only output is the progress bar; returns the number of failing packages.
+#[allow(clippy::cast_precision_loss)] // counts are small
+pub(crate) async fn run_parallel_checks(
+ task: &str,
+ status: &'static str,
+ args_for: fn(&Path) -> Vec<OsString>,
+ manifests: &Manifests,
+) -> usize {
+ reporter::set_task(task);
+ let total = manifests.package_dirs.len();
+ progress::update(task, 0.0, ProgressInfo::Info(status));
+
+ // Run one cargo invocation per manifest in parallel.
+ let mut set = tokio::task::JoinSet::new();
+ for (name, path) in &manifests.package_dirs {
+ let (name, path) = (name.clone(), path.clone());
+ let args = args_for(&path);
+ set.spawn(async move { (name, run_cargo(args).await) });
+ }
+
+ // Collect all outcomes first, then dump the report files in one round.
+ let mut results: Vec<(String, ReportResult)> = Vec::new();
+ let mut done = 0;
+ while let Some(joined) = set.join_next().await {
+ done += 1;
+ let Ok((name, (ok, output))) = joined else {
+ continue;
+ };
+ progress::update(task, done as f32 / total as f32, ProgressInfo::Info(status));
+ results.push((
+ name,
+ if ok {
+ ReportResult::Ok
+ } else {
+ ReportResult::Error(output)
+ },
+ ));
+ }
+
+ let fail_count = results
+ .iter()
+ .filter(|(_, r)| matches!(r, ReportResult::Error(_)))
+ .count();
+ for (name, result) in results {
+ reporter::export(&name, result);
+ }
+ fail_count
+}
+
+/// Runs a `cargo` subcommand, returning success and captured output.
+async fn run_cargo(args: Vec<OsString>) -> (bool, String) {
+ let output = tokio::process::Command::new("cargo")
+ .args(args)
+ .output()
+ .await;
+ match output {
+ Ok(output) => {
+ let mut log = String::from_utf8_lossy(&output.stdout).into_owned();
+ log.push_str(&String::from_utf8_lossy(&output.stderr));
+ (output.status.success(), log)
+ }
+ Err(e) => (false, format!("failed to run cargo: {e}")),
+ }
+}