diff options
| author | Weicao-CatilGrass <1992414357@qq.com> | 2026-05-09 14:31:42 +0800 |
|---|---|---|
| committer | Weicao-CatilGrass <1992414357@qq.com> | 2026-05-09 14:51:14 +0800 |
| commit | f5cdf5cc7c3bd434ff7a88c73b33f96c4d3b6562 (patch) | |
| tree | cb62ded889ad8cd0eafb43c94e2085a0416d22b2 /dev_tools | |
| parent | c0a29ccd2cd56e75c2b422b3cd9ca65d76a554da (diff) | |
Add CI tooling and cargo alias `ci`
Diffstat (limited to 'dev_tools')
| -rw-r--r-- | dev_tools/Cargo.lock | 25 | ||||
| -rw-r--r-- | dev_tools/Cargo.toml | 1 | ||||
| -rw-r--r-- | dev_tools/src/bin/ci.rs | 95 | ||||
| -rw-r--r-- | dev_tools/src/bin/docs-code-box-fix.rs | 13 | ||||
| -rw-r--r-- | dev_tools/src/bin/docsify-sidebar-gen.rs | 8 | ||||
| -rw-r--r-- | dev_tools/src/bin/refresh-docs.rs | 30 | ||||
| -rw-r--r-- | dev_tools/src/lib.rs | 102 |
7 files changed, 261 insertions, 13 deletions
diff --git a/dev_tools/Cargo.lock b/dev_tools/Cargo.lock index 39a1521..7bcc602 100644 --- a/dev_tools/Cargo.lock +++ b/dev_tools/Cargo.lock @@ -3,6 +3,15 @@ version = 4 [[package]] +name = "colored" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" +dependencies = [ + "windows-sys", +] + +[[package]] name = "just_fmt" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -21,6 +30,22 @@ dependencies = [ name = "tools" version = "0.1.0" dependencies = [ + "colored", "just_fmt", "just_template", ] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/dev_tools/Cargo.toml b/dev_tools/Cargo.toml index a28b156..7abb157 100644 --- a/dev_tools/Cargo.toml +++ b/dev_tools/Cargo.toml @@ -6,3 +6,4 @@ edition = "2024" [dependencies] just_template = "0.1.3" just_fmt = "0.1.2" +colored = "3.1.1" diff --git a/dev_tools/src/bin/ci.rs b/dev_tools/src/bin/ci.rs new file mode 100644 index 0000000..d5a108e --- /dev/null +++ b/dev_tools/src/bin/ci.rs @@ -0,0 +1,95 @@ +use std::process::exit; + +use tools::{cargo_tomls, eprintln_cargo_style, println_cargo_style, run_cmd}; + +fn main() { + #[cfg(windows)] + let _ = colored::control::set_virtual_terminal(true); + println!("{}", include_str!("../../../docs/res/ci_banner.txt")); + + let needs_commit_temp = !{ run_cmd!("git diff-index --quiet HEAD --").is_ok() }; + + if needs_commit_temp { + run_cmd!("git add .").unwrap(); + run_cmd!("git commit -m \"CI Temp\"").unwrap(); + } + + if ci().is_ok() { + println_cargo_style!("Done: All check passed!") + } + + let is_worktree_clean = run_cmd!("git diff-index --quiet HEAD --").is_ok(); + if !is_worktree_clean { + eprintln_cargo_style!("Documents needs refresh!"); + if needs_commit_temp { + run_cmd!("git restore .").unwrap(); + run_cmd!("git reset --soft HEAD~1").unwrap(); + } + exit(1) + } + + if needs_commit_temp { + run_cmd!("git restore .").unwrap(); + run_cmd!("git reset --soft HEAD~1").unwrap(); + } +} + +fn ci() -> Result<(), i32> { + build_all()?; + clippy_all()?; + test_all()?; + docs_refresh()?; + + run_cmd!("git add --renormalize .")?; + + Ok(()) +} + +fn build_all() -> Result<(), i32> { + let cargo_tomls = cargo_tomls(); + for cargo_toml in cargo_tomls { + println_cargo_style!("Build: {}", cargo_toml.to_string_lossy()); + run_cmd!( + "cargo check --manifest-path {}", + cargo_toml.to_string_lossy() + )?; + } + + Ok(()) +} + +fn clippy_all() -> Result<(), i32> { + let cargo_tomls = cargo_tomls(); + for cargo_toml in cargo_tomls { + println_cargo_style!("Clippy: {}", cargo_toml.to_string_lossy()); + run_cmd!( + "cargo clippy --manifest-path {} -- -D warnings", + cargo_toml.to_string_lossy() + )?; + } + + Ok(()) +} + +fn test_all() -> Result<(), i32> { + let cargo_tomls = cargo_tomls(); + for cargo_toml in cargo_tomls { + println_cargo_style!("Testing: {}", cargo_toml.to_string_lossy()); + run_cmd!( + "cargo test --manifest-path {}", + cargo_toml.to_string_lossy() + )?; + } + + Ok(()) +} + +fn docs_refresh() -> Result<(), i32> { + println_cargo_style!("Refresh: document at `./docs/`"); + + run_cmd!("cargo run --manifest-path dev_tools/Cargo.toml --bin docs-code-box-fix")?; + run_cmd!("cargo run --manifest-path dev_tools/Cargo.toml --bin docsify-sidebar-gen")?; + run_cmd!("cargo run --manifest-path dev_tools/Cargo.toml --bin refresh-docs")?; + + Ok(()) +} diff --git a/dev_tools/src/bin/docs-code-box-fix.rs b/dev_tools/src/bin/docs-code-box-fix.rs index db97592..21d2cce 100644 --- a/dev_tools/src/bin/docs-code-box-fix.rs +++ b/dev_tools/src/bin/docs-code-box-fix.rs @@ -1,6 +1,8 @@ use std::fs; use std::path::Path; +use tools::println_cargo_style; + /// Docsify code blocks require that blank lines before and after code blocks are not completely empty, /// but must contain at least one space, otherwise code block rendering will have issues. /// @@ -9,7 +11,7 @@ use std::path::Path; const DOCS_DIR: &str = "./docs"; fn main() { - println!("Fixing code box empty lines in docs/**/*.md ..."); + println_cargo_style!("Fixing: code box empty lines in docs/**/*.md ..."); let repo_root = find_git_repo().expect("Cannot find git repo root"); let docs_dir = repo_root.join(DOCS_DIR); @@ -32,15 +34,16 @@ fn main() { let new_content = fix_code_box_empty_lines(&content); if new_content != content { fs::write(path, &new_content).unwrap(); - println!(" Fixed: {}", path.display()); + println_cargo_style!("Fixed: {}", path.display()); fixed_count += 1; } file_count += 1; }); - println!( - "Done. Scanned {} files, fixed {} files.", - file_count, fixed_count + println_cargo_style!( + "Done: Scanned {} files, fixed {} files.", + file_count, + fixed_count ); } diff --git a/dev_tools/src/bin/docsify-sidebar-gen.rs b/dev_tools/src/bin/docsify-sidebar-gen.rs index ed9e9f0..e0f9370 100644 --- a/dev_tools/src/bin/docsify-sidebar-gen.rs +++ b/dev_tools/src/bin/docsify-sidebar-gen.rs @@ -1,13 +1,15 @@ use std::collections::BTreeMap; use std::path::Path; +use tools::println_cargo_style; + const PAGES_ROOT: &str = "./docs/pages"; const SIDEBAR_PATH: &str = "./docs/_sidebar.md"; const SIDEBAR_HEAD: &str = "- [Welcome!](README)\n"; fn main() { - println!("Refreshing _sidebar.md"); + println_cargo_style!("Refresh: _sidebar.md"); gen_sidebar(); gen_translation_sidebars(); } @@ -21,7 +23,7 @@ fn gen_sidebar() { let sidebar_path = repo_root.join(SIDEBAR_PATH); std::fs::write(&sidebar_path, lines).unwrap(); - println!(" Generated: {}", sidebar_path.display()); + println_cargo_style!("Generated: {}", sidebar_path.display()); } /// Generate _sidebar.md inside translation directories @@ -48,7 +50,7 @@ fn gen_translation_sidebars() { let sidebar_path = path.join("_sidebar.md"); std::fs::write(&sidebar_path, lines).unwrap(); - println!(" Generated: {}", sidebar_path.display()); + println_cargo_style!("Generated: {}", sidebar_path.display()); } } } diff --git a/dev_tools/src/bin/refresh-docs.rs b/dev_tools/src/bin/refresh-docs.rs index 32821ed..ffa80a2 100644 --- a/dev_tools/src/bin/refresh-docs.rs +++ b/dev_tools/src/bin/refresh-docs.rs @@ -2,6 +2,7 @@ use std::path::Path; use just_fmt::snake_case; use just_template::{Template, tmpl}; +use tools::println_cargo_style; const EXAMPLE_ROOT: &str = "./examples/"; const OUTPUT_PATH: &str = "./mingling/src/example_docs.rs"; @@ -9,10 +10,7 @@ const OUTPUT_PATH: &str = "./mingling/src/example_docs.rs"; const TEMPLATE_CONTENT: &str = include_str!("../../../mingling/src/example_docs.rs.tmpl"); fn main() { - { - println!("Refreshing Examples"); - gen_example_doc_module(); - } + gen_example_doc_module(); } fn gen_example_doc_module() { @@ -32,6 +30,8 @@ fn gen_example_doc_module() { } } + examples.sort(); + for example in examples { tmpl!(template += { examples { @@ -43,7 +43,7 @@ fn gen_example_doc_module() { ) } }); - println!(" Refresh: {}", example.name); + println_cargo_style!("Refresh: {}", example.name); } let template_str = template.to_string(); @@ -63,6 +63,26 @@ struct ExampleContent { cargo_toml: String, } +impl PartialOrd for ExampleContent { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for ExampleContent { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +impl PartialEq for ExampleContent { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl Eq for ExampleContent {} + impl ExampleContent { pub fn read(name: &str) -> Self { let repo = find_git_repo().unwrap(); diff --git a/dev_tools/src/lib.rs b/dev_tools/src/lib.rs index 8b13789..1b5dd0f 100644 --- a/dev_tools/src/lib.rs +++ b/dev_tools/src/lib.rs @@ -1 +1,103 @@ +use colored::Colorize; +#[macro_export] +macro_rules! run_cmd { + ($fmt:literal, $($arg:tt)*) => { + $crate::run_cmd(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::run_cmd($cmd) + }; +} + +#[macro_export] +macro_rules! println_cargo_style { + ($fmt:literal, $($arg:tt)*) => { + $crate::println_cargo_style(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::println_cargo_style($cmd) + }; +} + +#[macro_export] +macro_rules! eprintln_cargo_style { + ($fmt:literal, $($arg:tt)*) => { + $crate::eprintln_cargo_style(format!($fmt, $($arg)*)) + }; + ($cmd:expr) => { + $crate::eprintln_cargo_style($cmd) + }; +} + +pub fn println_cargo_style(str: impl Into<String>) { + let s = str.into(); + let (prefix, content) = if let Some(pos) = s.find(':') { + ( + s[..pos].trim().to_string(), + s[pos + 1..].trim_start().to_string(), + ) + } else { + ("".to_string(), s.trim().to_string()) + }; + + if prefix.len() > 12 { + panic!( + "prefix length exceeds 12: '{}' has length {}", + prefix, + prefix.len() + ); + } + + let padding = " ".repeat(12 - prefix.len()); + + println!( + "{}{} {}", + padding, + prefix.bold().bright_green(), + content.trim() + ); +} + +pub fn eprintln_cargo_style(str: impl Into<String>) { + println!("{}: {}", "error".bold().bright_red(), str.into()); +} + +pub fn run_cmd(cmd: impl Into<String>) -> Result<(), i32> { + let shell = if cfg!(target_os = "windows") { + "powershell" + } else { + "sh" + }; + let status = std::process::Command::new(shell) + .arg("-c") + .arg(cmd.into()) + .current_dir(std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."))) + .status() + .expect("failed to execute command"); + + let exit_code = status.code().unwrap_or(1); + if exit_code == 0 { + Ok(()) + } else { + Err(exit_code) + } +} + +pub fn cargo_tomls() -> Vec<std::path::PathBuf> { + let mut cargo_tomls = Vec::new(); + let mut dirs = vec![std::path::PathBuf::from(".")]; + while let Some(dir) = dirs.pop() { + if let Ok(entries) = std::fs::read_dir(&dir) { + for entry in entries.flatten() { + let path = entry.path(); + if path.is_dir() { + dirs.push(path); + } else if path.file_name().and_then(|n| n.to_str()) == Some("Cargo.toml") { + cargo_tomls.push(path); + } + } + } + } + cargo_tomls +} |
