Skip to content

Commit 3c5d217

Browse files
authored
Merge branch 'main' into ff-platform-dance
2 parents 18eabfc + 21e224e commit 3c5d217

34 files changed

+1232
-105
lines changed

.github/workflows/examples_matrix.yml renamed to .github/workflows/integration_tests.yml

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: ExamplesMatrix
1+
name: IntegrationTests
22

33
on:
44
workflow_call:
@@ -7,10 +7,22 @@ on:
77
type: string
88
description: "The name of the workflow used for the concurrency group."
99
required: true
10-
# examples:
11-
# type: sequence
12-
# description: "The examples to run."
13-
# required: true
10+
# We pass the list of examples here, but we can't pass an array as argument
11+
# Instead, we pass a String with a valid JSON array.
12+
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
13+
examples:
14+
type: string
15+
description: "The list of examples to run. Pass a String with a valid JSON array such as \"[ 'HelloWorld', 'APIGateway' ]\""
16+
required: true
17+
default: ""
18+
examples_enabled:
19+
type: boolean
20+
description: "Boolean to enable the compilation of examples. Defaults to true."
21+
default: true
22+
archive_plugin_enabled:
23+
type: boolean
24+
description: "Boolean to enable the test of the archive plugin. Defaults to true."
25+
default: true
1426
matrix_linux_command:
1527
type: string
1628
description: "The command of the current Swift version linux matrix job to execute."
@@ -26,15 +38,14 @@ concurrency:
2638
cancel-in-progress: true
2739

2840
jobs:
29-
linux:
30-
name: Example/${{ matrix.examples }} on Linux ${{ matrix.swift.swift_version }}
41+
test-examples:
42+
name: Test Examples/${{ matrix.examples }} on ${{ matrix.swift.swift_version }}
43+
if: ${{ inputs.examples_enabled }}
3144
runs-on: ubuntu-latest
3245
strategy:
3346
fail-fast: false
3447
matrix:
35-
# This should be passed as an argument in input. Can we pass arrays as argument ?
36-
examples: ["HelloWorld", "APIGateway", "S3_AWSSDK", "S3_Soto"]
37-
# examples: ${{ inputs.examples }}
48+
examples: ${{ fromJson(inputs.examples) }}
3849

3950
# We are using only one Swift version
4051
swift:
@@ -78,3 +89,42 @@ jobs:
7889
EXAMPLE: ${{ matrix.examples }}
7990
run: |
8091
./scripts/integration_tests.sh
92+
echo "✅ The examples compile correctly"
93+
94+
test-archive-plugin:
95+
name: Test archive plugin
96+
if: ${{ inputs.archive_plugin_enabled }}
97+
runs-on: ubuntu-latest
98+
strategy:
99+
fail-fast: false
100+
steps:
101+
- name: Checkout repository
102+
uses: actions/checkout@v4
103+
with:
104+
persist-credentials: false
105+
- name: Mark the workspace as safe
106+
# https://github.com/actions/checkout/issues/766
107+
run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
108+
- name: Test the archive plugin
109+
env:
110+
EXAMPLE: HelloWorld
111+
OUTPUT_FILE: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/bootstrap
112+
ZIP_FILE: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/MyLambda/MyLambda.zip
113+
run: |
114+
pushd Examples/${EXAMPLE}
115+
116+
# package the example (docker and swift toolchain are installed on the GH runner)
117+
LAMBDA_USE_LOCAL_DEPS=../.. swift package archive --allow-network-connections docker
118+
119+
# did the plugin generated a Linux binary?
120+
[ -f ${OUTPUT_FILE} ]
121+
file ${OUTPUT_FILE} | grep --silent ELF
122+
123+
# did the plugin created a ZIP file?
124+
[ -f ${ZIP_FILE} ]
125+
126+
# does the ZIP file contain the bootstrap?
127+
unzip -l ${ZIP_FILE} | grep --silent bootstrap
128+
129+
echo "✅ The archive plugin is OK"
130+
popd

.github/workflows/pull_request.yml

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ jobs:
1111
with:
1212
license_header_check_project_name: "SwiftAWSLambdaRuntime"
1313
shell_check_enabled: false
14+
python_lint_check_enabled: false
1415
api_breakage_check_container_image: "swift:6.0-noble"
1516
docs_check_container_image: "swift:6.0-noble"
1617
format_check_container_image: "swiftlang/swift:nightly-6.0-jammy"
@@ -19,21 +20,39 @@ jobs:
1920
name: Unit tests
2021
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
2122
with:
22-
linux_5_8_enabled: false
2323
linux_5_9_enabled: false
2424
linux_5_10_enabled: false
2525
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error"
2626
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error"
2727

2828
integration-tests:
2929
name: Integration Tests
30-
uses: ./.github/workflows/examples_matrix.yml
30+
uses: ./.github/workflows/integration_tests.yml
3131
with:
32-
# We should pass the list of examples here, but we can't pass an array as argument
33-
# examples: [ "HelloWorld", "APIGateway" ]
3432
name: "Integration tests"
33+
examples_enabled: true
3534
matrix_linux_command: "LAMBDA_USE_LOCAL_DEPS=../.. swift build"
35+
# We pass the list of examples here, but we can't pass an array as argument
36+
# Instead, we pass a String with a valid JSON array.
37+
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
38+
examples: "[ 'APIGateway', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'S3_AWSSDK', 'S3_Soto', 'Streaming' ]"
39+
40+
archive_plugin_enabled: true
3641

3742
swift-6-language-mode:
3843
name: Swift 6 Language Mode
3944
uses: apple/swift-nio/.github/workflows/swift_6_language_mode.yml@main
45+
46+
# until there is a support for musl in swiftlang/github-workflows
47+
# https://github.com/swiftlang/github-workflows/issues/34
48+
musl:
49+
runs-on: ubuntu-latest
50+
container: swift:6.0.2-noble
51+
timeout-minutes: 30
52+
steps:
53+
- name: Check out code
54+
uses: actions/checkout@v4
55+
- name: Install SDK
56+
run: swift sdk install https://download.swift.org/swift-6.0.2-release/static-sdk/swift-6.0.2-RELEASE/swift-6.0.2-RELEASE_static-linux-0.0.1.artifactbundle.tar.gz --checksum aa5515476a403797223fc2aad4ca0c3bf83995d5427fb297cab1d93c68cee075
57+
- name: Build
58+
run: swift build --swift-sdk x86_64-swift-linux-musl

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.DS_Store
22
*.build
3+
*.index-build
34
/.xcodeproj
45
*.pem
56
.podspecs
@@ -10,3 +11,4 @@ Package.resolved
1011
.serverless
1112
.vscode
1213
Makefile
14+
.devcontainer

Examples/APIGateway/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ To build the package, type the following commands.
2222

2323
```bash
2424
swift build
25-
swift package archive --allow-network-access docker
25+
swift package archive --allow-network-connections docker
2626
```
2727

2828
If there is no error, there is a ZIP file ready to deploy.

Examples/APIGateway/template.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
AWSTemplateFormatVersion: '2010-09-09'
22
Transform: AWS::Serverless-2016-10-31
3-
Description: SAM Template for QuoteService
3+
Description: SAM Template for APIGateway Lambda Example
44

55
Resources:
66
# Lambda function

