55use anyhow:: { Context , Result , bail} ;
66use camino:: Utf8PathBuf ;
77use chrono:: { DateTime , Utc } ;
8- use clap:: { CommandFactory , Parser } ;
9- use semver:: Version ;
10- use tufaceous_artifact:: {
11- ArtifactKind , ArtifactVersion , ArtifactsDocument , KnownArtifactKind ,
12- } ;
8+ use clap:: Parser ;
9+ use tufaceous_artifact:: { ArtifactsDocument , KnownArtifactKind } ;
1310use tufaceous_lib:: assemble:: { ArtifactManifest , OmicronRepoAssembler } ;
14- use tufaceous_lib:: { AddArtifact , ArchiveExtractor , Key , OmicronRepo } ;
11+ use tufaceous_lib:: { ArchiveExtractor , Key , OmicronRepo } ;
1512
1613#[ derive( Debug , Parser ) ]
1714pub struct Args {
@@ -38,117 +35,44 @@ pub struct Args {
3835impl Args {
3936 /// Executes these arguments.
4037 pub async fn exec ( self , log : & slog:: Logger ) -> Result < ( ) > {
41- let repo_path = match self . repo {
42- Some ( repo) => repo,
43- None => std:: env:: current_dir ( ) ?. try_into ( ) ?,
44- } ;
45-
4638 match self . command {
47- // TODO-cleanup: we no longer use the init and add commands in
48- // production. We should get rid of these options and direct users
49- // towards assemble. (If necessary, we should build tooling for
50- // making it easy to build up a manifest that can then be
51- // assembled.)
52- Command :: Init { system_version, no_generate_key } => {
53- let keys = maybe_generate_keys ( self . keys , no_generate_key) ?;
54- let root =
55- tufaceous_lib:: root:: new_root ( keys. clone ( ) , self . expiry )
56- . await ?;
57-
58- let repo = OmicronRepo :: initialize (
59- log,
60- & repo_path,
61- system_version,
62- keys,
63- root,
64- self . expiry ,
65- true ,
66- )
67- . await ?;
68- slog:: info!(
69- log,
70- "Initialized TUF repository in {}" ,
71- repo. repo_path( )
72- ) ;
73- Ok ( ( ) )
74- }
75- Command :: Add {
76- kind,
77- allow_unknown_kinds,
78- path,
79- name,
80- version,
39+ Command :: Assemble {
40+ manifest_path,
41+ output_path,
42+ build_dir,
43+ no_generate_key,
44+ skip_all_present,
8145 allow_non_semver,
46+ no_installinator_document,
8247 } => {
83- if !allow_unknown_kinds {
84- // Try converting kind to a known kind.
85- if kind. to_known ( ) . is_none ( ) {
86- // Simulate a failure to parse (though ideally there would
87- // be a way to also specify the underlying error -- there
88- // doesn't appear to be a public API to do so in clap 4).
89- let mut error = clap:: Error :: new (
90- clap:: error:: ErrorKind :: ValueValidation ,
91- )
92- . with_cmd ( & Args :: command ( ) ) ;
93- error. insert (
94- clap:: error:: ContextKind :: InvalidArg ,
95- clap:: error:: ContextValue :: String (
96- "<KIND>" . to_owned ( ) ,
97- ) ,
98- ) ;
99- error. insert (
100- clap:: error:: ContextKind :: InvalidValue ,
101- clap:: error:: ContextValue :: String ( kind. to_string ( ) ) ,
102- ) ;
103- error. exit ( ) ;
104- }
48+ // The filename must end with "zip".
49+ if output_path. extension ( ) != Some ( "zip" ) {
50+ bail ! ( "output path `{output_path}` must end with .zip" ) ;
10551 }
10652
53+ let manifest = ArtifactManifest :: from_path ( & manifest_path)
54+ . context ( "error reading manifest" ) ?;
10755 if !allow_non_semver {
108- if let Err ( error) = version. as_str ( ) . parse :: < Version > ( ) {
109- let error = Args :: command ( ) . error (
110- clap:: error:: ErrorKind :: ValueValidation ,
111- format ! (
112- "version `{version}` is not valid semver \
113- (pass in --allow-non-semver to override): {error}"
114- ) ,
115- ) ;
116- error. exit ( ) ;
117- }
56+ manifest. verify_all_semver ( ) ?;
57+ }
58+ if !skip_all_present {
59+ manifest. verify_all_present ( ) ?;
11860 }
11961
120- let repo = OmicronRepo :: load_untrusted_ignore_expiration (
121- log, & repo_path,
122- )
123- . await ?;
124- let mut editor = repo. into_editor ( ) . await ?;
125-
126- let new_artifact =
127- AddArtifact :: from_path ( kind, name, version, path) ?;
128-
129- editor
130- . add_artifact ( & new_artifact)
131- . context ( "error adding artifact" ) ?;
132- editor. sign_and_finish ( self . keys , self . expiry , true ) . await ?;
133- println ! (
134- "added {} {}, version {}" ,
135- new_artifact. kind( ) ,
136- new_artifact. name( ) ,
137- new_artifact. version( )
62+ let keys = maybe_generate_keys ( self . keys , no_generate_key) ?;
63+ let mut assembler = OmicronRepoAssembler :: new (
64+ log,
65+ manifest,
66+ keys,
67+ self . expiry ,
68+ !no_installinator_document,
69+ output_path,
13870 ) ;
139- Ok ( ( ) )
140- }
141- Command :: Archive { output_path } => {
142- // The filename must end with "zip".
143- if output_path. extension ( ) != Some ( "zip" ) {
144- bail ! ( "output path `{output_path}` must end with .zip" ) ;
71+ if let Some ( dir) = build_dir {
72+ assembler. set_build_dir ( dir) ;
14573 }
14674
147- let repo = OmicronRepo :: load_untrusted_ignore_expiration (
148- log, & repo_path,
149- )
150- . await ?;
151- repo. archive ( & output_path) ?;
75+ assembler. build ( ) . await ?;
15276
15377 Ok ( ( ) )
15478 }
@@ -166,7 +90,7 @@ impl Args {
16690 . with_context ( || {
16791 format ! (
16892 "error loading extracted repository at `{dest}` \
169- (extracted files are still available)"
93+ (extracted files are still available)"
17094 )
17195 } ) ?;
17296 let artifacts =
@@ -207,46 +131,6 @@ impl Args {
207131 } ) ?;
208132 }
209133
210- Ok ( ( ) )
211- }
212- Command :: Assemble {
213- manifest_path,
214- output_path,
215- build_dir,
216- no_generate_key,
217- skip_all_present,
218- allow_non_semver,
219- no_installinator_document,
220- } => {
221- // The filename must end with "zip".
222- if output_path. extension ( ) != Some ( "zip" ) {
223- bail ! ( "output path `{output_path}` must end with .zip" ) ;
224- }
225-
226- let manifest = ArtifactManifest :: from_path ( & manifest_path)
227- . context ( "error reading manifest" ) ?;
228- if !allow_non_semver {
229- manifest. verify_all_semver ( ) ?;
230- }
231- if !skip_all_present {
232- manifest. verify_all_present ( ) ?;
233- }
234-
235- let keys = maybe_generate_keys ( self . keys , no_generate_key) ?;
236- let mut assembler = OmicronRepoAssembler :: new (
237- log,
238- manifest,
239- keys,
240- self . expiry ,
241- !no_installinator_document,
242- output_path,
243- ) ;
244- if let Some ( dir) = build_dir {
245- assembler. set_build_dir ( dir) ;
246- }
247-
248- assembler. build ( ) . await ?;
249-
250134 Ok ( ( ) )
251135 }
252136 }
@@ -255,60 +139,6 @@ impl Args {
255139
256140#[ derive( Debug , Parser ) ]
257141enum Command {
258- /// Create a new rack update TUF repository
259- Init {
260- /// The system version.
261- system_version : Version ,
262-
263- /// Disable random key generation and exit if no keys are provided
264- #[ clap( long) ]
265- no_generate_key : bool ,
266- } ,
267- Add {
268- /// The kind of artifact this is.
269- kind : ArtifactKind ,
270-
271- /// Allow artifact kinds that aren't known to tufaceous
272- #[ clap( long) ]
273- allow_unknown_kinds : bool ,
274-
275- /// Path to the artifact.
276- path : Utf8PathBuf ,
277-
278- /// Override the name for this artifact (default: filename with extension stripped)
279- #[ clap( long) ]
280- name : Option < String > ,
281-
282- /// Artifact version.
283- ///
284- /// This is required to be semver by default, but can be overridden with
285- /// --allow-non-semver.
286- version : ArtifactVersion ,
287-
288- /// Allow versions to be non-semver.
289- ///
290- /// Transitional option for v13 -> v14. After v14, versions will be
291- /// allowed to be non-semver by default.
292- #[ clap( long) ]
293- allow_non_semver : bool ,
294- } ,
295- /// Archives this repository to a zip file.
296- Archive {
297- /// The path to write the archive to (must end with .zip).
298- output_path : Utf8PathBuf ,
299- } ,
300- /// Validates and extracts a repository created by the `archive` command.
301- Extract {
302- /// The file to extract.
303- archive_file : Utf8PathBuf ,
304-
305- /// The destination to extract the file to.
306- dest : Utf8PathBuf ,
307-
308- /// Indicate that the file does not contain an installinator document.
309- #[ clap( long) ]
310- no_installinator_document : bool ,
311- } ,
312142 /// Assembles a repository from a provided manifest.
313143 Assemble {
314144 /// Path to artifact manifest.
@@ -342,6 +172,18 @@ enum Command {
342172 #[ clap( long) ]
343173 no_installinator_document : bool ,
344174 } ,
175+ /// Validates and extracts a repository created by the `assemble` command.
176+ Extract {
177+ /// The file to extract.
178+ archive_file : Utf8PathBuf ,
179+
180+ /// The destination to extract the file to.
181+ dest : Utf8PathBuf ,
182+
183+ /// Indicate that the file does not contain an installinator document.
184+ #[ clap( long) ]
185+ no_installinator_document : bool ,
186+ } ,
345187}
346188
347189fn maybe_generate_keys (
0 commit comments