Skip to content

Commit 6aa4387

Browse files
manojmahapatraSebastien Stormacq
andauthored
feat: Added support for apple container (#647)
<!--- Provide a general summary of your changes in the Title above --> ## Issue \# <!--- If it fixes an issue, please link to the issue here --> #644 ## Description of changes <!--- Why is this change required? What problem does it solve? --> - Added `--container-cli` option to the archive SwiftPM plugin so users can choose Docker (default) or Apple container - Implemented Apple container support by switching pull/run commands to `container image pull` and `container run` - Documented the Apple container path in readme.md - swift test passed ## New/existing dependencies impact assessment, if applicable <!--- No new dependencies were added to this change. --> <!--- If any dependency was added / modified / removed, THIRD-PARTY-LICENSES must be updated accordingly. --> N/A ## Conventional Commits <!--- Please use conventional commits to let us know what kind of change this is.--> <!--- More info can be found here: https://www.conventionalcommits.org/en/v1.0.0/--> By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --------- Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
1 parent 22dd701 commit 6aa4387

2 files changed

Lines changed: 114 additions & 24 deletions

File tree

Plugins/AWSLambdaPackager/Plugin.swift

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct AWSLambdaPackager: CommandPlugin {
5555
products: configuration.products,
5656
toolsProvider: { name in try context.tool(named: name).url },
5757
outputDirectory: configuration.outputDirectory,
58+
containerCLI: configuration.containerCLI,
5859
baseImage: configuration.baseDockerImage,
5960
disableDockerImageUpdate: configuration.disableDockerImageUpdate,
6061
buildConfiguration: configuration.buildConfiguration,
@@ -85,35 +86,39 @@ struct AWSLambdaPackager: CommandPlugin {
8586
products: [Product],
8687
toolsProvider: (String) throws -> URL,
8788
outputDirectory: URL,
89+
containerCLI: ContainerCLI,
8890
baseImage: String,
8991
disableDockerImageUpdate: Bool,
9092
buildConfiguration: PackageManager.BuildConfiguration,
9193
verboseLogging: Bool
9294
) throws -> [LambdaProduct: URL] {
93-
let dockerToolPath = try toolsProvider("docker")
95+
let containerCLIPath = try toolsProvider(containerCLI.executableName)
9496

9597
print("-------------------------------------------------------------------------")
96-
print("building \"\(packageIdentity)\" in docker")
98+
print("building \"\(packageIdentity)\" in \(containerCLI.displayName)")
9799
print("-------------------------------------------------------------------------")
98100

99101
if !disableDockerImageUpdate {
100-
// update the underlying docker image, if necessary
101-
print("updating \"\(baseImage)\" docker image")
102+
// update the underlying image, if necessary
103+
print("updating \"\(baseImage)\" image")
102104
try Utils.execute(
103-
executable: dockerToolPath,
104-
arguments: ["pull", baseImage],
105+
executable: containerCLIPath,
106+
arguments: containerCLI.pullArguments(image: baseImage),
105107
logLevel: verboseLogging ? .debug : .output
106108
)
107109
}
108110

109111
// get the build output path
110112
let buildOutputPathCommand = "swift build -c \(buildConfiguration.rawValue) --show-bin-path"
111113
let dockerBuildOutputPath = try Utils.execute(
112-
executable: dockerToolPath,
113-
arguments: [
114-
"run", "--rm", "-v", "\(packageDirectory.path()):/workspace", "-w", "/workspace", baseImage, "bash",
115-
"-cl", buildOutputPathCommand,
116-
],
114+
executable: containerCLIPath,
115+
arguments: containerCLI.runArguments(
116+
baseImage: baseImage,
117+
workingDirectory: "/workspace",
118+
mounts: ["\(packageDirectory.path()):/workspace"],
119+
env: nil,
120+
command: buildOutputPathCommand
121+
),
117122
logLevel: verboseLogging ? .debug : .silent
118123
)
119124
guard let buildPathOutput = dockerBuildOutputPath.split(separator: "\n").last else {
@@ -135,21 +140,26 @@ struct AWSLambdaPackager: CommandPlugin {
135140
// just like Package.swift's examples assume ../.., we assume we are two levels below the root project
136141
let slice = packageDirectory.pathComponents.suffix(2)
137142
try Utils.execute(
138-
executable: dockerToolPath,
139-
arguments: [
140-
"run", "--rm", "--env", "LAMBDA_USE_LOCAL_DEPS=\(localPath)", "-v",
141-
"\(packageDirectory.path())../..:/workspace", "-w",
142-
"/workspace/\(slice.joined(separator: "/"))", baseImage, "bash", "-cl", buildCommand,
143-
],
143+
executable: containerCLIPath,
144+
arguments: containerCLI.runArguments(
145+
baseImage: baseImage,
146+
workingDirectory: "/workspace/\(slice.joined(separator: "/"))",
147+
mounts: ["\(packageDirectory.path())../..:/workspace"],
148+
env: ["LAMBDA_USE_LOCAL_DEPS": localPath],
149+
command: buildCommand
150+
),
144151
logLevel: verboseLogging ? .debug : .output
145152
)
146153
} else {
147154
try Utils.execute(
148-
executable: dockerToolPath,
149-
arguments: [
150-
"run", "--rm", "-v", "\(packageDirectory.path()):/workspace", "-w", "/workspace", baseImage,
151-
"bash", "-cl", buildCommand,
152-
],
155+
executable: containerCLIPath,
156+
arguments: containerCLI.runArguments(
157+
baseImage: baseImage,
158+
workingDirectory: "/workspace",
159+
mounts: ["\(packageDirectory.path()):/workspace"],
160+
env: nil,
161+
command: buildCommand
162+
),
153163
logLevel: verboseLogging ? .debug : .output
154164
)
155165
}
@@ -304,7 +314,7 @@ struct AWSLambdaPackager: CommandPlugin {
304314
"""
305315
OVERVIEW: A SwiftPM plugin to build and package your lambda function.
306316
307-
REQUIREMENTS: To use this plugin, you must have docker installed and started.
317+
REQUIREMENTS: To use this plugin, you must have docker or container installed and started.
308318
309319
USAGE: swift package --allow-network-connections docker archive
310320
[--help] [--verbose]
@@ -314,6 +324,7 @@ struct AWSLambdaPackager: CommandPlugin {
314324
[--swift-version <version>]
315325
[--base-docker-image <docker_image_name>]
316326
[--disable-docker-image-update]
327+
[--container-cli <docker | container>]
317328
318329
319330
OPTIONS:
@@ -331,12 +342,72 @@ struct AWSLambdaPackager: CommandPlugin {
331342
(default : swift-<version>:amazonlinux2)
332343
This parameter cannot be used when --swift-version is specified.
333344
--disable-docker-image-update Do not attempt to update the docker image
345+
--container-cli <name> The container CLI to use (docker or container)
346+
(default is docker)
334347
--help Show help information.
335348
"""
336349
)
337350
}
338351
}
339352

353+
@available(macOS 15.0, *)
354+
private enum ContainerCLI: String, CustomStringConvertible {
355+
case docker
356+
case container
357+
358+
var executableName: String {
359+
self.rawValue
360+
}
361+
362+
var displayName: String {
363+
self.rawValue
364+
}
365+
366+
static func parse(_ value: String?) throws -> Self {
367+
guard let value else {
368+
return .docker
369+
}
370+
371+
guard let tool = ContainerCLI(rawValue: value.lowercased()) else {
372+
throw Errors.invalidArgument("invalid container CLI '\(value)'. Use 'docker' or 'container'.")
373+
}
374+
return tool
375+
}
376+
377+
func pullArguments(image: String) -> [String] {
378+
switch self {
379+
case .docker:
380+
return ["pull", image]
381+
case .container:
382+
return ["image", "pull", image]
383+
}
384+
}
385+
386+
func runArguments(
387+
baseImage: String,
388+
workingDirectory: String,
389+
mounts: [String],
390+
env: [String: String]?,
391+
command: String
392+
) -> [String] {
393+
var args: [String] = ["run", "--rm"]
394+
for mount in mounts {
395+
args += ["-v", mount]
396+
}
397+
if let env {
398+
for (key, value) in env.sorted(by: { $0.key < $1.key }) {
399+
args += ["--env", "\(key)=\(value)"]
400+
}
401+
}
402+
args += ["-w", workingDirectory, baseImage, "bash", "-cl", command]
403+
return args
404+
}
405+
406+
var description: String {
407+
self.rawValue
408+
}
409+
}
410+
340411
@available(macOS 15.0, *)
341412
private struct Configuration: CustomStringConvertible {
342413
public let help: Bool
@@ -347,6 +418,7 @@ private struct Configuration: CustomStringConvertible {
347418
public let verboseLogging: Bool
348419
public let baseDockerImage: String
349420
public let disableDockerImageUpdate: Bool
421+
public let containerCLI: ContainerCLI
350422

351423
public init(
352424
context: PluginContext,
@@ -360,6 +432,7 @@ private struct Configuration: CustomStringConvertible {
360432
let swiftVersionArgument = argumentExtractor.extractOption(named: "swift-version")
361433
let baseDockerImageArgument = argumentExtractor.extractOption(named: "base-docker-image")
362434
let disableDockerImageUpdateArgument = argumentExtractor.extractFlag(named: "disable-docker-image-update") > 0
435+
let containerCliArgument = argumentExtractor.extractOption(named: "container-cli")
363436
let helpArgument = argumentExtractor.extractFlag(named: "help") > 0
364437

365438
// help required ?
@@ -415,6 +488,9 @@ private struct Configuration: CustomStringConvertible {
415488
baseDockerImageArgument.first ?? "swift:\(swiftVersion.map { $0 + "-" } ?? "")amazonlinux2"
416489

417490
self.disableDockerImageUpdate = disableDockerImageUpdateArgument
491+
self.containerCLI = try ContainerCLI.parse(
492+
containerCliArgument.first
493+
)
418494

419495
if self.verboseLogging {
420496
print("-------------------------------------------------------------------------")
@@ -432,9 +508,11 @@ private struct Configuration: CustomStringConvertible {
432508
buildConfiguration: \(self.buildConfiguration)
433509
baseDockerImage: \(self.baseDockerImage)
434510
disableDockerImageUpdate: \(self.disableDockerImageUpdate)
511+
containerCLI: \(self.containerCLI)
435512
}
436513
"""
437514
}
515+
438516
}
439517

440518
private enum ProcessLogLevel: Comparable {

readme.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ Swift AWS Lambda Runtime was designed to make building Lambda functions in Swift
3535

3636
- When developing on macOS, be sure you use macOS 15 (Sequoia) or a more recent macOS version.
3737

38-
- To build and archive your Lambda function, you need to [install docker](https://docs.docker.com/desktop/install/mac-install/).
38+
- To build and archive your Lambda function, you need to install [docker](https://docs.docker.com/desktop/install/mac-install/) or Apple [container](https://github.com/apple/container).
3939

4040
- To deploy the Lambda function and invoke it, you must have [an AWS account](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-creating.html) and [install and configure the `aws` command line](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).
4141

@@ -137,6 +137,18 @@ The runtime comes with a plugin to compile on Amazon Linux and create a ZIP arch
137137
swift package archive --allow-network-connections docker
138138
```
139139

140+
By default, it runs on `docker` but it also allows you to build with [Apple container](https://github.com/apple/container) (it requires disabling the sandbox):
141+
142+
```bash
143+
# Both --disable-sandbox and
144+
# --allow-network-connections are required
145+
# until https://github.com/swiftlang/swift-package-manager/issues/9763 is fixed
146+
swift package --disable-sandbox \
147+
--allow-network-connections docker \
148+
archive \
149+
--container-cli container
150+
```
151+
140152
If there is no error, the ZIP archive is ready to deploy.
141153
The ZIP file is located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip`
142154

0 commit comments

Comments
 (0)