@@ -42,7 +42,7 @@ use std::sync::Arc;
4242
4343use anyhow:: { Context as _, Result } ;
4444use clap:: { Parser , Subcommand , ValueEnum } ;
45- #[ cfg( feature = "oci" ) ]
45+ #[ cfg( any ( feature = "oci" , feature = "ostree" ) ) ]
4646use comfy_table:: { Table , presets:: UTF8_FULL } ;
4747#[ cfg( any( feature = "oci" , feature = "http" ) ) ]
4848use indicatif:: { MultiProgress , ProgressBar , ProgressStyle } ;
@@ -501,6 +501,76 @@ enum OciCommand {
501501 } ,
502502}
503503
504+ #[ cfg( feature = "ostree" ) ]
505+ #[ derive( Debug , Subcommand ) ]
506+ enum OstreeCommand {
507+ PullLocal {
508+ ostree_repo_path : PathBuf ,
509+ /// Ostree ref name or commit ID (64-character hex)
510+ ostree_ref : String ,
511+ #[ clap( long) ]
512+ base_name : Option < String > ,
513+ } ,
514+ Pull {
515+ ostree_repo_url : String ,
516+ /// Ostree ref name or commit ID (64-character hex)
517+ ostree_ref : String ,
518+ #[ clap( long) ]
519+ base_name : Option < String > ,
520+ } ,
521+ /// Mount an ostree commit's composefs EROFS at the given mountpoint
522+ Mount {
523+ /// Ostree commit ref or commit ID
524+ commit : String ,
525+ /// Target mountpoint
526+ mountpoint : String ,
527+ /// Writable upper layer directory for overlayfs
528+ #[ arg( long, requires = "workdir" ) ]
529+ upperdir : Option < PathBuf > ,
530+ /// Work directory for overlayfs (required with --upperdir)
531+ #[ arg( long, requires = "upperdir" ) ]
532+ workdir : Option < PathBuf > ,
533+ /// Mount read-write (requires --upperdir)
534+ #[ arg( long, requires = "upperdir" ) ]
535+ read_write : bool ,
536+ } ,
537+ /// Dump the filesystem of an ostree commit as a composefs dumpfile to stdout
538+ Dump {
539+ /// Ostree commit ref name
540+ commit_name : String ,
541+ } ,
542+ /// Compute the composefs image ID of an ostree commit
543+ ComputeId {
544+ /// Ostree commit ref name
545+ commit_name : String ,
546+ } ,
547+ /// Show the contents of an ostree commit
548+ Inspect {
549+ /// Ostree ref name, commit ID, or commit ID prefix
550+ source : String ,
551+ /// Print only the commit metadata key-value pairs
552+ #[ clap( long) ]
553+ metadata : bool ,
554+ } ,
555+ /// Tag an ostree commit with a name
556+ ///
557+ /// The source can be an ostree commit checksum or an existing ref name.
558+ Tag {
559+ /// Ostree commit checksum (hex) or existing ref name
560+ source : String ,
561+ /// Tag name to assign
562+ name : String ,
563+ } ,
564+ /// Remove a named ostree reference
565+ Untag {
566+ /// Tag name to remove
567+ name : String ,
568+ } ,
569+ /// List all ostree commits in the repository
570+ #[ clap( name = "images" ) ]
571+ ListCommits ,
572+ }
573+
504574/// Common options for reading a filesystem from a path
505575#[ derive( Debug , Parser ) ]
506576struct FsReadOptions {
@@ -570,6 +640,11 @@ enum Command {
570640 #[ clap( subcommand) ]
571641 cmd : OciCommand ,
572642 } ,
643+ #[ cfg( feature = "ostree" ) ]
644+ Ostree {
645+ #[ clap( subcommand) ]
646+ cmd : OstreeCommand ,
647+ } ,
573648 /// Mounts a composefs image, possibly enforcing fsverity of the image
574649 Mount {
575650 /// the name of the image to mount, either an fs-verity hash or prefixed with 'ref/'
@@ -1566,6 +1641,111 @@ where
15661641 unreachable ! ( "oci varlink is handled before opening a repository" ) ;
15671642 }
15681643 } ,
1644+ #[ cfg( feature = "ostree" ) ]
1645+ Command :: Ostree { cmd : ostree_cmd } => match ostree_cmd {
1646+ OstreeCommand :: PullLocal {
1647+ ref ostree_repo_path,
1648+ ref ostree_ref,
1649+ base_name,
1650+ } => {
1651+ eprintln ! ( "Fetching {ostree_ref}" ) ;
1652+ let ( verity, stats) = composefs_ostree:: pull_local (
1653+ & repo,
1654+ ostree_repo_path,
1655+ ostree_ref,
1656+ base_name. as_deref ( ) ,
1657+ )
1658+ . await ?;
1659+
1660+ let image_id = composefs_ostree:: get_image_ref ( & repo, & stats. commit_id ) ?;
1661+ println ! ( "commit {}" , stats. commit_id) ;
1662+ println ! ( "verity {}" , verity. to_hex( ) ) ;
1663+ println ! ( "image {}" , image_id. to_hex( ) ) ;
1664+ if !composefs_ostree:: is_commit_id ( ostree_ref) {
1665+ println ! ( "tagged {ostree_ref}" ) ;
1666+ }
1667+ println ! (
1668+ "objects {} metadata + {} files fetched" ,
1669+ stats. metadata_fetched, stats. files_fetched
1670+ ) ;
1671+ }
1672+ OstreeCommand :: Pull {
1673+ ref ostree_repo_url,
1674+ ref ostree_ref,
1675+ base_name,
1676+ } => {
1677+ eprintln ! ( "Fetching {ostree_ref}" ) ;
1678+ let ( verity, stats) = composefs_ostree:: pull (
1679+ & repo,
1680+ ostree_repo_url,
1681+ ostree_ref,
1682+ base_name. as_deref ( ) ,
1683+ )
1684+ . await ?;
1685+
1686+ let image_id = composefs_ostree:: get_image_ref ( & repo, & stats. commit_id ) ?;
1687+ println ! ( "commit {}" , stats. commit_id) ;
1688+ println ! ( "verity {}" , verity. to_hex( ) ) ;
1689+ println ! ( "image {}" , image_id. to_hex( ) ) ;
1690+ if !composefs_ostree:: is_commit_id ( ostree_ref) {
1691+ println ! ( "tagged {ostree_ref}" ) ;
1692+ }
1693+ println ! (
1694+ "objects {} metadata + {} files fetched" ,
1695+ stats. metadata_fetched, stats. files_fetched
1696+ ) ;
1697+ }
1698+ OstreeCommand :: Mount {
1699+ ref commit,
1700+ ref mountpoint,
1701+ ref upperdir,
1702+ ref workdir,
1703+ read_write,
1704+ } => {
1705+ let mount_options =
1706+ get_mount_options ( upperdir. as_deref ( ) , workdir. as_deref ( ) , read_write) ?;
1707+ let image_id = composefs_ostree:: get_image_ref ( & repo, commit) ?;
1708+ repo. mount_at ( & image_id. to_hex ( ) , mountpoint. as_str ( ) , & mount_options) ?;
1709+ }
1710+ OstreeCommand :: Dump { ref commit_name } => {
1711+ let fs = composefs_ostree:: create_filesystem ( & repo, commit_name) ?;
1712+ fs. print_dumpfile ( ) ?;
1713+ }
1714+ OstreeCommand :: ComputeId { ref commit_name } => {
1715+ let image_id = composefs_ostree:: ensure_ostree_erofs ( & repo, commit_name) ?;
1716+ println ! ( "{}" , image_id. to_hex( ) ) ;
1717+ }
1718+ OstreeCommand :: Inspect {
1719+ ref source,
1720+ metadata,
1721+ } => {
1722+ composefs_ostree:: inspect ( & repo, source, metadata) ?;
1723+ }
1724+ OstreeCommand :: Tag {
1725+ ref source,
1726+ ref name,
1727+ } => {
1728+ composefs_ostree:: tag ( & repo, source, name) ?;
1729+ println ! ( "Tagged {source} as {name}" ) ;
1730+ }
1731+ OstreeCommand :: Untag { ref name } => {
1732+ composefs_ostree:: untag ( & repo, name) ?;
1733+ }
1734+ OstreeCommand :: ListCommits => {
1735+ let commits = composefs_ostree:: list_commits ( & repo) ?;
1736+ if commits. is_empty ( ) {
1737+ println ! ( "No ostree commits found" ) ;
1738+ } else {
1739+ let mut table = Table :: new ( ) ;
1740+ table. load_preset ( UTF8_FULL ) ;
1741+ table. set_header ( [ "NAME" , "COMMIT" ] ) ;
1742+ for c in commits {
1743+ table. add_row ( [ c. name . as_str ( ) , & c. commit_id ] ) ;
1744+ }
1745+ println ! ( "{table}" ) ;
1746+ }
1747+ }
1748+ } ,
15691749 Command :: CreateImage {
15701750 fs_opts,
15711751 ref image_name,
0 commit comments