Examples/BackgroundTasks/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// swift-tools-version:6.0
2+
3+
import PackageDescription
4+
5+
// needed for CI to test the local version of the library
6+
import struct Foundation.URL
7+
8+
#if os(macOS)
9+
let platforms: [PackageDescription.SupportedPlatform]? = [.macOS(.v15)]
10+
#else
11+
let platforms: [PackageDescription.SupportedPlatform]? = nil
12+
#endif
13+
14+
let package = Package(
15+
name: "swift-aws-lambda-runtime-example",
16+
platforms: platforms,
17+
products: [
18+
.executable(name: "BackgroundTasks", targets: ["BackgroundTasks"])
19+
],
20+
dependencies: [
21+
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
22+
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main")
23+
],
24+
targets: [
25+
.executableTarget(
26+
name: "BackgroundTasks",
27+
dependencies: [
28+
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
29+
],
30+
path: "."
31+
)
32+
]
33+
)
34+
35+
if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"],
36+
localDepsPath != "",
37+
let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]),
38+
v.isDirectory == true
39+
{
40+
// when we use the local runtime as deps, let's remove the dependency added above
41+
let indexToRemove = package.dependencies.firstIndex { dependency in
42+
if case .sourceControl(
43+
name: _,
44+
location: "https://github.com/swift-server/swift-aws-lambda-runtime.git",
45+
requirement: _
46+
) = dependency.kind {
47+
return true
48+
}
49+
return false
50+
}
51+
if let indexToRemove {
52+
package.dependencies.remove(at: indexToRemove)
53+
}
54+
55+
// then we add the dependency on LAMBDA_USE_LOCAL_DEPS' path (typically ../..)
56+
print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)")
57+
package.dependencies += [
58+
.package(name: "swift-aws-lambda-runtime", path: localDepsPath)
59+
]
60+
}

