@@ -38,6 +38,9 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
38
38
@Argument ( help: " Executable to package " )
39
39
private var executable : String
40
40
41
+ @Option ( help: " Resource bundle directory " )
42
+ private var resources : [ String ] = [ ]
43
+
41
44
@Option ( help: " Username " )
42
45
private var username : String ?
43
46
@@ -72,9 +75,6 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
72
75
let baseimage = try ImageReference ( fromString: from, defaultRegistry: defaultRegistry)
73
76
var destination_image = try ImageReference ( fromString: repository, defaultRegistry: defaultRegistry)
74
77
75
- let executableURL = URL ( fileURLWithPath: executable)
76
- let payload = try Data ( contentsOf: executableURL)
77
-
78
78
let authProvider : AuthorizationProvider ?
79
79
if !netrc {
80
80
authProvider = nil
@@ -87,8 +87,9 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
87
87
authProvider = try NetrcAuthorizationProvider ( defaultNetrc)
88
88
}
89
89
90
- // Create clients for the source and destination registries
91
- // The base image may be stored on a different registry, so two clients are needed.
90
+ // MARK: Create registry clients
91
+
92
+ // The base image may be stored on a different registry to the final destination, so two clients are needed.
92
93
// `scratch` is a special case and requires no source client.
93
94
let source : RegistryClient ?
94
95
if from == " scratch " {
@@ -113,7 +114,10 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
113
114
114
115
// MARK: Find the base image
115
116
116
- let elfheader = ELF . read ( [ UInt8] ( payload) )
117
+ // Try to detect the architecture of the application executable so a suitable base image can be selected.
118
+ // This reduces the risk of accidentally creating an image which stacks an aarch64 executable on top of an x86_64 base image.
119
+ let executableURL = URL ( fileURLWithPath: executable)
120
+ let elfheader = try ELF . read ( at: executableURL)
117
121
let architecture =
118
122
architecture
119
123
?? ProcessInfo . processInfo. environment [ " CONTAINERTOOL_ARCHITECTURE " ]
@@ -146,29 +150,39 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
146
150
if verbose { log ( " Using scratch as base image " ) }
147
151
}
148
152
149
- // MARK: Build the application layer
153
+ // MARK: Upload resource layers
150
154
151
- let payload_name = executableURL. lastPathComponent
152
- let tardiff = try tar ( [ UInt8] ( payload) , filename: payload_name)
153
- log ( " Built application layer " )
155
+ var resourceLayers : [ RegistryClient . ImageLayer ] = [ ]
156
+ for resourceDir in resources {
157
+ let resourceTardiff = try Archive ( ) . appendingRecursively ( atPath: resourceDir) . bytes
158
+ let resourceLayer = try await destination. uploadLayer (
159
+ repository: destination_image. repository,
160
+ contents: resourceTardiff
161
+ )
154
162
155
- // MARK: Upload the application layer
163
+ if verbose {
164
+ log ( " resource layer: \( resourceLayer. descriptor. digest) ( \( resourceLayer. descriptor. size) bytes) " )
165
+ }
166
+
167
+ resourceLayers. append ( resourceLayer)
168
+ }
156
169
157
- let application_layer = try await destination. uploadImageLayer (
170
+ // MARK: Upload the application layer
171
+ let applicationLayer = try await destination. uploadLayer (
158
172
repository: destination_image. repository,
159
- layer : Data ( tardiff )
173
+ contents : try Archive ( ) . appendingFile ( at : executableURL ) . bytes
160
174
)
161
-
162
- if verbose { log ( " application layer: \( application_layer. digest) ( \( application_layer. size) bytes) " ) }
175
+ if verbose {
176
+ log ( " application layer: \( applicationLayer. descriptor. digest) ( \( applicationLayer. descriptor. size) bytes) " )
177
+ }
163
178
164
179
// MARK: Create the application configuration
165
-
166
180
let timestamp = Date ( timeIntervalSince1970: 0 ) . ISO8601Format ( )
167
181
168
182
// Inherit the configuration of the base image - UID, GID, environment etc -
169
183
// and override the entrypoint.
170
184
var inherited_config = baseimage_config. config ?? . init( )
171
- inherited_config. Entrypoint = [ " / \( payload_name ) " ]
185
+ inherited_config. Entrypoint = [ " / \( executableURL . lastPathComponent ) " ]
172
186
inherited_config. Cmd = [ ]
173
187
inherited_config. WorkingDir = " / "
174
188
@@ -182,7 +196,9 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
182
196
// The diff_id is the digest of the _uncompressed_ layer archive.
183
197
// It is used by the runtime, which might not store the layers in
184
198
// the compressed form in which it received them from the registry.
185
- diff_ids: baseimage_config. rootfs. diff_ids + [ digest ( of: tardiff) ]
199
+ diff_ids: baseimage_config. rootfs. diff_ids
200
+ + resourceLayers. map { $0. diffID }
201
+ + [ applicationLayer. diffID]
186
202
) ,
187
203
history: [ . init( created: timestamp, created_by: " containertool " ) ]
188
204
)
@@ -200,7 +216,9 @@ enum AllowHTTP: String, ExpressibleByArgument, CaseIterable { case source, desti
200
216
schemaVersion: 2 ,
201
217
mediaType: " application/vnd.oci.image.manifest.v1+json " ,
202
218
config: config_blob,
203
- layers: baseimage_manifest. layers + [ application_layer]
219
+ layers: baseimage_manifest. layers
220
+ + resourceLayers. map { $0. descriptor }
221
+ + [ applicationLayer. descriptor]
204
222
)
205
223
206
224
// MARK: Upload base image
0 commit comments