Skip to content

Commit 945d0d4

Browse files
authored
Merge branch 'main' into sebsto/new-plugin-proposal
2 parents 5444714 + ae06d59 commit 945d0d4

File tree

136 files changed

+959
-866
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+959
-866
lines changed

.github/workflows/integration_tests.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ on:
3838
required: true
3939
matrix_linux_swift_container_image:
4040
type: string
41-
description: "Container image for the matrix job. Defaults to matching latest Swift Ubuntu image."
42-
default: "swift:amazonlinux2"
41+
description: "Container image for the matrix job. Defaults to matching latest Swift 6.1 Amazon Linux 2 image."
42+
default: "swiftlang/swift:nightly-6.1-amazonlinux2"
4343

4444
## We are cancelling previously triggered workflow runs
4545
concurrency:
@@ -59,7 +59,6 @@ jobs:
5959
# We are using only one Swift version
6060
swift:
6161
- image: ${{ inputs.matrix_linux_swift_container_image }}
62-
swift_version: "6.0.3-amazonlinux2"
6362
container:
6463
image: ${{ matrix.swift.image }}
6564
steps:
@@ -93,7 +92,6 @@ jobs:
9392
- name: Run matrix job
9493
working-directory: ${{ github.event.repository.name }} # until we can use action/checkout@v4
9594
env:
96-
SWIFT_VERSION: ${{ matrix.swift.swift_version }}
9795
COMMAND: ${{ inputs.matrix_linux_command }}
9896
EXAMPLE: ${{ matrix.examples }}
9997
run: |

.github/workflows/pull_request.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ jobs:
1212
license_header_check_project_name: "SwiftAWSLambdaRuntime"
1313
shell_check_enabled: true
1414
python_lint_check_enabled: true
15-
api_breakage_check_container_image: "swift:6.0-noble"
15+
api_breakage_check_container_image: "swiftlang/swift:nightly-6.1-jammy"
1616
docs_check_container_image: "swift:6.0-noble"
17-
format_check_container_image: "swiftlang/swift:nightly-6.0-jammy"
17+
format_check_container_image: "swiftlang/swift:nightly-6.1-jammy"
1818
yamllint_check_enabled: true
1919

2020
unit-tests:
@@ -36,7 +36,7 @@ jobs:
3636
# We pass the list of examples here, but we can't pass an array as argument
3737
# Instead, we pass a String with a valid JSON array.
3838
# The workaround is mentioned here https://github.com/orgs/community/discussions/11692
39-
examples: "[ 'APIGateway', 'APIGateway+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'ResourcesPackaging', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Testing', 'Tutorial' ]"
39+
examples: "[ 'APIGateway', 'APIGateway+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'ResourcesPackaging', 'S3EventNotifier', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Testing', 'Tutorial' ]"
4040
archive_plugin_examples: "[ 'HelloWorld', 'ResourcesPackaging' ]"
4141
archive_plugin_enabled: true
4242

.github/workflows/scripts/integration_tests.sh

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ log() { printf -- "** %s\n" "$*" >&2; }
1919
error() { printf -- "** ERROR: %s\n" "$*" >&2; }
2020
fatal() { error "$@"; exit 1; }
2121

22+
SWIFT_VERSION=$(swift --version)
2223
test -n "${SWIFT_VERSION:-}" || fatal "SWIFT_VERSION unset"
2324
test -n "${COMMAND:-}" || fatal "COMMAND unset"
2425
test -n "${EXAMPLE:-}" || fatal "EXAMPLE unset"

.spi.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
version: 1
22
builder:
33
configs:
4-
- documentation_targets: [AWSLambdaRuntimeCore, AWSLambdaRuntime]
4+
- documentation_targets: [AWSLambdaRuntime]

Examples/APIGateway+LambdaAuthorizer/Package.swift

+1-7
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@ import PackageDescription
55
// needed for CI to test the local version of the library
66
import struct Foundation.URL
77

