Add clap to quire-ci with validate subcommand
Promote clap to a workspace dependency and consolidate
quire-server onto it. quire-ci gets a `quire validate` subcommand
that compiles and validates a ci.fnl pipeline.
Assisted-by: GLM-5.1 via pi
diff --git a/Cargo.lock b/Cargo.lock
index 837024c..3facc65 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2127,6 +2127,7 @@ dependencies = [
name = "quire-ci"
version = "0.1.0"
dependencies = [
+ "clap",
"fs-err",
"miette",
"quire-core",
diff --git a/Cargo.toml b/Cargo.toml
index d35d962..ba1c458 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ members = ["quire-ci", "quire-core", "quire-server"]
resolver = "3"
[workspace.dependencies]
+clap = { version = "*", features = ["derive", "env"] }
fs-err = "*"
jiff = { version = "*", features = ["serde"] }
miette = "*"
diff --git a/quire-ci/Cargo.toml b/quire-ci/Cargo.toml
index 0b1e2a8..a1970c5 100644
--- a/quire-ci/Cargo.toml
+++ b/quire-ci/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
+clap = { workspace = true }
fs-err = { workspace = true }
miette = { workspace = true, features = ["fancy"] }
quire-core = { path = "../quire-core" }
diff --git a/quire-ci/src/main.rs b/quire-ci/src/main.rs
index 2d8a313..2f9744d 100644
--- a/quire-ci/src/main.rs
+++ b/quire-ci/src/main.rs
@@ -1,34 +1,39 @@
use std::path::PathBuf;
-use std::process::ExitCode;
+use clap::Parser;
use miette::IntoDiagnostic;
-use quire_core::ci::pipeline::CompileError;
-fn main() -> ExitCode {
+/// Validate a quire CI pipeline.
+#[derive(Parser)]
+#[command(version, propagate_version = true)]
+struct Cli {
+ #[command(subcommand)]
+ command: Commands,
+}
+
+#[derive(clap::Subcommand)]
+enum Commands {
+ /// Compile and validate a ci.fnl pipeline.
+ Validate {
+ /// Workspace root containing .quire/ci.fnl. Defaults to cwd.
+ #[arg(short, long, default_value = ".")]
+ workspace: PathBuf,
+ },
+}
+
+fn main() -> miette::Result<()> {
miette::set_panic_hook();
- match run() {
- Ok(()) => ExitCode::SUCCESS,
- Err(e) => {
- eprintln!("{e:?}");
- ExitCode::FAILURE
- }
- }
+ run()
}
fn run() -> miette::Result<()> {
- let path = find_ci_fnl()?;
+ let cli = Cli::parse();
+ let Commands::Validate { workspace } = cli.command;
+ let path = workspace.join(".quire").join("ci.fnl");
let source = fs_err::read_to_string(&path).into_diagnostic()?;
- let pipeline = match quire_core::ci::pipeline::compile(&source, &path.display().to_string()) {
- Ok(p) => p,
- Err(CompileError::Fennel(err)) => {
- return Err(miette::Report::new_boxed(err));
- }
- Err(CompileError::Pipeline(err)) => {
- return Err(miette::Report::new_boxed(err));
- }
- };
+ let pipeline = quire_core::ci::pipeline::compile(&source, &path.display().to_string())?;
let jobs = pipeline.jobs();
if jobs.is_empty() {
@@ -51,18 +56,3 @@ fn run() -> miette::Result<()> {
println!("\nAll validations passed.");
Ok(())
}
-
-/// Walk from cwd upward to find `.quire/ci.fnl`.
-fn find_ci_fnl() -> miette::Result<PathBuf> {
- let cwd = std::env::current_dir().into_diagnostic()?;
- let mut dir = cwd.as_path();
- loop {
- let candidate = dir.join(".quire").join("ci.fnl");
- if candidate.is_file() {
- return Ok(candidate);
- }
- dir = dir
- .parent()
- .ok_or_else(|| miette::miette!("no .quire/ci.fnl found in any parent directory"))?;
- }
-}
diff --git a/quire-server/Cargo.toml b/quire-server/Cargo.toml
index 50ad291..49709e6 100644
--- a/quire-server/Cargo.toml
+++ b/quire-server/Cargo.toml
@@ -32,7 +32,7 @@ tracing = { workspace = true }
askama = "*"
axum = "*"
-clap = { version = "*", features = ["derive", "env"] }
+clap = { workspace = true }
clap_complete = "*"
rusqlite = { version = "*", features = ["bundled"] }
rusqlite_migration = "*"