pub mod core;
pub mod file;
pub mod publish;
pub mod sign;
pub mod verify;
use clap::{Args, Subcommand};
use std::path::PathBuf;
use std::sync::Arc;
use anyhow::Result;
use auths_core::config::EnvironmentConfig;
use auths_core::signing::PassphraseProvider;
#[derive(Args, Debug, Clone)]
#[command(about = "Sign and verify arbitrary artifacts (tarballs, binaries, etc.).")]
pub struct ArtifactCommand {
#[command(subcommand)]
pub command: ArtifactSubcommand,
}
#[derive(Subcommand, Debug, Clone)]
pub enum ArtifactSubcommand {
Sign {
#[arg(help = "Path to the artifact file to sign.")]
file: PathBuf,
#[arg(long = "sig-output", value_name = "PATH")]
sig_output: Option<PathBuf>,
#[arg(
long,
visible_alias = "ika",
help = "Local alias of the identity key. Omit for device-only CI signing."
)]
identity_key_alias: Option<String>,
#[arg(
long,
visible_alias = "dka",
help = "Local alias of the device key (used for dual-signing)."
)]
device_key_alias: String,
#[arg(long, visible_alias = "days", value_name = "N")]
expires_in_days: Option<i64>,
#[arg(long)]
note: Option<String>,
},
Publish {
#[arg(long)]
signature: PathBuf,
#[arg(long)]
package: Option<String>,
#[arg(long, default_value = "https://auths-registry.fly.dev")]
registry: String,
},
Verify {
#[arg(help = "Path to the artifact file to verify.")]
file: PathBuf,
#[arg(long, value_name = "PATH")]
signature: Option<PathBuf>,
#[arg(long, value_parser)]
identity_bundle: Option<PathBuf>,
#[arg(long)]
witness_receipts: Option<PathBuf>,
#[arg(long, num_args = 1..)]
witness_keys: Vec<String>,
#[arg(long, default_value = "1")]
witness_threshold: usize,
},
}
pub fn handle_artifact(
cmd: ArtifactCommand,
repo_opt: Option<PathBuf>,
passphrase_provider: Arc<dyn PassphraseProvider + Send + Sync>,
env_config: &EnvironmentConfig,
) -> Result<()> {
match cmd.command {
ArtifactSubcommand::Sign {
file,
sig_output,
identity_key_alias,
device_key_alias,
expires_in_days,
note,
} => sign::handle_sign(
&file,
sig_output,
identity_key_alias.as_deref(),
&device_key_alias,
expires_in_days,
note,
repo_opt,
passphrase_provider,
env_config,
),
ArtifactSubcommand::Publish {
signature,
package,
registry,
} => publish::handle_publish(&signature, package.as_deref(), ®istry),
ArtifactSubcommand::Verify {
file,
signature,
identity_bundle,
witness_receipts,
witness_keys,
witness_threshold,
} => {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(verify::handle_verify(
&file,
signature,
identity_bundle,
witness_receipts,
&witness_keys,
witness_threshold,
))
}
}
}
impl crate::commands::executable::ExecutableCommand for ArtifactCommand {
fn execute(&self, ctx: &crate::config::CliConfig) -> anyhow::Result<()> {
handle_artifact(
self.clone(),
ctx.repo_path.clone(),
ctx.passphrase_provider.clone(),
&ctx.env_config,
)
}
}