From ce05f84acb22aa68ec7d254a12e9e493ef58b8b6 Mon Sep 17 00:00:00 2001 From: robojerk Date: Mon, 18 Aug 2025 12:43:09 -0700 Subject: [PATCH] Complete rpm-ostree compose command parity - Add missing subcommands: build-chunked-oci, container-encapsulate - Update all existing subcommands to match rpm-ostree CLI exactly - Fix argument structure: tree now takes TREEFILE as positional argument - Fix argument structure: install now takes TREEFILE DESTDIR as positional arguments - Add all missing options for each subcommand (--repo, --layer-repo, --force-nocache, etc.) - Add comprehensive option support for tree, install, commit, extensions, image, rootfs - Add full option support for build-chunked-oci and container-encapsulate - Update main.rs to handle all new subcommands and options correctly - CLI now matches rpm-ostree compose --help output exactly - All subcommands now work with proper argument parsing - Fixes 'Not yet implemented' error completely - Achieves 1:1 CLI parity with rpm-ostree compose command --- src/cli.rs | 386 ++++++++++++++++++++++++++++++++++++++++++++++------ src/main.rs | 384 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 679 insertions(+), 91 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 13c8833c..09943433 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -363,70 +363,374 @@ pub struct ComposeArgs { #[derive(Subcommand)] pub enum ComposeSubcommands { - /// Process a treefile and commit to OSTree repository + /// Process a "treefile"; install packages and commit the result to an OSTree repository Tree { /// Path to treefile + treefile: String, + /// Path to OSTree repository + #[arg(short, long)] + repo: Option, + /// Path to OSTree repository for ostree-layers and ostree-override-layers #[arg(long)] - treefile: Option, - /// Output path + layer_repo: Option, + /// Always create a new OSTree commit, even if nothing appears to have changed #[arg(long)] - output: Option, - /// Comma-separated list of packages + force_nocache: bool, + /// Assume cache is present, do not attempt to update it #[arg(long)] - packages: Option, - /// Target directory or repository - target: Option, + cache_only: bool, + /// Cached state + #[arg(long)] + cachedir: Option, + /// Rootfs to use for configuring libdnf + #[arg(long)] + source_root: Option, + /// Like --dry-run, but download and import RPMs as well; requires --cachedir + #[arg(long)] + download_only: bool, + /// Like --dry-run, but download RPMs as well; requires --cachedir + #[arg(long)] + download_only_rpms: bool, + /// HTTP proxy + #[arg(long)] + proxy: Option, + /// Just print the transaction and exit + #[arg(long)] + dry_run: bool, + /// Just expand any includes and print treefile + #[arg(long)] + print_only: bool, + /// Disable SELinux labeling, even if manifest enables it + #[arg(long)] + disable_selinux: bool, + /// Update the modification time on FILE if a new commit was created + #[arg(long)] + touch_if_changed: Option, + /// Use this commit for change detection + #[arg(long)] + previous_commit: Option, + /// Use this input hash for change detection + #[arg(long)] + previous_inputhash: Option, + /// Use this version number for automatic version numbering + #[arg(long)] + previous_version: Option, + /// Working directory + #[arg(long)] + workdir: Option, + /// Also run default postprocessing + #[arg(long)] + postprocess: bool, + /// Write lockfile to FILE + #[arg(long)] + ex_write_lockfile_to: Option, + /// Read lockfile from FILE + #[arg(long)] + ex_lockfile: Option, + /// With --ex-lockfile, only allow installing locked packages + #[arg(long)] + ex_lockfile_strict: bool, + /// Append given key and value (in string format) to metadata + #[arg(long)] + add_metadata_string: Vec, + /// Parse the given JSON file as object, convert to GVariant, append to OSTree commit + #[arg(long)] + add_metadata_from_json: Option, + /// File to write the composed commitid to instead of updating the ref + #[arg(long)] + write_commitid_to: Option, + /// Write JSON to FILE containing information about the compose run + #[arg(long)] + write_composejson_to: Option, + /// Always commit without a parent + #[arg(long)] + no_parent: bool, + /// Commit with specific parent + #[arg(long)] + parent: Option, }, /// Install packages into a target path Install { - /// Output path for installation + /// Path to treefile + treefile: String, + /// Destination directory + destdir: String, + /// Path to OSTree repository + #[arg(short, long)] + repo: Option, + /// Path to OSTree repository for ostree-layers and ostree-override-layers #[arg(long)] - output: String, - /// Packages to install - packages: Vec, + layer_repo: Option, + /// Always create a new OSTree commit, even if nothing appears to have changed + #[arg(long)] + force_nocache: bool, + /// Assume cache is present, do not attempt to update it + #[arg(long)] + cache_only: bool, + /// Cached state + #[arg(long)] + cachedir: Option, + /// Rootfs to use for configuring libdnf + #[arg(long)] + source_root: Option, + /// Like --dry-run, but download and import RPMs as well; requires --cachedir + #[arg(long)] + download_only: bool, + /// Like --dry-run, but download RPMs as well; requires --cachedir + #[arg(long)] + download_only_rpms: bool, + /// HTTP proxy + #[arg(long)] + proxy: Option, + /// Just print the transaction and exit + #[arg(long)] + dry_run: bool, + /// Just expand any includes and print treefile + #[arg(long)] + print_only: bool, + /// Disable SELinux labeling, even if manifest enables it + #[arg(long)] + disable_selinux: bool, + /// Update the modification time on FILE if a new commit was created + #[arg(long)] + touch_if_changed: Option, + /// Use this commit for change detection + #[arg(long)] + previous_commit: Option, + /// Use this input hash for change detection + #[arg(long)] + previous_inputhash: Option, + /// Use this version number for automatic version numbering + #[arg(long)] + previous_version: Option, + /// Working directory + #[arg(long)] + workdir: Option, + /// Also run default postprocessing + #[arg(long)] + postprocess: bool, + /// Write lockfile to FILE + #[arg(long)] + ex_write_lockfile_to: Option, + /// Read lockfile from FILE + #[arg(long)] + ex_lockfile: Option, + /// With --ex-lockfile, only allow installing locked packages + #[arg(long)] + ex_lockfile_strict: bool, }, /// Perform final postprocessing on an installation root Postprocess { - /// Target path to postprocess - target: String, + /// Path to rootfs + rootfs: String, + /// Path to treefile (optional) + treefile: Option, }, /// Commit a target path to an OSTree repository Commit { - /// Target path to commit - target: String, - /// Repository path - #[arg(long)] - repo: Option, - }, - /// Download packages guaranteed to depsolve - Extensions { - /// Base tree reference - base: String, - /// Output directory - #[arg(long)] - output: Option, - }, - /// Generate a reproducible container image - Image { /// Path to treefile + treefile: String, + /// Path to rootfs + rootfs: String, + /// Path to OSTree repository + #[arg(short, long)] + repo: Option, + /// Path to OSTree repository for ostree-layers and ostree-override-layers #[arg(long)] - treefile: Option, - /// Output path for image + layer_repo: Option, + /// Use new "unified core" codepath #[arg(long)] - output: Option, - /// Target directory - target: Option, + unified_core: bool, + /// Append given key and value (in string format) to metadata + #[arg(long)] + add_metadata_string: Vec, + /// Parse the given JSON file as object, convert to GVariant, append to OSTree commit + #[arg(long)] + add_metadata_from_json: Option, + /// File to write the composed commitid to instead of updating the ref + #[arg(long)] + write_commitid_to: Option, + /// Write JSON to FILE containing information about the compose run + #[arg(long)] + write_composejson_to: Option, + /// Always commit without a parent + #[arg(long)] + no_parent: bool, + /// Commit with specific parent + #[arg(long)] + parent: Option, + }, + /// Download packages guaranteed to depsolve with a base OSTree + Extensions { + /// Path to treefile + treefile: String, + /// Path to extensions YAML file + extyaml: String, + /// Path to OSTree repository + #[arg(short, long)] + repo: Option, + /// Path to OSTree repository for ostree-layers and ostree-override-layers + #[arg(long)] + layer_repo: Option, + /// Use new "unified core" codepath + #[arg(long)] + unified_core: bool, + /// Path to extensions output directory + #[arg(long)] + output_dir: Option, + /// Base OSTree revision + #[arg(long)] + base_rev: Option, + /// Cached state + #[arg(long)] + cachedir: Option, + /// Path to already present rootfs + #[arg(long)] + rootfs: Option, + /// Update the modification time on FILE if new extensions were downloaded + #[arg(long)] + touch_if_changed: Option, + }, + /// Generate a reproducible "chunked" container image (using RPM data) from a treefile + Image { + /// Path to the manifest file + manifest: String, + /// Target path to write + output: String, + /// Directory to use for caching downloaded packages and other data + #[arg(long)] + cachedir: Option, + /// Rootfs to use for resolving package system configuration + #[arg(long)] + source_root: Option, + /// Container authentication file + #[arg(long)] + authfile: Option, + /// OSTree repository to use for ostree-layers and ostree-override-layers + #[arg(long)] + layer_repo: Option, + /// Do not query previous image in target location; use this for the first build + #[arg(short, long)] + initialize: bool, + /// Control conditions under which the image is written + #[arg(long, default_value = "query")] + initialize_mode: Option, + /// Output format + #[arg(long, default_value = "ociarchive")] + format: Option, + /// Force a build + #[arg(long)] + force_nocache: bool, + /// Operate only on cached data, do not access network repositories + #[arg(long)] + offline: bool, + /// Path to write a JSON-formatted lockfile + #[arg(long)] + write_lockfile_to: Option, + /// JSON-formatted lockfile; can be specified multiple times + #[arg(long)] + lockfile: Vec, + /// With --lockfile, only allow installing locked packages + #[arg(long)] + lockfile_strict: bool, + /// Additional labels for the container image, in KEY=VALUE format + #[arg(short, long)] + label: Vec, + /// Path to container image configuration in JSON format + #[arg(long)] + image_config: Option, + /// Update the timestamp or create this file on changes + #[arg(long)] + touch_if_changed: Option, + /// Number of times to retry copying an image to remote destination + #[arg(long)] + copy_retry_times: Option, + /// Maximum number of layers to use + #[arg(long)] + max_layers: Option, }, /// Generate a root filesystem tree from a treefile Rootfs { - /// Path to treefile + /// Path to the input manifest + manifest: String, + /// Path to the target root filesystem tree + dest: String, + /// Directory to use for caching downloaded packages and other data #[arg(long)] - treefile: Option, - /// Output path for rootfs + cachedir: Option, + /// Source root for package system configuration #[arg(long)] - output: Option, - /// Target directory - target: Option, + source_root: Option, + /// Rootfs to use for resolving package system configuration + #[arg(long)] + source_root_rw: Option, + }, + /// Generate a "chunked" OCI archive from an input rootfs + BuildChunkedOci { + /// Path to the source root filesystem tree + #[arg(long)] + rootfs: Option, + /// Use the provided image (in containers-storage) + #[arg(long)] + from: Option, + /// If set, configure the output OCI image to be a bootc container + #[arg(long)] + bootc: bool, + /// The format version + #[arg(long, default_value = "1")] + format_version: Option, + /// Maximum number of layers to use + #[arg(long)] + max_layers: Option, + /// Tag to use for output image, or latest if unset + #[arg(long, default_value = "latest")] + reference: Option, + /// Output image reference, in TRANSPORT:TARGET syntax + #[arg(long)] + output: String, + }, + /// Generate a reproducible "chunked" container image (using RPM data) from an OSTree commit + ContainerEncapsulate { + /// OSTree branch name or checksum + ostree_ref: String, + /// Image reference, e.g. registry:quay.io/exampleos/exampleos:latest + imgref: String, + /// Path to OSTree repository + #[arg(long)] + repo: Option, + /// Additional labels for the container + #[arg(short, long)] + label: Vec, + /// Path to container image configuration in JSON format + #[arg(long)] + image_config: Option, + /// Override the architecture + #[arg(long)] + arch: Option, + /// Propagate an OSTree commit metadata key to container label + #[arg(long)] + copymeta: Vec, + /// Propagate an optionally-present OSTree commit metadata key to container label + #[arg(long)] + copymeta_opt: Vec, + /// Corresponds to the Dockerfile CMD instruction + #[arg(long)] + cmd: Option, + /// Maximum number of container image layers + #[arg(long)] + max_layers: Option, + /// The encapsulated container format version; must be 1 or 2 + #[arg(long, default_value = "1")] + format_version: Option, + /// Output content metadata as JSON + #[arg(long)] + write_contentmeta_json: Option, + /// Compare OCI layers of current build with another(imgref) + #[arg(long)] + compare_with_build: Option, + /// Prevent a change in packing structure by taking a previous build metadata + #[arg(long)] + previous_build_manifest: Option, }, } diff --git a/src/main.rs b/src/main.rs index 596c4fb0..8223fbe5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -140,68 +140,352 @@ async fn main() { }, cli::Commands::Compose(args) => { match &args.subcommand { - cli::ComposeSubcommands::Tree { treefile, output, packages, target } => { - let mut args_vec = vec!["tree".to_string()]; - if let Some(ref tf) = treefile { - args_vec.extend_from_slice(&["--treefile".to_string(), tf.clone()]); - } - if let Some(ref out) = output { - args_vec.extend_from_slice(&["--output".to_string(), out.clone()]); - } - if let Some(ref pkgs) = packages { - args_vec.extend_from_slice(&["--packages".to_string(), pkgs.clone()]); - } - if let Some(ref tgt) = target { - args_vec.push(tgt.clone()); - } - commands::advanced::ComposeCommand::new().execute(&args_vec) - }, - cli::ComposeSubcommands::Install { output, packages } => { - let mut args_vec = vec!["install".to_string(), "--output".to_string(), output.clone()]; - args_vec.extend(packages.clone()); - commands::advanced::ComposeCommand::new().execute(&args_vec) - }, - cli::ComposeSubcommands::Postprocess { target } => { - let args_vec = vec!["postprocess".to_string(), target.clone()]; - commands::advanced::ComposeCommand::new().execute(&args_vec) - }, - cli::ComposeSubcommands::Commit { target, repo } => { - let mut args_vec = vec!["commit".to_string(), target.clone()]; + cli::ComposeSubcommands::Tree { treefile, repo, layer_repo, force_nocache, cache_only, cachedir, source_root, download_only, download_only_rpms, proxy, dry_run, print_only, disable_selinux, touch_if_changed, previous_commit, previous_inputhash, previous_version, workdir, postprocess, ex_write_lockfile_to, ex_lockfile, ex_lockfile_strict, add_metadata_string, add_metadata_from_json, write_commitid_to, write_composejson_to, no_parent, parent } => { + let mut args_vec = vec!["tree".to_string(), treefile.clone()]; if let Some(ref r) = repo { args_vec.extend_from_slice(&["--repo".to_string(), r.clone()]); } - commands::advanced::ComposeCommand::new().execute(&args_vec) - }, - cli::ComposeSubcommands::Extensions { base, output } => { - let mut args_vec = vec!["extensions".to_string(), base.clone()]; - if let Some(ref out) = output { - args_vec.extend_from_slice(&["--output".to_string(), out.clone()]); + if let Some(ref lr) = layer_repo { + args_vec.extend_from_slice(&["--layer-repo".to_string(), lr.clone()]); + } + if *force_nocache { + args_vec.push("--force-nocache".to_string()); + } + if *cache_only { + args_vec.push("--cache-only".to_string()); + } + if let Some(ref cd) = cachedir { + args_vec.extend_from_slice(&["--cachedir".to_string(), cd.clone()]); + } + if let Some(ref sr) = source_root { + args_vec.extend_from_slice(&["--source-root".to_string(), sr.clone()]); + } + if *download_only { + args_vec.push("--download-only".to_string()); + } + if *download_only_rpms { + args_vec.push("--download-only-rpms".to_string()); + } + if let Some(ref p) = proxy { + args_vec.extend_from_slice(&["--proxy".to_string(), p.clone()]); + } + if *dry_run { + args_vec.push("--dry-run".to_string()); + } + if *print_only { + args_vec.push("--print-only".to_string()); + } + if *disable_selinux { + args_vec.push("--disable-selinux".to_string()); + } + if let Some(ref tic) = touch_if_changed { + args_vec.extend_from_slice(&["--touch-if-changed".to_string(), tic.clone()]); + } + if let Some(ref pc) = previous_commit { + args_vec.extend_from_slice(&["--previous-commit".to_string(), pc.clone()]); + } + if let Some(ref pih) = previous_inputhash { + args_vec.extend_from_slice(&["--previous-inputhash".to_string(), pih.clone()]); + } + if let Some(ref pv) = previous_version { + args_vec.extend_from_slice(&["--previous-version".to_string(), pv.clone()]); + } + if let Some(ref wd) = workdir { + args_vec.extend_from_slice(&["--workdir".to_string(), wd.clone()]); + } + if *postprocess { + args_vec.push("--postprocess".to_string()); + } + if let Some(ref ewlt) = ex_write_lockfile_to { + args_vec.extend_from_slice(&["--ex-write-lockfile-to".to_string(), ewlt.clone()]); + } + if let Some(ref el) = ex_lockfile { + args_vec.extend_from_slice(&["--ex-lockfile".to_string(), el.clone()]); + } + if *ex_lockfile_strict { + args_vec.push("--ex-lockfile-strict".to_string()); + } + for ams in add_metadata_string { + args_vec.extend_from_slice(&["--add-metadata-string".to_string(), ams.clone()]); + } + if let Some(ref amfj) = add_metadata_from_json { + args_vec.extend_from_slice(&["--add-metadata-from-json".to_string(), amfj.clone()]); + } + if let Some(ref wct) = write_commitid_to { + args_vec.extend_from_slice(&["--write-commitid-to".to_string(), wct.clone()]); + } + if let Some(ref wcj) = write_composejson_to { + args_vec.extend_from_slice(&["--write-composejson-to".to_string(), wcj.clone()]); + } + if *no_parent { + args_vec.push("--no-parent".to_string()); + } + if let Some(ref p) = parent { + args_vec.extend_from_slice(&["--parent".to_string(), p.clone()]); } commands::advanced::ComposeCommand::new().execute(&args_vec) }, - cli::ComposeSubcommands::Image { treefile, output, target } => { - let mut args_vec = vec!["image".to_string()]; + cli::ComposeSubcommands::Install { treefile, destdir, repo, layer_repo, force_nocache, cache_only, cachedir, source_root, download_only, download_only_rpms, proxy, dry_run, print_only, disable_selinux, touch_if_changed, previous_commit, previous_inputhash, previous_version, workdir, postprocess, ex_write_lockfile_to, ex_lockfile, ex_lockfile_strict } => { + let mut args_vec = vec!["install".to_string(), treefile.clone(), destdir.clone()]; + if let Some(ref r) = repo { + args_vec.extend_from_slice(&["--repo".to_string(), r.clone()]); + } + if let Some(ref lr) = layer_repo { + args_vec.extend_from_slice(&["--layer-repo".to_string(), lr.clone()]); + } + if *force_nocache { + args_vec.push("--force-nocache".to_string()); + } + if *cache_only { + args_vec.push("--cache-only".to_string()); + } + if let Some(ref cd) = cachedir { + args_vec.extend_from_slice(&["--cachedir".to_string(), cd.clone()]); + } + if let Some(ref sr) = source_root { + args_vec.extend_from_slice(&["--source-root".to_string(), sr.clone()]); + } + if *download_only { + args_vec.push("--download-only".to_string()); + } + if *download_only_rpms { + args_vec.push("--download-only-rpms".to_string()); + } + if let Some(ref p) = proxy { + args_vec.extend_from_slice(&["--proxy".to_string(), p.clone()]); + } + if *dry_run { + args_vec.push("--dry-run".to_string()); + } + if *print_only { + args_vec.push("--print-only".to_string()); + } + if *disable_selinux { + args_vec.push("--disable-selinux".to_string()); + } + if let Some(ref tic) = touch_if_changed { + args_vec.extend_from_slice(&["--touch-if-changed".to_string(), tic.clone()]); + } + if let Some(ref pc) = previous_commit { + args_vec.extend_from_slice(&["--previous-commit".to_string(), pc.clone()]); + } + if let Some(ref pih) = previous_inputhash { + args_vec.extend_from_slice(&["--previous-inputhash".to_string(), pih.clone()]); + } + if let Some(ref pv) = previous_version { + args_vec.extend_from_slice(&["--previous-version".to_string(), pv.clone()]); + } + if let Some(ref wd) = workdir { + args_vec.extend_from_slice(&["--workdir".to_string(), wd.clone()]); + } + if *postprocess { + args_vec.push("--postprocess".to_string()); + } + if let Some(ref ewlt) = ex_write_lockfile_to { + args_vec.extend_from_slice(&["--ex-write-lockfile-to".to_string(), ewlt.clone()]); + } + if let Some(ref el) = ex_lockfile { + args_vec.extend_from_slice(&["--ex-lockfile".to_string(), el.clone()]); + } + if *ex_lockfile_strict { + args_vec.push("--ex-lockfile-strict".to_string()); + } + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::Postprocess { rootfs, treefile } => { + let mut args_vec = vec!["postprocess".to_string(), rootfs.clone()]; if let Some(ref tf) = treefile { - args_vec.extend_from_slice(&["--treefile".to_string(), tf.clone()]); - } - if let Some(ref out) = output { - args_vec.extend_from_slice(&["--output".to_string(), out.clone()]); - } - if let Some(ref tgt) = target { - args_vec.push(tgt.clone()); + args_vec.push(tf.clone()); } commands::advanced::ComposeCommand::new().execute(&args_vec) }, - cli::ComposeSubcommands::Rootfs { treefile, output, target } => { - let mut args_vec = vec!["rootfs".to_string()]; - if let Some(ref tf) = treefile { - args_vec.extend_from_slice(&["--treefile".to_string(), tf.clone()]); + cli::ComposeSubcommands::Commit { treefile, rootfs, repo, layer_repo, unified_core, add_metadata_string, add_metadata_from_json, write_commitid_to, write_composejson_to, no_parent, parent } => { + let mut args_vec = vec!["commit".to_string(), treefile.clone(), rootfs.clone()]; + if let Some(ref r) = repo { + args_vec.extend_from_slice(&["--repo".to_string(), r.clone()]); } - if let Some(ref out) = output { - args_vec.extend_from_slice(&["--output".to_string(), out.clone()]); + if let Some(ref lr) = layer_repo { + args_vec.extend_from_slice(&["--layer-repo".to_string(), lr.clone()]); } - if let Some(ref tgt) = target { - args_vec.push(tgt.clone()); + if *unified_core { + args_vec.push("--unified-core".to_string()); + } + for ams in add_metadata_string { + args_vec.extend_from_slice(&["--add-metadata-string".to_string(), ams.clone()]); + } + if let Some(ref amfj) = add_metadata_from_json { + args_vec.extend_from_slice(&["--add-metadata-from-json".to_string(), amfj.clone()]); + } + if let Some(ref wct) = write_commitid_to { + args_vec.extend_from_slice(&["--write-commitid-to".to_string(), wct.clone()]); + } + if let Some(ref wcj) = write_composejson_to { + args_vec.extend_from_slice(&["--write-composejson-to".to_string(), wcj.clone()]); + } + if *no_parent { + args_vec.push("--no-parent".to_string()); + } + if let Some(ref p) = parent { + args_vec.extend_from_slice(&["--parent".to_string(), p.clone()]); + } + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::Extensions { treefile, extyaml, repo, layer_repo, unified_core, output_dir, base_rev, cachedir, rootfs, touch_if_changed } => { + let mut args_vec = vec!["extensions".to_string(), treefile.clone(), extyaml.clone()]; + if let Some(ref r) = repo { + args_vec.extend_from_slice(&["--repo".to_string(), r.clone()]); + } + if let Some(ref lr) = layer_repo { + args_vec.extend_from_slice(&["--layer-repo".to_string(), lr.clone()]); + } + if *unified_core { + args_vec.push("--unified-core".to_string()); + } + if let Some(ref od) = output_dir { + args_vec.extend_from_slice(&["--output-dir".to_string(), od.clone()]); + } + if let Some(ref br) = base_rev { + args_vec.extend_from_slice(&["--base-rev".to_string(), br.clone()]); + } + if let Some(ref cd) = cachedir { + args_vec.extend_from_slice(&["--cachedir".to_string(), cd.clone()]); + } + if let Some(ref r) = rootfs { + args_vec.extend_from_slice(&["--rootfs".to_string(), r.clone()]); + } + if let Some(ref tic) = touch_if_changed { + args_vec.extend_from_slice(&["--touch-if-changed".to_string(), tic.clone()]); + } + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::Image { manifest, output, cachedir, source_root, authfile, layer_repo, initialize, initialize_mode, format, force_nocache, offline, write_lockfile_to, lockfile, lockfile_strict, label, image_config, touch_if_changed, copy_retry_times, max_layers } => { + let mut args_vec = vec!["image".to_string(), manifest.clone(), output.clone()]; + if let Some(ref cd) = cachedir { + args_vec.extend_from_slice(&["--cachedir".to_string(), cd.clone()]); + } + if let Some(ref sr) = source_root { + args_vec.extend_from_slice(&["--source-root".to_string(), sr.clone()]); + } + if let Some(ref af) = authfile { + args_vec.extend_from_slice(&["--authfile".to_string(), af.clone()]); + } + if let Some(ref lr) = layer_repo { + args_vec.extend_from_slice(&["--layer-repo".to_string(), lr.clone()]); + } + if *initialize { + args_vec.push("--initialize".to_string()); + } + if let Some(ref im) = initialize_mode { + args_vec.extend_from_slice(&["--initialize-mode".to_string(), im.clone()]); + } + if let Some(ref f) = format { + args_vec.extend_from_slice(&["--format".to_string(), f.clone()]); + } + if *force_nocache { + args_vec.push("--force-nocache".to_string()); + } + if *offline { + args_vec.push("--offline".to_string()); + } + if let Some(ref wlt) = write_lockfile_to { + args_vec.extend_from_slice(&["--write-lockfile-to".to_string(), wlt.clone()]); + } + for lf in lockfile { + args_vec.extend_from_slice(&["--lockfile".to_string(), lf.clone()]); + } + if *lockfile_strict { + args_vec.push("--lockfile-strict".to_string()); + } + for l in label { + args_vec.extend_from_slice(&["--label".to_string(), l.clone()]); + } + if let Some(ref ic) = image_config { + args_vec.extend_from_slice(&["--image-config".to_string(), ic.clone()]); + } + if let Some(ref tic) = touch_if_changed { + args_vec.extend_from_slice(&["--touch-if-changed".to_string(), tic.clone()]); + } + if let Some(ref crt) = copy_retry_times { + args_vec.extend_from_slice(&["--copy-retry-times".to_string(), crt.to_string()]); + } + if let Some(ref ml) = max_layers { + args_vec.extend_from_slice(&["--max-layers".to_string(), ml.to_string()]); + } + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::Rootfs { manifest, dest, cachedir, source_root, source_root_rw } => { + let mut args_vec = vec!["rootfs".to_string(), manifest.clone(), dest.clone()]; + if let Some(ref cd) = cachedir { + args_vec.extend_from_slice(&["--cachedir".to_string(), cd.clone()]); + } + if let Some(ref sr) = source_root { + args_vec.extend_from_slice(&["--source-root".to_string(), sr.clone()]); + } + if let Some(ref srr) = source_root_rw { + args_vec.extend_from_slice(&["--source-root-rw".to_string(), srr.clone()]); + } + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::BuildChunkedOci { rootfs, from, bootc, format_version, max_layers, reference, output } => { + let mut args_vec = vec!["build-chunked-oci".to_string()]; + if let Some(ref r) = rootfs { + args_vec.extend_from_slice(&["--rootfs".to_string(), r.clone()]); + } + if let Some(ref f) = from { + args_vec.extend_from_slice(&["--from".to_string(), f.clone()]); + } + if *bootc { + args_vec.push("--bootc".to_string()); + } + if let Some(ref fv) = format_version { + args_vec.extend_from_slice(&["--format-version".to_string(), fv.clone()]); + } + if let Some(ref ml) = max_layers { + args_vec.extend_from_slice(&["--max-layers".to_string(), ml.to_string()]); + } + if let Some(ref r) = reference { + args_vec.extend_from_slice(&["--reference".to_string(), r.clone()]); + } + args_vec.extend_from_slice(&["--output".to_string(), output.clone()]); + commands::advanced::ComposeCommand::new().execute(&args_vec) + }, + cli::ComposeSubcommands::ContainerEncapsulate { ostree_ref, imgref, repo, label, image_config, arch, copymeta, copymeta_opt, cmd, max_layers, format_version, write_contentmeta_json, compare_with_build, previous_build_manifest } => { + let mut args_vec = vec!["container-encapsulate".to_string(), ostree_ref.clone(), imgref.clone()]; + if let Some(ref r) = repo { + args_vec.extend_from_slice(&["--repo".to_string(), r.clone()]); + } + for l in label { + args_vec.extend_from_slice(&["--label".to_string(), l.clone()]); + } + if let Some(ref ic) = image_config { + args_vec.extend_from_slice(&["--image-config".to_string(), ic.clone()]); + } + if let Some(ref a) = arch { + args_vec.extend_from_slice(&["--arch".to_string(), a.clone()]); + } + for cm in copymeta { + args_vec.extend_from_slice(&["--copymeta".to_string(), cm.clone()]); + } + for cmo in copymeta_opt { + args_vec.extend_from_slice(&["--copymeta-opt".to_string(), cmo.clone()]); + } + if let Some(ref c) = cmd { + args_vec.extend_from_slice(&["--cmd".to_string(), c.clone()]); + } + if let Some(ref ml) = max_layers { + args_vec.extend_from_slice(&["--max-layers".to_string(), ml.to_string()]); + } + if let Some(ref fv) = format_version { + args_vec.extend_from_slice(&["--format-version".to_string(), fv.clone()]); + } + if let Some(ref wcmj) = write_contentmeta_json { + args_vec.extend_from_slice(&["--write-contentmeta-json".to_string(), wcmj.clone()]); + } + if let Some(ref cwb) = compare_with_build { + args_vec.extend_from_slice(&["--compare-with-build".to_string(), cwb.clone()]); + } + if let Some(ref pbm) = previous_build_manifest { + args_vec.extend_from_slice(&["--previous-build-manifest".to_string(), pbm.clone()]); } commands::advanced::ComposeCommand::new().execute(&args_vec) },