diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-08-12 17:11:34 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-08-12 17:12:54 +0800 |
| commit | 2efec9d3924ce1d806b04e2d51c1cda285bf501d (patch) | |
| tree | 5490a4ab433f77ae0e078558028f808b1dc6087e /.run/src/bin | |
| parent | 9dad6b93e4e735d061db6b4349227d8f23b65116 (diff) | |
ci: Refactor CI workflows into single check matrix
Replace the separate code/docs CI jobs and local check flags with
a unified `--check-*` flag system, enabling each check to run
independently across a platform matrix.
Diffstat (limited to '.run/src/bin')
| -rw-r--r-- | .run/src/bin/ci.rs | 201 | ||||
| -rw-r--r-- | .run/src/bin/cov-test.rs | 21 | ||||
| -rw-r--r-- | .run/src/bin/docsify-sidebar-gen.rs | 14 | ||||
| -rw-r--r-- | .run/src/bin/refresh-feature-mod.rs | 2 |
4 files changed, 173 insertions, 65 deletions
diff --git a/.run/src/bin/ci.rs b/.run/src/bin/ci.rs index 39d55eb..c090eaa 100644 --- a/.run/src/bin/ci.rs +++ b/.run/src/bin/ci.rs @@ -11,17 +11,47 @@ fn get_ignore_dirs() -> Vec<String> { vec![".temp".to_string()] } +/// A single CI step, each individually toggleable via `--check-*`. +struct Checks { + build: bool, + clippy: bool, + test: bool, + arg_picker: bool, + markdown_code: bool, + examples: bool, + docs_refresh: bool, + api_docs: bool, +} + +impl Checks { + fn any(&self) -> bool { + self.build + || self.clippy + || self.test + || self.arg_picker + || self.markdown_code + || self.examples + || self.docs_refresh + || self.api_docs + } +} + fn print_help() { println!( r" Usage: ci [options] Options: - -h, --help Print this help message - -y Auto-confirm temporary commits - --dirty Run CI on dirty workspace (skip temp commit & clean check) - --refresh-docs Refresh documentation files - --test-docs Run documentation tests (build, clippy, test) - --test-codes Test examples and documentation code blocks + -h, --help Print this help message + -y Auto-confirm temporary commits + --dirty Run CI on dirty workspace (skip temp commit & clean check) + --check-build Build all crates + --check-clippy Run clippy on all crates (-D warnings) + --check-test Run unit tests for all crates + --check-arg-picker Test the arg-picker crate + --check-markdown-code Verify all *.md code blocks compile + --check-examples Test all examples + --check-docs-refresh Refresh docs and fail if the tree is contaminated + --check-api-docs Build API docs with docs.rs features If no specific options are given, all checks are run. " @@ -33,12 +63,29 @@ fn main() { let _ = colored::control::set_virtual_terminal(true); println!("{}", include_str!("../../../docs/res/ci_banner.txt")); - let (auto_yes, dirty, test_docs, refresh_docs, test_codes, help) = Picker::from_args() + let ( + auto_yes, + dirty, + check_build, + check_clippy, + check_test, + check_arg_picker, + check_markdown_code, + check_examples, + check_docs_refresh, + check_api_docs, + help, + ) = Picker::from_args() .pick_or_default(&arg![yes: bool, 'y']) .pick_or_default(&arg![dirty: bool]) - .pick_or_default(&arg![test_docs: bool]) - .pick_or_default(&arg![refresh_docs: bool]) - .pick_or_default(&arg![test_codes: bool]) + .pick_or_default(&arg![check_build: bool]) + .pick_or_default(&arg![check_clippy: bool]) + .pick_or_default(&arg![check_test: bool]) + .pick_or_default(&arg![check_arg_picker: bool]) + .pick_or_default(&arg![check_markdown_code: bool]) + .pick_or_default(&arg![check_examples: bool]) + .pick_or_default(&arg![check_docs_refresh: bool]) + .pick_or_default(&arg![check_api_docs: bool]) .pick_or_default(&arg![help: bool, 'h']) .unwrap(); @@ -47,8 +94,17 @@ fn main() { return; } - let any_specified = test_docs || refresh_docs || test_codes; - let run_all = !any_specified; + let checks = Checks { + build: check_build, + clippy: check_clippy, + test: check_test, + arg_picker: check_arg_picker, + markdown_code: check_markdown_code, + examples: check_examples, + docs_refresh: check_docs_refresh, + api_docs: check_api_docs, + }; + let run_all = !checks.any(); let needs_commit_temp = !dirty && !{ run_cmd!("git diff-index --quiet HEAD --").is_ok() }; @@ -72,7 +128,7 @@ fn main() { } } - if let Err(exit_code) = ci(test_docs, test_codes, run_all) { + if let Err(exit_code) = ci(&checks, run_all) { restore_workspace(needs_commit_temp).unwrap(); exit(exit_code) } @@ -109,47 +165,94 @@ fn restore_workspace(undo_commit: bool) -> Result<(), i32> { Ok(()) } -fn ci(test_docs: bool, test_codes: bool, run_all: bool) -> Result<(), i32> { - if run_all || test_codes { - println_cargo_style!("Phase: Scan and build all crates"); - build_all()?; - - println_cargo_style!("Phase: Run clippy for all crates"); - clippy_all()?; - - println_cargo_style!("Phase: Test all crates"); - test_all()?; - - println_cargo_style!("Phase: Test arg picker"); - test_arg_picker()?; - } - - if run_all || test_docs { - let mut exit_code = 0; - - println_cargo_style!("Phase: Verify all *.md document code blocks are compilable"); - if let Err(code) = test_docs_code_blocks() { - exit_code = exit_code.max(code); +/// Run one CI step. +/// +/// When `continue_on_error` is set (used for the documentation steps in +/// "run all" mode), a failing step is recorded and the remaining steps still +/// execute, so every problem is reported in a single run. +fn run_step( + exit_code: &mut i32, + phase: &str, + step: fn() -> Result<(), i32>, + continue_on_error: bool, +) -> Result<(), i32> { + println_cargo_style!(phase); + match step() { + Ok(()) => Ok(()), + Err(code) if continue_on_error => { + *exit_code = (*exit_code).max(code); + Ok(()) } + Err(code) => Err(code), + } +} - println_cargo_style!("Phase: Test all examples"); - if let Err(code) = test_examples() { - exit_code = exit_code.max(code); - } +fn ci(checks: &Checks, run_all: bool) -> Result<(), i32> { + let mut exit_code = 0; - println_cargo_style!("Phase: Check all documentation is up to date"); - if let Err(code) = docs_refresh() { - exit_code = exit_code.max(code); - } + if run_all || checks.build { + run_step( + &mut exit_code, + "Phase: Scan and build all crates", + build_all, + false, + )?; + } + if run_all || checks.clippy { + run_step( + &mut exit_code, + "Phase: Run clippy for all crates", + clippy_all, + false, + )?; + } + if run_all || checks.test { + run_step(&mut exit_code, "Phase: Test all crates", test_all, false)?; + } + if run_all || checks.arg_picker { + run_step( + &mut exit_code, + "Phase: Test arg picker", + test_arg_picker, + false, + )?; + } - println_cargo_style!("Phase: Try Build API docs"); - if let Err(code) = deploy_api_docs() { - exit_code = exit_code.max(code); - } + if run_all || checks.markdown_code { + run_step( + &mut exit_code, + "Phase: Verify all *.md document code blocks are compilable", + test_docs_code_blocks, + run_all, + )?; + } + if run_all || checks.examples { + run_step( + &mut exit_code, + "Phase: Test all examples", + test_examples, + run_all, + )?; + } + if run_all || checks.docs_refresh { + run_step( + &mut exit_code, + "Phase: Check all documentation is up to date", + docs_refresh, + run_all, + )?; + } + if run_all || checks.api_docs { + run_step( + &mut exit_code, + "Phase: Try Build API docs", + deploy_api_docs, + run_all, + )?; + } - if exit_code != 0 { - return Err(exit_code); - } + if exit_code != 0 { + return Err(exit_code); } run_cmd!("git add --renormalize .")?; diff --git a/.run/src/bin/cov-test.rs b/.run/src/bin/cov-test.rs index 1599ff1..1b2342e 100644 --- a/.run/src/bin/cov-test.rs +++ b/.run/src/bin/cov-test.rs @@ -98,7 +98,11 @@ fn main() { manifest.display() )) .unwrap_or_else(|code| { - eprintln_cargo_style!("test crate {} failed with exit code {}", manifest.display(), code); + eprintln_cargo_style!( + "test crate {} failed with exit code {}", + manifest.display(), + code + ); std::process::exit(code); }); } @@ -131,7 +135,11 @@ fn main() { example )) .unwrap_or_else(|code| { - eprintln_cargo_style!("build of example {} failed with exit code {}", example, code); + eprintln_cargo_style!( + "build of example {} failed with exit code {}", + example, + code + ); std::process::exit(code); }); } @@ -232,11 +240,7 @@ fn main() { // is generated, so the summary table is rewritten here. let index_path = output_path.join("index.html"); if let Err(e) = recolor_report_index(&index_path) { - eprintln_cargo_style!( - "Warning: failed to recolor {}: {}", - index_path.display(), - e - ); + eprintln_cargo_style!("Warning: failed to recolor {}: {}", index_path.display(), e); } println_cargo_style!( @@ -472,7 +476,8 @@ fn is_executable(path: &Path) -> bool { } #[cfg(not(unix))] { - path.extension().is_some_and(|e| e.eq_ignore_ascii_case("exe")) + path.extension() + .is_some_and(|e| e.eq_ignore_ascii_case("exe")) } } diff --git a/.run/src/bin/docsify-sidebar-gen.rs b/.run/src/bin/docsify-sidebar-gen.rs index 5beda7f..15ae184 100644 --- a/.run/src/bin/docsify-sidebar-gen.rs +++ b/.run/src/bin/docsify-sidebar-gen.rs @@ -67,10 +67,9 @@ fn find_content_dir(site_root: &Path) -> Option<PathBuf> { entries.sort_by_key(|e| e.path()); for entry in entries { let path = entry.path(); - if path.is_dir() - && has_markdown_files(&path) { - return Some(path); - } + if path.is_dir() && has_markdown_files(&path) { + return Some(path); + } } } @@ -255,8 +254,9 @@ fn natural_cmp(a: &str, b: &str) -> std::cmp::Ordering { fn extract_leading_number(link: &str) -> usize { if let Some(file_stem) = link.rsplit('/').next() && let Some(num_end) = file_stem.find('-') - && let Ok(num) = file_stem[..num_end].parse::<usize>() { - return num; - } + && let Ok(num) = file_stem[..num_end].parse::<usize>() + { + return num; + } usize::MAX } diff --git a/.run/src/bin/refresh-feature-mod.rs b/.run/src/bin/refresh-feature-mod.rs index 2255dbc..4cd6532 100644 --- a/.run/src/bin/refresh-feature-mod.rs +++ b/.run/src/bin/refresh-feature-mod.rs @@ -2,7 +2,7 @@ use std::collections::BTreeSet; use std::path::Path; use just_fmt::snake_case; -use just_template::{tmpl, Template}; +use just_template::{Template, tmpl}; use tools::println_cargo_style; const CARGO_TOML_PATH: &str = "./mingling/Cargo.toml"; |
