chore(workspace): add cargo xtask crate for common ops
This commit is contained in:
@@ -11,6 +11,7 @@ members = [
|
||||
"crates/mcp/code-server",
|
||||
"crates/mcp/prompt-server",
|
||||
"crates/owlen-markdown",
|
||||
"xtask",
|
||||
]
|
||||
exclude = []
|
||||
|
||||
|
||||
9
xtask/Cargo.toml
Normal file
9
xtask/Cargo.toml
Normal 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
162
xtask/src/main.rs
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user