Examples/BackgroundTasks/README.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Background Tasks
2+
3+
This is an example for running background tasks in an AWS Lambda function.
4+
5+
Background tasks allow code to execute asynchronously after the main response has been returned, enabling additional processing without affecting response latency. This approach is ideal for scenarios like logging, data updates, or notifications that can be deferred. The code leverages Lambda's "Response Streaming" feature, which is effective for balancing real-time user responsiveness with the ability to perform extended tasks post-response.
6+
7+
For more information about Lambda background tasks, see [this AWS blog post](https://aws.amazon.com/blogs/compute/running-code-after-returning-a-response-from-an-aws-lambda-function/).
8+
9+
## Code
10+
11+
The sample code creates a `BackgroundProcessingHandler` struct that conforms to the `LambdaWithBackgroundProcessingHandler` protocol provided by the Swift AWS Lambda Runtime.
12+
13+
The `BackgroundProcessingHandler` struct defines the input and output JSON received and returned by the Handler.
14+
15+
The `handle(...)` method of this protocol receives incoming events as `Input` and returns the output as a `Greeting`. The `handle(...)` methods receives an `outputWriter` parameter to write the output before the function returns, giving some opportunities to run long-lasting tasks after the response has been returned to the client but before the function returns.
16+
17+
The `handle(...)` method uses the `outputWriter` to return the response as soon as possible. It then waits for 10 seconds to simulate a long background work. When the 10 seconds elapsed, the function returns. The billing cycle ends when the function returns.
18+
19+
The `handle(...)` method is marked as `mutating` to allow handlers to be implemented with a `struct`.
20+
21+
Once the struct is created and the `handle(...)` method is defined, the sample code creates a `LambdaCodableAdapter` adapter to adapt the `LambdaWithBackgroundProcessingHandler` to a type accepted by the `LambdaRuntime` struct. Then, the sample code initializes the `LambdaRuntime` with the adapter just created. Finally, the code calls `run()` to start the interaction with the AWS Lambda control plane.
22+
23+
## Build & Package
24+
25+
To build & archive the package, type the following commands.
26+
27+
```bash
28+
swift package archive --allow-network-connections docker
29+
```
30+
31+
If there is no error, there is a ZIP file ready to deploy.
32+
The ZIP file is located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/BackgroundTasks/BackgroundTasks.zip`
33+
34+
## Deploy with the AWS CLI
35+
36+
Here is how to deploy using the `aws` command line.
37+
38+
### Create the function
39+
40+
```bash
41+
AWS_ACCOUNT_ID=012345678901
42+
aws lambda create-function \
43+
--function-name BackgroundTasks \
44+
--zip-file fileb://.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/BackgroundTasks/BackgroundTasks.zip \
45+
--runtime provided.al2 \
46+
--handler provided \
47+
--architectures arm64 \
48+
--role arn:aws:iam::${AWS_ACCOUNT_ID}:role/lambda_basic_execution \
49+
--environment "Variables={LOG_LEVEL=debug}" \
50+
--timeout 15
51+
```
52+
53+
> [!IMPORTANT]
54+
> The timeout value must be bigger than the time it takes for your function to complete its background tasks. Otherwise, the Lambda control plane will terminate the execution environment before your code has a chance to finish the tasks. Here, the sample function waits for 10 seconds and we set the timeout for 15 seconds.
55+
56+
The `--environment` arguments sets the `LOG_LEVEL` environment variable to `debug`. This will ensure the debugging statements in the handler `context.logger.debug("...")` are printed in the Lambda function logs.
57+
58+
The `--architectures` flag is only required when you build the binary on an Apple Silicon machine (Apple M1 or more recent). It defaults to `x64`.
59+
60+
Be sure to set `AWS_ACCOUNT_ID` with your actual AWS account ID (for example: 012345678901).
61+
62+
### Invoke your Lambda function
63+
64+
To invoke the Lambda function, use `aws` command line.
65+
```bash
66+
aws lambda invoke \
67+
--function-name BackgroundTasks \
68+
--cli-binary-format raw-in-base64-out \
69+
--payload '{ "message" : "Hello Background Tasks" }' \
70+
response.json
71+
```
72+
73+
This should immediately output the following result.
74+
75+
```
76+
{
77+
"StatusCode": 200,
78+
"ExecutedVersion": "$LATEST"
79+
}
80+
```
81+
82+
The response is visible in the `response.json` file.
83+
84+
```bash
85+
cat response.json
86+
{"echoedMessage":"Hello Background Tasks"}
87+
```
88+
89+
### View the function's logs
90+
91+
You can observe additional messages being logged after the response is received.
92+
93+
To tail the log, use the AWS CLI:
94+
```bash
95+
aws logs tail /aws/lambda/BackgroundTasks --follow
96+
```
97+
98+
This produces an output like:
99+
```text
100+
INIT_START Runtime Version: provided:al2.v59 Runtime Version ARN: arn:aws:lambda:us-east-1::runtime:974c4a90f22278a2ef1c3f53c5c152167318aaf123fbb07c055a4885a4e97e52
101+
START RequestId: 4c8edd74-d776-4df9-9714-19086ab59bfd Version: $LATEST
102+
debug LambdaRuntime : [BackgroundTasks] BackgroundProcessingHandler - message received
103+
debug LambdaRuntime : [BackgroundTasks] BackgroundProcessingHandler - response sent. Performing background tasks.
104+
debug LambdaRuntime : [BackgroundTasks] BackgroundProcessingHandler - Background tasks completed. Returning
105+
END RequestId: 4c8edd74-d776-4df9-9714-19086ab59bfd
106+
REPORT RequestId: 4c8edd74-d776-4df9-9714-19086ab59bfd Duration: 10160.89 ms Billed Duration: 10250 ms Memory Size: 128 MB Max Memory Used: 27 MB Init Duration: 88.20 ms
107+
```
108+
> [!NOTE]
109+
> The `debug` message are sent by the code inside the `handler()` function. Note that the `Duration` and `Billed Duration` on the last line are for 10.1 and 10.2 seconds respectively.
110+
111+
Type CTRL-C to stop tailing the logs.
112+
113+
## Cleanup
114+
115+
When done testing, you can delete the Lambda function with this command.
116+
117+
```bash
118+
aws lambda delete-function --function-name BackgroundTasks
119+
```

0 commit comments

Comments
 (0)