summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHermes Agent <hermes@localhost>2026-08-12 00:03:21 +0000
committerHermes Agent <hermes@localhost>2026-08-12 00:03:21 +0000
commit191e2b7429ff4547b5fdd0fbfcff943ce96c2d68 (patch)
treea7f989330ac0d2f2de13f67d4c897943391c5a55
parent25f39c857da591c665865b77483b79495caf5e00 (diff)
Add explicit image builder CLI commands
-rw-r--r--src/cli.rs58
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs9
-rw-r--r--tests/cli.rs71
4 files changed, 137 insertions, 2 deletions
diff --git a/src/cli.rs b/src/cli.rs
new file mode 100644
index 0000000..de9404a
--- /dev/null
+++ b/src/cli.rs
@@ -0,0 +1,58 @@
+use std::path::PathBuf;
+
+use anyhow::{bail, Result};
+use clap::{Parser, Subcommand};
+
+use crate::{BuildPlan, ImageSpec};
+
+#[derive(Debug, Parser)]
+#[command(name = "alt-controller-image", about = "Native ALT image builder")]
+pub struct Cli {
+ #[command(subcommand)]
+ pub command: Command,
+}
+
+#[derive(Debug, Subcommand)]
+pub enum Command {
+ /// Validate an image specification and render its build intent.
+ Plan {
+ #[arg(long)]
+ spec: PathBuf,
+ },
+ /// Build an image from a specification.
+ Build {
+ #[arg(long)]
+ spec: PathBuf,
+ #[arg(long)]
+ workspace: PathBuf,
+ #[arg(long)]
+ output: PathBuf,
+ },
+ /// Inspect a built artifact.
+ Inspect {
+ #[arg(long)]
+ artifact: PathBuf,
+ },
+ /// Compare two built artifacts semantically.
+ Compare {
+ #[arg(long)]
+ left: PathBuf,
+ #[arg(long)]
+ right: PathBuf,
+ },
+}
+
+pub fn run(cli: Cli) -> Result<()> {
+ match cli.command {
+ Command::Plan { spec } => {
+ let plan = BuildPlan::compile(ImageSpec::load(&spec)?)?;
+ println!("target: {}", plan.spec().target.name);
+ println!("architecture: {}", plan.spec().target.architecture.as_str());
+ println!("format: tar");
+ Ok(())
+ }
+ Command::Build { .. } => bail!("build execution is not available yet"),
+ Command::Inspect { .. } => bail!("artifact inspection is not available yet"),
+ Command::Compare { .. } => bail!("artifact comparison is not available yet"),
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index a62cbde..52a875d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod cli;
pub mod model;
pub mod plan;
diff --git a/src/main.rs b/src/main.rs
index 1ca0cab..1e06263 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,9 @@
+use alt_controller_image::cli::{run, Cli};
+use clap::Parser;
+
fn main() {
- eprintln!("The command-line interface is not available yet. Use the library to load an ImageSpec.");
- std::process::exit(2);
+ if let Err(error) = run(Cli::parse()) {
+ eprintln!("error: {error:#}");
+ std::process::exit(1);
+ }
}
diff --git a/tests/cli.rs b/tests/cli.rs
new file mode 100644
index 0000000..b800e68
--- /dev/null
+++ b/tests/cli.rs
@@ -0,0 +1,71 @@
+use std::path::PathBuf;
+
+use alt_controller_image::cli::{Cli, Command};
+use clap::Parser;
+
+#[test]
+fn parses_plan_with_an_explicit_spec_path() {
+ let cli = Cli::try_parse_from(["alt-controller-image", "plan", "--spec", "image.toml"])
+ .expect("plan command parses");
+
+ assert!(matches!(
+ cli.command,
+ Command::Plan { spec } if spec == PathBuf::from("image.toml")
+ ));
+}
+
+#[test]
+fn parses_build_with_explicit_workspace_and_output_paths() {
+ let cli = Cli::try_parse_from([
+ "alt-controller-image",
+ "build",
+ "--spec",
+ "image.toml",
+ "--workspace",
+ "work",
+ "--output",
+ "out/image.tar",
+ ])
+ .expect("build command parses");
+
+ assert!(matches!(
+ cli.command,
+ Command::Build {
+ spec,
+ workspace,
+ output
+ } if spec == PathBuf::from("image.toml")
+ && workspace == PathBuf::from("work")
+ && output == PathBuf::from("out/image.tar")
+ ));
+}
+
+#[test]
+fn parses_inspect_with_an_explicit_artifact_path() {
+ let cli = Cli::try_parse_from(["alt-controller-image", "inspect", "--artifact", "out/image.tar"])
+ .expect("inspect command parses");
+
+ assert!(matches!(
+ cli.command,
+ Command::Inspect { artifact } if artifact == PathBuf::from("out/image.tar")
+ ));
+}
+
+#[test]
+fn parses_compare_with_explicit_artifact_paths() {
+ let cli = Cli::try_parse_from([
+ "alt-controller-image",
+ "compare",
+ "--left",
+ "legacy.tar",
+ "--right",
+ "native.tar",
+ ])
+ .expect("compare command parses");
+
+ assert!(matches!(
+ cli.command,
+ Command::Compare { left, right }
+ if left == PathBuf::from("legacy.tar") && right == PathBuf::from("native.tar")
+ ));
+}