diff --git a/lib/src/cli.rs b/lib/src/cli.rs index 287031ae..4641505e 100644 --- a/lib/src/cli.rs +++ b/lib/src/cli.rs @@ -6,7 +6,7 @@ //! such as `rpm-ostree` can directly reuse it. use anyhow::{Context, Result}; -use camino::Utf8PathBuf; +use camino::{Utf8Path, Utf8PathBuf}; use clap::{Parser, Subcommand}; use ostree::{cap_std, gio, glib}; use std::collections::BTreeMap; @@ -31,18 +31,19 @@ pub fn parse_base_imgref(s: &str) -> Result { } /// Parse an [`ostree::Repo`] from a CLI arguemnt. -pub fn parse_repo(s: &str) -> Result { - let repofd = cap_std::fs::Dir::open_ambient_dir(s, cap_std::ambient_authority())?; - Ok(ostree::Repo::open_at_dir(&repofd, ".")?) +pub fn parse_repo(s: &Utf8Path) -> Result { + let repofd = cap_std::fs::Dir::open_ambient_dir(s, cap_std::ambient_authority()) + .with_context(|| format!("Opening directory at '{s}'"))?; + ostree::Repo::open_at_dir(&repofd, ".") + .with_context(|| format!("Opening ostree repository at '{s}'")) } /// Options for importing a tar archive. #[derive(Debug, Parser)] struct ImportOpts { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// Path to a tar archive; if unspecified, will be stdin. Currently the tar archive must not be compressed. path: Option, @@ -52,9 +53,8 @@ struct ImportOpts { #[derive(Debug, Parser)] struct ExportOpts { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// The format version. Must be 0 or 1. #[clap(long)] @@ -81,12 +81,11 @@ enum ContainerOpts { /// Import an ostree commit embedded in a remote container image Unencapsulate { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] imgref: OstreeImageReference, /// Create an ostree ref pointing to the imported commit @@ -101,7 +100,7 @@ enum ContainerOpts { /// Print information about an exported ostree-container image. Info { /// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] imgref: OstreeImageReference, }, @@ -109,15 +108,14 @@ enum ContainerOpts { #[clap(alias = "export")] Encapsulate { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// The ostree ref or commit to export rev: String, /// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_base_imgref))] + #[clap(value_parser = parse_base_imgref)] imgref: ImageReference, /// Additional labels for the container @@ -174,19 +172,18 @@ enum ContainerImageOpts { /// List container images List { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, }, /// Pull (or update) a container image. Pull { /// Path to the repository - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(value_parser)] + repo: Utf8PathBuf, /// Image reference, e.g. ostree-remote-image:someremote:registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] imgref: OstreeImageReference, #[clap(flatten)] @@ -200,28 +197,26 @@ enum ContainerImageOpts { /// Output metadata about an already stored container image. History { /// Path to the repository - #[clap(long, parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// Container image reference, e.g. registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_base_imgref))] + #[clap(value_parser = parse_base_imgref)] imgref: ImageReference, }, /// Copy a pulled container image from one repo to another. Copy { /// Path to the source repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - src_repo: ostree::Repo, + #[clap(long, value_parser)] + src_repo: Utf8PathBuf, /// Path to the destination repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - dest_repo: ostree::Repo, + #[clap(long, value_parser)] + dest_repo: Utf8PathBuf, /// Image reference, e.g. ostree-remote-image:someremote:registry:quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] imgref: OstreeImageReference, }, @@ -229,12 +224,12 @@ enum ContainerImageOpts { ReplaceDetachedMetadata { /// Path to the source repository #[clap(long)] - #[clap(parse(try_from_str = parse_base_imgref))] + #[clap(value_parser = parse_base_imgref)] src: ImageReference, /// Target image #[clap(long)] - #[clap(parse(try_from_str = parse_base_imgref))] + #[clap(value_parser = parse_base_imgref)] dest: ImageReference, /// Path to file containing new detached metadata; if not provided, @@ -245,12 +240,11 @@ enum ContainerImageOpts { /// Unreference one or more pulled container images and perform a garbage collection. Remove { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, /// Image reference, e.g. quay.io/exampleos/exampleos:latest - #[clap(parse(try_from_str = parse_base_imgref))] + #[clap(value_parser = parse_base_imgref)] imgrefs: Vec, /// Do not garbage collect unused layers @@ -270,7 +264,7 @@ enum ContainerImageOpts { /// Source image reference, e.g. ostree-remote-image:someremote:registry:quay.io/exampleos/exampleos@sha256:abcd... #[clap(long)] - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] imgref: OstreeImageReference, #[clap(flatten)] @@ -281,7 +275,7 @@ enum ContainerImageOpts { /// If specified, `--imgref` will be used as a source, but this reference will be emitted into the origin /// so that later OS updates pull from it. #[clap(long)] - #[clap(parse(try_from_str = parse_imgref))] + #[clap(value_parser = parse_imgref)] target_imgref: Option, #[clap(long)] @@ -298,9 +292,9 @@ enum ContainerImageOpts { #[derive(Debug, Parser)] struct ImaSignOpts { /// Path to the repository - #[clap(long)] - #[clap(parse(try_from_str = parse_repo))] - repo: ostree::Repo, + #[clap(long, value_parser)] + repo: Utf8PathBuf, + /// The ostree ref or commit to use as a base src_rev: String, /// The ostree ref to use for writing the signed commit @@ -365,12 +359,13 @@ impl Into for ContainerProxyOpts { /// Import a tar archive containing an ostree commit. async fn tar_import(opts: &ImportOpts) -> Result<()> { + let repo = parse_repo(&opts.repo)?; let imported = if let Some(path) = opts.path.as_ref() { let instream = tokio::fs::File::open(path).await?; - crate::tar::import_tar(&opts.repo, instream, None).await? + crate::tar::import_tar(&repo, instream, None).await? } else { let stdin = tokio::io::stdin(); - crate::tar::import_tar(&opts.repo, stdin, None).await? + crate::tar::import_tar(&repo, stdin, None).await? }; println!("Imported: {}", imported); Ok(()) @@ -381,17 +376,13 @@ fn tar_export(opts: &ExportOpts) -> Result<()> { if !crate::tar::FORMAT_VERSIONS.contains(&opts.format_version) { anyhow::bail!("Invalid format version: {}", opts.format_version); } + let repo = parse_repo(&opts.repo)?; #[allow(clippy::needless_update)] let subopts = crate::tar::ExportOptions { format_version: opts.format_version, ..Default::default() }; - crate::tar::export_commit( - &opts.repo, - opts.rev.as_str(), - std::io::stdout(), - Some(subopts), - )?; + crate::tar::export_commit(&repo, opts.rev.as_str(), std::io::stdout(), Some(subopts))?; Ok(()) } @@ -648,9 +639,10 @@ fn ima_sign(cmdopts: &ImaSignOpts) -> Result<()> { key: cmdopts.key.clone(), overwrite: cmdopts.overwrite, }; - let tx = cmdopts.repo.auto_transaction(cancellable)?; - let signed_commit = crate::ima::ima_sign(&cmdopts.repo, cmdopts.src_rev.as_str(), &signopts)?; - cmdopts.repo.transaction_set_ref( + let repo = parse_repo(&cmdopts.repo)?; + let tx = repo.auto_transaction(cancellable)?; + let signed_commit = crate::ima::ima_sign(&repo, cmdopts.src_rev.as_str(), &signopts)?; + repo.transaction_set_ref( None, cmdopts.target_ref.as_str(), Some(signed_commit.as_str()), @@ -684,7 +676,7 @@ where I: IntoIterator, I::Item: Into + Clone, { - let opt = Opt::from_iter(args); + let opt = Opt::parse_from(args); match opt { Opt::Tar(TarOpts::Import(ref opt)) => tar_import(opt).await, Opt::Tar(TarOpts::Export(ref opt)) => tar_export(opt), @@ -696,7 +688,10 @@ where imgref, write_ref, quiet, - } => container_import(&repo, &imgref, write_ref.as_deref(), quiet).await, + } => { + let repo = parse_repo(&repo)?; + container_import(&repo, &imgref, write_ref.as_deref(), quiet).await + } ContainerOpts::Encapsulate { repo, rev, @@ -715,6 +710,7 @@ where Ok((k.to_string(), v.to_string())) }) .collect(); + let repo = parse_repo(&repo)?; container_export( &repo, &rev, @@ -728,6 +724,7 @@ where } ContainerOpts::Image(opts) => match opts { ContainerImageOpts::List { repo } => { + let repo = parse_repo(&repo)?; for image in crate::container::store::list_images(&repo)? { println!("{}", image); } @@ -738,8 +735,12 @@ where imgref, proxyopts, quiet, - } => container_store(&repo, &imgref, proxyopts, quiet).await, + } => { + let repo = parse_repo(&repo)?; + container_store(&repo, &imgref, proxyopts, quiet).await + } ContainerImageOpts::History { repo, imgref } => { + let repo = parse_repo(&repo)?; container_history(&repo, &imgref).await } ContainerImageOpts::Remove { @@ -748,6 +749,7 @@ where skip_gc, } => { let nimgs = imgrefs.len(); + let repo = parse_repo(&repo)?; crate::container::store::remove_images(&repo, imgrefs.iter())?; if !skip_gc { let nlayers = crate::container::store::gc_image_layers(&repo)?; @@ -761,7 +763,11 @@ where src_repo, dest_repo, imgref, - } => crate::container::store::copy(&src_repo, &dest_repo, &imgref).await, + } => { + let src_repo = parse_repo(&src_repo)?; + let dest_repo = parse_repo(&dest_repo)?; + crate::container::store::copy(&src_repo, &dest_repo, &imgref).await + } ContainerImageOpts::ReplaceDetachedMetadata { src, dest, diff --git a/lib/tests/it/main.rs b/lib/tests/it/main.rs index dfcca571..17da67d6 100644 --- a/lib/tests/it/main.rs +++ b/lib/tests/it/main.rs @@ -43,7 +43,7 @@ static TEST_REGISTRY: Lazy = Lazy::new(|| match std::env::var_os("TEST_R fn test_cli_fns() -> Result<()> { let fixture = Fixture::new_v1()?; let srcpath = fixture.path.join("src/repo"); - let srcrepo_parsed = ostree_ext::cli::parse_repo(srcpath.as_str()).unwrap(); + let srcrepo_parsed = ostree_ext::cli::parse_repo(&srcpath).unwrap(); assert_eq!(srcrepo_parsed.mode(), fixture.srcrepo().mode()); let ir =