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
This commit is contained in:
robojerk 2025-08-18 12:43:09 -07:00
parent bcf7183107
commit c87a832831
2 changed files with 679 additions and 91 deletions

View file

@ -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<String>,
/// Path to OSTree repository for ostree-layers and ostree-override-layers
#[arg(long)]
treefile: Option<String>,
/// Output path
layer_repo: Option<String>,
/// Always create a new OSTree commit, even if nothing appears to have changed
#[arg(long)]
output: Option<String>,
/// Comma-separated list of packages
force_nocache: bool,
/// Assume cache is present, do not attempt to update it
#[arg(long)]
packages: Option<String>,
/// Target directory or repository
target: Option<String>,
cache_only: bool,
/// Cached state
#[arg(long)]
cachedir: Option<String>,
/// Rootfs to use for configuring libdnf
#[arg(long)]
source_root: Option<String>,
/// 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<String>,
/// 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<String>,
/// Use this commit for change detection
#[arg(long)]
previous_commit: Option<String>,
/// Use this input hash for change detection
#[arg(long)]
previous_inputhash: Option<String>,
/// Use this version number for automatic version numbering
#[arg(long)]
previous_version: Option<String>,
/// Working directory
#[arg(long)]
workdir: Option<String>,
/// Also run default postprocessing
#[arg(long)]
postprocess: bool,
/// Write lockfile to FILE
#[arg(long)]
ex_write_lockfile_to: Option<String>,
/// Read lockfile from FILE
#[arg(long)]
ex_lockfile: Option<String>,
/// 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<String>,
/// Parse the given JSON file as object, convert to GVariant, append to OSTree commit
#[arg(long)]
add_metadata_from_json: Option<String>,
/// File to write the composed commitid to instead of updating the ref
#[arg(long)]
write_commitid_to: Option<String>,
/// Write JSON to FILE containing information about the compose run
#[arg(long)]
write_composejson_to: Option<String>,
/// Always commit without a parent
#[arg(long)]
no_parent: bool,
/// Commit with specific parent
#[arg(long)]
parent: Option<String>,
},
/// 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<String>,
/// Path to OSTree repository for ostree-layers and ostree-override-layers
#[arg(long)]
output: String,
/// Packages to install
packages: Vec<String>,
layer_repo: Option<String>,
/// 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<String>,
/// Rootfs to use for configuring libdnf
#[arg(long)]
source_root: Option<String>,
/// 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<String>,
/// 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<String>,
/// Use this commit for change detection
#[arg(long)]
previous_commit: Option<String>,
/// Use this input hash for change detection
#[arg(long)]
previous_inputhash: Option<String>,
/// Use this version number for automatic version numbering
#[arg(long)]
previous_version: Option<String>,
/// Working directory
#[arg(long)]
workdir: Option<String>,
/// Also run default postprocessing
#[arg(long)]
postprocess: bool,
/// Write lockfile to FILE
#[arg(long)]
ex_write_lockfile_to: Option<String>,
/// Read lockfile from FILE
#[arg(long)]
ex_lockfile: Option<String>,
/// 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<String>,
},
/// Commit a target path to an OSTree repository
Commit {
/// Target path to commit
target: String,
/// Repository path
#[arg(long)]
repo: Option<String>,
},
/// Download packages guaranteed to depsolve
Extensions {
/// Base tree reference
base: String,
/// Output directory
#[arg(long)]
output: Option<String>,
},
/// 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<String>,
/// Path to OSTree repository for ostree-layers and ostree-override-layers
#[arg(long)]
treefile: Option<String>,
/// Output path for image
layer_repo: Option<String>,
/// Use new "unified core" codepath
#[arg(long)]
output: Option<String>,
/// Target directory
target: Option<String>,
unified_core: bool,
/// Append given key and value (in string format) to metadata
#[arg(long)]
add_metadata_string: Vec<String>,
/// Parse the given JSON file as object, convert to GVariant, append to OSTree commit
#[arg(long)]
add_metadata_from_json: Option<String>,
/// File to write the composed commitid to instead of updating the ref
#[arg(long)]
write_commitid_to: Option<String>,
/// Write JSON to FILE containing information about the compose run
#[arg(long)]
write_composejson_to: Option<String>,
/// Always commit without a parent
#[arg(long)]
no_parent: bool,
/// Commit with specific parent
#[arg(long)]
parent: Option<String>,
},
/// 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<String>,
/// Path to OSTree repository for ostree-layers and ostree-override-layers
#[arg(long)]
layer_repo: Option<String>,
/// Use new "unified core" codepath
#[arg(long)]
unified_core: bool,
/// Path to extensions output directory
#[arg(long)]
output_dir: Option<String>,
/// Base OSTree revision
#[arg(long)]
base_rev: Option<String>,
/// Cached state
#[arg(long)]
cachedir: Option<String>,
/// Path to already present rootfs
#[arg(long)]
rootfs: Option<String>,
/// Update the modification time on FILE if new extensions were downloaded
#[arg(long)]
touch_if_changed: Option<String>,
},
/// 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<String>,
/// Rootfs to use for resolving package system configuration
#[arg(long)]
source_root: Option<String>,
/// Container authentication file
#[arg(long)]
authfile: Option<String>,
/// OSTree repository to use for ostree-layers and ostree-override-layers
#[arg(long)]
layer_repo: Option<String>,
/// 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<String>,
/// Output format
#[arg(long, default_value = "ociarchive")]
format: Option<String>,
/// 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<String>,
/// JSON-formatted lockfile; can be specified multiple times
#[arg(long)]
lockfile: Vec<String>,
/// 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<String>,
/// Path to container image configuration in JSON format
#[arg(long)]
image_config: Option<String>,
/// Update the timestamp or create this file on changes
#[arg(long)]
touch_if_changed: Option<String>,
/// Number of times to retry copying an image to remote destination
#[arg(long)]
copy_retry_times: Option<u32>,
/// Maximum number of layers to use
#[arg(long)]
max_layers: Option<u32>,
},
/// 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<String>,
/// Output path for rootfs
cachedir: Option<String>,
/// Source root for package system configuration
#[arg(long)]
output: Option<String>,
/// Target directory
target: Option<String>,
source_root: Option<String>,
/// Rootfs to use for resolving package system configuration
#[arg(long)]
source_root_rw: Option<String>,
},
/// Generate a "chunked" OCI archive from an input rootfs
BuildChunkedOci {
/// Path to the source root filesystem tree
#[arg(long)]
rootfs: Option<String>,
/// Use the provided image (in containers-storage)
#[arg(long)]
from: Option<String>,
/// 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<String>,
/// Maximum number of layers to use
#[arg(long)]
max_layers: Option<u32>,
/// Tag to use for output image, or latest if unset
#[arg(long, default_value = "latest")]
reference: Option<String>,
/// 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<String>,
/// Additional labels for the container
#[arg(short, long)]
label: Vec<String>,
/// Path to container image configuration in JSON format
#[arg(long)]
image_config: Option<String>,
/// Override the architecture
#[arg(long)]
arch: Option<String>,
/// Propagate an OSTree commit metadata key to container label
#[arg(long)]
copymeta: Vec<String>,
/// Propagate an optionally-present OSTree commit metadata key to container label
#[arg(long)]
copymeta_opt: Vec<String>,
/// Corresponds to the Dockerfile CMD instruction
#[arg(long)]
cmd: Option<String>,
/// Maximum number of container image layers
#[arg(long)]
max_layers: Option<u32>,
/// The encapsulated container format version; must be 1 or 2
#[arg(long, default_value = "1")]
format_version: Option<String>,
/// Output content metadata as JSON
#[arg(long)]
write_contentmeta_json: Option<String>,
/// Compare OCI layers of current build with another(imgref)
#[arg(long)]
compare_with_build: Option<String>,
/// Prevent a change in packing structure by taking a previous build metadata
#[arg(long)]
previous_build_manifest: Option<String>,
},
}