aboutsummaryrefslogtreecommitdiff
path: root/.run
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-12 17:11:34 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-12 17:12:54 +0800
commit2efec9d3924ce1d806b04e2d51c1cda285bf501d (patch)
tree5490a4ab433f77ae0e078558028f808b1dc6087e /.run
parent9dad6b93e4e735d061db6b4349227d8f23b65116 (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')
-rw-r--r--.run/src/bin/ci.rs201
-rw-r--r--.run/src/bin/cov-test.rs21
-rw-r--r--.run/src/bin/docsify-sidebar-gen.rs14
-rw-r--r--.run/src/bin/refresh-feature-mod.rs2
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";