@@ -28,85 +28,111 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
2828 abstract: " Build and publish a container image "
2929 )
3030
31- @Option ( help: " Default registry for references which do not specify a registry " )
32- private var defaultRegistry : String ?
33-
34- @Option ( help: " Repository path " )
35- private var repository : String ?
36-
3731 @Argument ( help: " Executable to package " )
3832 private var executable : String
3933
40- @Option ( help: " Resource bundle directory " )
41- private var resources : [ String ] = [ ]
34+ /// Options controlling how the source and destination repositories
35+ struct RepositoryOptions : ParsableArguments {
36+ @Option ( help: " Default registry for image references which do not specify one " )
37+ var defaultRegistry : String ?
4238
43- @Option (
44- help: ArgumentHelp (
45- " [DEPRECATED] Default username, used if there are no matching entries in .netrc. Use --default-username instead. " ,
46- visibility: . private
47- )
48- )
49- private var username : String ?
39+ @Option ( help: " Destination image reference " )
40+ var repository : String ?
41+
42+ @Option ( help: " Destination image tag " )
43+ var tag : String ?
44+
45+ @Option ( help: " Base image reference " )
46+ var from : String ?
47+ }
48+
49+ /// Options controlling how the destination image is built
50+ struct ImageBuildOptions : ParsableArguments {
51+ @Option ( help: " Directory of resources to include in the image " )
52+ var resources : [ String ] = [ ]
53+ }
54+
55+ @OptionGroup ( title: " Source and destination repository options " )
56+ var repositoryOptions : RepositoryOptions
57+
58+ // Image configuration options
59+ struct ImageConfigurationOptions : ParsableArguments {
60+ @Option ( help: " CPU architecture " )
61+ var architecture : String ?
62+
63+ @Option ( help: " Operating system " )
64+ var os : String ?
65+ }
66+
67+ @OptionGroup ( title: " Image build options " )
68+ var imageBuildOptions : ImageBuildOptions
5069
51- @Option ( help : " Default username, used if there are no matching entries in .netrc " )
52- private var defaultUsername : String ?
70+ @OptionGroup ( title : " Image configuration options " )
71+ var imageConfigurationOptions : ImageConfigurationOptions
5372
54- @Option (
55- help: ArgumentHelp (
56- " [DEPRECATED] Default password, used if there are no matching entries in .netrc. Use --default-password instead. " ,
57- visibility: . private
73+ /// Options controlling how containertool authenticates to the registry
74+ struct AuthenticationOptions : ParsableArguments {
75+ @Option (
76+ help: ArgumentHelp (
77+ " [DEPRECATED] Default username, used if there are no matching entries in .netrc. Use --default-username instead. " ,
78+ visibility: . private
79+ )
5880 )
59- )
60- private var password : String ?
81+ var username : String ?
6182
62- @Option ( help: " Default password , used if there are no matching entries in .netrc " )
63- private var defaultPassword : String ?
83+ @Option ( help: " Default username , used if there are no matching entries in .netrc " )
84+ var defaultUsername : String ?
6485
65- @Flag ( name: . shortAndLong, help: " Verbose output " )
66- private var verbose : Bool = false
86+ @Option (
87+ help: ArgumentHelp (
88+ " [DEPRECATED] Default password, used if there are no matching entries in .netrc. Use --default-password instead. " ,
89+ visibility: . private
90+ )
91+ )
92+ var password : String ?
6793
68- @Option ( help: " Connect to the container registry using plaintext HTTP " )
69- private var allowInsecureHttp : AllowHTTP ?
94+ @Option ( help: " Default password, used if there are no matching entries in .netrc " )
95+ var defaultPassword : String ?
7096
71- @ Option ( help: " CPU architecture " )
72- private var architecture : String ?
97+ @ Flag ( inversion : . prefixedEnableDisable , exclusivity : . exclusive , help: " Load credentials from a netrc file " )
98+ var netrc : Bool = true
7399
74- @Option ( help: " Base image reference " )
75- private var from : String ?
100+ @Option ( help: " Specify the netrc file path " )
101+ var netrcFile : String ?
76102
77- @Option ( help: " Operating system " )
78- private var os : String ?
103+ @Option ( help: " Connect to the registry using plaintext HTTP " )
104+ var allowInsecureHttp : AllowHTTP ?
105+ }
79106
80- @Option ( help : " Tag for this manifest " )
81- private var tag : String ?
107+ @OptionGroup ( title : " Authentication options " )
108+ var authenticationOptions : AuthenticationOptions
82109
83- @Flag ( inversion: . prefixedEnableDisable, exclusivity: . exclusive, help: " Load credentials from a netrc file " )
84- private var netrc : Bool = true
110+ // General options
85111
86- @Option ( help: " Specify the netrc file path " )
87- private var netrcFile : String ?
112+ @Flag ( name : . shortAndLong , help: " Verbose output " )
113+ private var verbose : Bool = false
88114
89115 mutating func validate( ) throws {
90- if username != nil {
91- guard defaultUsername == nil else {
116+ if authenticationOptions . username != nil {
117+ guard authenticationOptions . defaultUsername == nil else {
92118 throw ValidationError (
93119 " --default-username and --username cannot be specified together. Please use --default-username only. "
94120 )
95121 }
96122
97123 log ( " Deprecation warning: --username is deprecated, please use --default-username instead. " )
98- defaultUsername = username
124+ authenticationOptions . defaultUsername = authenticationOptions . username
99125 }
100126
101- if password != nil {
102- guard defaultPassword == nil else {
127+ if authenticationOptions . password != nil {
128+ guard authenticationOptions . defaultPassword == nil else {
103129 throw ValidationError (
104130 " --default-password and --password cannot be specified together. Please use --default-password only. "
105131 )
106132 }
107133
108134 log ( " Deprecation warning: --password is deprecated, please use --default-password instead. " )
109- defaultPassword = password
135+ authenticationOptions . defaultPassword = authenticationOptions . password
110136 }
111137 }
112138
@@ -115,25 +141,25 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
115141
116142 let env = ProcessInfo . processInfo. environment
117143
118- let defaultRegistry = defaultRegistry ?? env [ " CONTAINERTOOL_DEFAULT_REGISTRY " ] ?? " docker.io "
119- guard let repository = repository ?? env [ " CONTAINERTOOL_REPOSITORY " ] else {
144+ let defaultRegistry = repositoryOptions . defaultRegistry ?? env [ " CONTAINERTOOL_DEFAULT_REGISTRY " ] ?? " docker.io "
145+ guard let repository = repositoryOptions . repository ?? env [ " CONTAINERTOOL_REPOSITORY " ] else {
120146 throw ValidationError (
121147 " Please specify the destination repository using --repository or CONTAINERTOOL_REPOSITORY "
122148 )
123149 }
124150
125- let username = defaultUsername ?? env [ " CONTAINERTOOL_DEFAULT_USERNAME " ]
126- let password = defaultPassword ?? env [ " CONTAINERTOOL_DEFAULT_PASSWORD " ]
127- let from = from ?? env [ " CONTAINERTOOL_BASE_IMAGE " ] ?? " swift:slim "
128- let os = os ?? env [ " CONTAINERTOOL_OS " ] ?? " linux "
151+ let username = authenticationOptions . defaultUsername ?? env [ " CONTAINERTOOL_DEFAULT_USERNAME " ]
152+ let password = authenticationOptions . defaultPassword ?? env [ " CONTAINERTOOL_DEFAULT_PASSWORD " ]
153+ let from = repositoryOptions . from ?? env [ " CONTAINERTOOL_BASE_IMAGE " ] ?? " swift:slim "
154+ let os = imageConfigurationOptions . os ?? env [ " CONTAINERTOOL_OS " ] ?? " linux "
129155
130156 // Try to detect the architecture of the application executable so a suitable base image can be selected.
131157 // This reduces the risk of accidentally creating an image which stacks an aarch64 executable on top of an x86_64 base image.
132158 let executableURL = URL ( fileURLWithPath: executable)
133159 let elfheader = try ELF . read ( at: executableURL)
134160
135161 let architecture =
136- architecture
162+ imageConfigurationOptions . architecture
137163 ?? env [ " CONTAINERTOOL_ARCHITECTURE " ]
138164 ?? elfheader? . ISA. containerArchitecture
139165 ?? " amd64 "
@@ -142,10 +168,12 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
142168 // MARK: Load netrc
143169
144170 let authProvider : AuthorizationProvider ?
145- if !netrc {
171+ if !authenticationOptions . netrc {
146172 authProvider = nil
147- } else if let netrcFile {
148- guard FileManager . default. fileExists ( atPath: netrcFile) else { throw " \( netrcFile) not found " }
173+ } else if let netrcFile = authenticationOptions. netrcFile {
174+ guard FileManager . default. fileExists ( atPath: netrcFile) else {
175+ throw " \( netrcFile) not found "
176+ }
149177 let customNetrc = URL ( fileURLWithPath: netrcFile)
150178 authProvider = try NetrcAuthorizationProvider ( customNetrc)
151179 } else {
@@ -166,15 +194,17 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
166194 } else {
167195 source = try await RegistryClient (
168196 registry: baseImage. registry,
169- insecure: allowInsecureHttp == . source || allowInsecureHttp == . both,
197+ insecure: authenticationOptions. allowInsecureHttp == . source
198+ || authenticationOptions. allowInsecureHttp == . both,
170199 auth: . init( username: username, password: password, auth: authProvider)
171200 )
172201 if verbose { log ( " Connected to source registry: \( baseImage. registry) " ) }
173202 }
174203
175204 let destination = try await RegistryClient (
176205 registry: destinationImage. registry,
177- insecure: allowInsecureHttp == . destination || allowInsecureHttp == . both,
206+ insecure: authenticationOptions. allowInsecureHttp == . destination
207+ || authenticationOptions. allowInsecureHttp == . both,
178208 auth: . init( username: username, password: password, auth: authProvider)
179209 )
180210
@@ -189,8 +219,8 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
189219 source: source,
190220 architecture: architecture,
191221 os: os,
192- resources: resources,
193- tag: tag,
222+ resources: imageBuildOptions . resources,
223+ tag: repositoryOptions . tag,
194224 verbose: verbose,
195225 executableURL: executableURL
196226 )
0 commit comments