chore(workspace): add cargo xtask crate for common ops

This commit is contained in:
2025-10-17 00:47:54 +02:00
parent 85ae319690
commit 9c0cf274a3
3 changed files with 172 additions and 0 deletions

View File

@@ -11,6 +11,7 @@ members = [
"crates/mcp/code-server",
"crates/mcp/prompt-server",
"crates/owlen-markdown",
"xtask",
]
exclude = []

9
xtask/Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "xtask"
version = "0.1.0"
edition.workspace = true
publish = false
[dependencies]
anyhow = { workspace = true }
clap = { workspace = true, features = ["derive"] }

162
xtask/src/main.rs Normal file
View File

@@ -0,0 +1,162 @@
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, Result, bail};
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(author, version, about = "Owlen developer tasks", long_about = None)]
struct Xtask {
#[command(subcommand)]
command: Task,
}
#[derive(Subcommand)]
enum Task {
/// Format the workspace (use --check to verify without writing).
Fmt {
#[arg(long, help = "Run rustfmt in check mode")]
check: bool,
},
/// Run clippy with all warnings elevated to errors.
Lint,
/// Execute the full workspace test suite.
Test,
/// Run coverage via cargo-llvm-cov (requires the tool to be installed).
Coverage,
/// Launch the default Owlen CLI binary (owlen) with optional args.
DevRun {
#[arg(last = true, help = "Arguments forwarded to `owlen`")]
args: Vec<String>,
},
/// Composite release validation (fmt --check, clippy, test).
ReleaseCheck,
/// Regenerate docs/repo-map.md (accepts optional output path).
GenRepoMap {
#[arg(long, value_name = "PATH", help = "Override the repo map output path")]
output: Option<PathBuf>,
},
}
fn main() -> Result<()> {
let cli = Xtask::parse();
match cli.command {
Task::Fmt { check } => fmt(check),
Task::Lint => lint(),
Task::Test => test(),
Task::Coverage => coverage(),
Task::DevRun { args } => dev_run(args),
Task::ReleaseCheck => release_check(),
Task::GenRepoMap { output } => gen_repo_map(output),
}
}
fn fmt(check: bool) -> Result<()> {
let mut args = vec!["fmt".to_string(), "--all".to_string()];
if check {
args.push("--".to_string());
args.push("--check".to_string());
}
run_cargo(args)
}
fn lint() -> Result<()> {
run_cargo(vec![
"clippy".into(),
"--workspace".into(),
"--all-features".into(),
"--".into(),
"-D".into(),
"warnings".into(),
])
}
fn test() -> Result<()> {
run_cargo(vec![
"test".into(),
"--workspace".into(),
"--all-features".into(),
])
}
fn coverage() -> Result<()> {
run_cargo(vec![
"llvm-cov".into(),
"--workspace".into(),
"--all-features".into(),
"--summary-only".into(),
])
.with_context(|| "install `cargo llvm-cov` to use the coverage task".to_string())
}
fn dev_run(args: Vec<String>) -> Result<()> {
let mut command_args = vec![
"run".into(),
"-p".into(),
"owlen-cli".into(),
"--bin".into(),
"owlen".into(),
];
if !args.is_empty() {
command_args.push("--".into());
command_args.extend(args);
}
run_cargo(command_args)
}
fn release_check() -> Result<()> {
fmt(true)?;
lint()?;
test()?;
Ok(())
}
fn gen_repo_map(output: Option<PathBuf>) -> Result<()> {
let script = workspace_root().join("scripts/gen-repo-map.sh");
if !script.exists() {
bail!("repo map script not found at {}", script.display());
}
let mut cmd = Command::new(&script);
cmd.current_dir(workspace_root());
if let Some(path) = output {
cmd.arg(path);
}
let status = cmd
.status()
.with_context(|| format!("failed to run {}", script.display()))?;
if !status.success() {
bail!(
"{} exited with status {}",
script.display(),
status.code().unwrap_or_default()
);
}
Ok(())
}
fn run_cargo(args: Vec<String>) -> Result<()> {
let mut cmd = Command::new("cargo");
cmd.current_dir(workspace_root());
cmd.args(&args);
let status = cmd
.status()
.with_context(|| format!("failed to run cargo {}", args.join(" ")))?;
if !status.success() {
bail!(
"`cargo {}` exited with status {}",
args.join(" "),
status.code().unwrap_or_default()
);
}
Ok(())
}
fn workspace_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("xtask has a parent directory")
.to_path_buf()
}