8-
#if os(macOS)
9-
let platforms: [PackageDescription.SupportedPlatform]? = [.macOS(.v15)]
10-
#else
11-
let platforms: [PackageDescription.SupportedPlatform]? = nil
12-
#endif
13-
148
let package = Package(
159
name: "swift-aws-lambda-runtime-example",
1610
platforms: [.macOS(.v15)],
@@ -21,7 +15,7 @@ let package = Package(
2115
dependencies: [
2216
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
2317
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
24-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", branch: "main"),
18+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "1.0.0"),
2519
],
2620
targets: [
2721
.executableTarget(

Examples/APIGateway/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let package = Package(
1414
dependencies: [
1515
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
1616
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
17-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", branch: "main"),
17+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "1.0.0"),
1818
],
1919
targets: [
2020
.executableTarget(

Examples/CDK/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let package = Package(
1414
dependencies: [
1515
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
1616
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
17-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", branch: "main"),
17+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "1.0.0"),
1818
],
1919
targets: [
2020
.executableTarget(

Examples/HelloJSON/Package.swift

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
1-
// swift-tools-version:6.0
1+
// swift-tools-version:6.1
22

33
import PackageDescription
44

55
// needed for CI to test the local version of the library
66
import struct Foundation.URL
77

8-
#if os(macOS)
9-
let platforms: [PackageDescription.SupportedPlatform]? = [.macOS(.v15)]
10-
#else
11-
let platforms: [PackageDescription.SupportedPlatform]? = nil
12-
#endif
13-
148
let package = Package(
159
name: "swift-aws-lambda-runtime-example",
16-
platforms: platforms,
10+
platforms: [.macOS(.v15)],
1711
products: [
1812
.executable(name: "HelloJSON", targets: ["HelloJSON"])
1913
],
2014
dependencies: [
2115
// 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")
16+
.package(
17+
url: "https://github.com/swift-server/swift-aws-lambda-runtime.git",
18+
branch: "ff-package-traits",
19+
traits: [
20+
.trait(name: "FoundationJSONSupport")
21+
]
22+
)
2323
],
2424
targets: [
2525
.executableTarget(

Examples/HelloWorld/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,35 @@ The handler is `(event: String, context: LambdaContext)`. The function takes two
1212

1313
The function return value will be encoded as your Lambda function response.
1414

15+
## Test locally
16+
17+
You can test your function locally before deploying it to AWS Lambda.
18+
19+
To start the local function, type the following commands:
20+
21+
```bash
22+
swift run
23+
```
24+
25+
It will compile your code and start the local server. You know the local server is ready to accept connections when you see this message.
26+
27+
```txt
28+
Building for debugging...
29+
[1/1] Write swift-version--644A47CB88185983.txt
30+
Build of product 'MyLambda' complete! (0.31s)
31+
2025-01-29T12:44:48+0100 info LocalServer : host="127.0.0.1" port=7000 [AWSLambdaRuntime] Server started and listening
32+
```
33+
34+
Then, from another Terminal, send your payload with `curl`. Note that the payload must be a valid JSON string. In the case of this function that accepts a simple String, it means the String must be wrapped in between double quotes.
35+
36+
```bash
37+
curl -d '"seb"' http://127.0.0.1:7000/invoke
38+
"Hello seb"
39+
```
40+
41+
> [!IMPORTANT]
42+
> The local server is only available in `DEBUG` mode. It will not start with `swift -c release run`.
43+
1544
## Build & Package
1645

1746
To build & archive the package, type the following commands.

Examples/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ This directory contains example code for Lambda functions.
2828

2929
- **[HelloWorld](HelloWorld/README.md)**: a simple Lambda function (requires [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)).
3030

31+
- **[S3EventNotifier](S3EventNotifier/README.md)**: a Lambda function that receives object-upload notifications from an [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) bucket.
32+
3133
- **[S3_AWSSDK](S3_AWSSDK/README.md)**: a Lambda function that uses the [AWS SDK for Swift](https://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/getting-started.html) to invoke an [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) API (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)).
3234

3335
- **[S3_Soto](S3_Soto/README.md)**: a Lambda function that uses [Soto](https://github.com/soto-project/soto) to invoke an [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) API (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)).
@@ -64,4 +66,4 @@ To obtain these keys, you need an AWS account:
6466

6567
4. **(Optional) Generate Temporary Security Credentials**: If you’re using temporary credentials (which are more secure for short-term access), use AWS Security Token Service (STS). You can call the `GetSessionToken` or `AssumeRole` API to generate temporary credentials, including a session token.
6668

67-
With these in hand, you can use AWS SigV4 to securely sign your requests and interact with AWS services from your Swift app.
69+
With these in hand, you can use AWS SigV4 to securely sign your requests and interact with AWS services from your Swift app.

Examples/S3EventNotifier/.gitignore

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

Examples/S3EventNotifier/README.md

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# S3 Event Notifier
2+
3+
This example demonstrates how to write a Lambda that is invoked by an event originating from Amazon S3, such as a new object being uploaded to a bucket.
4+
5+
## Code
6+
7+
In this example the Lambda function receives an `S3Event` object defined in the `AWSLambdaEvents` library as input object. The `S3Event` object contains all the information about the S3 event that triggered the function, but what we are interested in is the bucket name and the object key, which are inside of a notification `Record`. The object contains an array of records, however since the Lambda function is triggered by a single event, we can safely assume that there is only one record in the array: the first one. Inside of this record, we can find the bucket name and the object key:
8+
9+
```swift
10+
guard let s3NotificationRecord = event.records.first else {
11+
throw LambdaError.noNotificationRecord
12+
}
13+
14+
let bucket = s3NotificationRecord.s3.bucket.name
15+
let key = s3NotificationRecord.s3.object.key.replacingOccurrences(of: "+", with: " ")
16+
```
17+
18+
The key is URL encoded, so we replace the `+` with a space.
19+
20+
## Build & Package
21+
22+
To build & archive the package you can use the following commands:
23+
24+
```bash
25+
swift build
26+
swift package archive --allow-network-connections docker
27+
```
28+
29+
If there are no errors, a ZIP file should be ready to deploy, located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/S3EventNotifier/S3EventNotifier.zip`.
30+
31+
## Deploy
32+
33+
> [!IMPORTANT]
34+
> The Lambda function and the S3 bucket must be located in the same AWS Region. In the code below, we use `eu-west-1` (Ireland).
35+
36+
To deploy the Lambda function, you can use the `aws` command line:
37+
38+
```bash
39+
REGION=eu-west-1
40+
aws lambda create-function \
41+
--region "${REGION}" \
42+
--function-name S3EventNotifier \
43+
--zip-file fileb://.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/S3EventNotifier/S3EventNotifier.zip \
44+
--runtime provided.al2 \
45+
--handler provided \
46+
--architectures arm64 \
47+
--role arn:aws:iam::<YOUR_ACCOUNT_ID>:role/lambda_basic_execution
48+
```
49+
50+
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`.
51+
52+
Be sure to define `REGION` with the region where you want to deploy your Lambda function and replace `<YOUR_ACCOUNT_ID>` with your actual AWS account ID (for example: 012345678901).
53+
54+
Besides deploying the Lambda function you also need to create the S3 bucket and configure it to send events to the Lambda function. You can do this using the following commands:
55+
56+
```bash
57+
REGION=eu-west-1
58+
59+
aws s3api create-bucket \
60+
--region "${REGION}" \
61+
--bucket my-test-bucket \
62+
--create-bucket-configuration LocationConstraint="${REGION}"
63+
64+
aws lambda add-permission \
65+
--region "${REGION}" \
66+
--function-name S3EventNotifier \
67+
--statement-id S3InvokeFunction \
68+
--action lambda:InvokeFunction \
69+
--principal s3.amazonaws.com \
70+
--source-arn arn:aws:s3:::my-test-bucket
71+
72+
aws s3api put-bucket-notification-configuration \
73+
--region "${REGION}" \
74+
--bucket my-test-bucket \
75+
--notification-configuration '{
76+
"LambdaFunctionConfigurations": [{
77+
"LambdaFunctionArn": "arn:aws:lambda:${REGION}:<YOUR_ACCOUNT_ID>:function:S3EventNotifier",
78+
"Events": ["s3:ObjectCreated:*"]
79+
}]
80+
}'
81+
82+
touch testfile.txt && aws s3 cp testfile.txt s3://my-test-bucket/
83+
```
84+
85+
This will:
86+
- create a bucket named `my-test-bucket` in the `$REGION` region;
87+
- add a permission to the Lambda function to be invoked by Amazon S3;
88+
- configure the bucket to send `s3:ObjectCreated:*` events to the Lambda function named `S3EventNotifier`;
89+
- upload a file named `testfile.txt` to the bucket.
90+
91+
Replace `my-test-bucket` with your bucket name (bucket names are unique globaly and this one is already taken). Also replace `REGION` environment variable with the AWS Region where you deployed the Lambda function and `<YOUR_ACCOUNT_ID>` with your actual AWS account ID.
92+
93+
> [!IMPORTANT]
94+
> The Lambda function and the S3 bucket must be located in the same AWS Region. Adjust the code above according to your closest AWS Region.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2025 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import AWSLambdaEvents
16+
import AWSLambdaRuntime
17+
import Foundation
18+
19+
let runtime = LambdaRuntime { (event: S3Event, context: LambdaContext) async throws in
20+
guard let s3NotificationRecord = event.records.first else {
21+
context.logger.error("No S3 notification record found in the event")
22+
return
23+
}
24+
25+
let bucket = s3NotificationRecord.s3.bucket.name
26+
let key = s3NotificationRecord.s3.object.key.replacingOccurrences(of: "+", with: " ")
27+
28+
context.logger.info("Received notification from S3 bucket '\(bucket)' for object with key '\(key)'")
29+
30+
// Here you could, for example, notify an API or a messaging service
31+
}
32+
33+
try await runtime.run()

Examples/S3_AWSSDK/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ let package = Package(
1414
dependencies: [
1515
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
1616
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
17-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", branch: "main"),
17+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", from: "1.0.0"),
1818
.package(url: "https://github.com/awslabs/aws-sdk-swift", from: "1.0.0"),
1919
],
2020
targets: [

Examples/S3_Soto/Package.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let package = Package(
1616

1717
// during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below
1818
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main"),
19-
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", branch: "main"),
19+
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", from: "1.0.0"),
2020
],
2121
targets: [
2222
.executableTarget(

0 commit comments

Comments
 (0)