Lambda SPM Resources

Posted Tuesday, February 6, 2024.

There is an additional step you need to do when packaging an SPM based Lambda that has resources.

Project Setup

File Structure

.
├── Sources
│ ├── Lambda
│ │ └── Handler.swift
│ └── Resources
│ └── info.txt
├── Package.swift
└── Package.resolved

Package.swift

// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "Lambda",
platforms: [
.macOS(.v13)
],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0-alpha.2")
],
targets: [
.executableTarget(
name: "Lambda",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
],
resources: [
.process("Resources")
]
)
]
)

Handler Definition

Imagine we had a handler that would look for a resource decided by the request parameters:

import AWSLambdaRuntime
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

struct Request: Codable {
let resource: String
let extension: String
}

struct Response: Codable {
let output: String
}

@main
struct Handler: SimpleLambdaHandler {
func handle(_ request: Request, context: LambdaContext) async throws -> Response {
guard let url = Bundle.module.url(forResource: request.resource, withExtension: request.extension) else {
throw Error.missingResource
}

let data = try Data(contentsOf: url)
let string = String(decoding: data, as: UTF8.self)

return .init(output: string)
}

enum Error: Swift.Error {
case missingResource
}
}

Without doing any extra steps, Bundle.module.url(forResource:withExtension:) would return nil when running on the Lambda because the resources are not bundled properly (even if the resource was "supposed" to exist).

Steps to Mitigate

# use package plugin to bundle lambda
swift package --disable-sandbox archive --output-path .
# remove generated lambda zip
rm ./Lambda/Lambda.zip
# Copy Resources from build directory (PackageName_TargetName.resources)
cp -r .build/release/Lambda_Lambda.resources ./Lambda/Lambda_Lambda.resources
# zip new lambda with resources included
zip -r Lambda.zip Lambda_Lambda.resources bootstrap Lambda

Upload

Upload new Lambda zip to AWS


Tagged With: