diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 468fc295..1a67f637 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -19,6 +19,11 @@ on: type: boolean description: "Boolean to enable the compilation of examples. Defaults to true." default: true + archive_plugin_examples: + type: string + description: "The list of examples to run through the archive plugin test. Pass a String with a valid JSON array such as \"[ 'HelloWorld', 'APIGateway' ]\"" + required: true + default: "" archive_plugin_enabled: type: boolean description: "Boolean to enable the test of the archive plugin. Defaults to true." @@ -54,7 +59,7 @@ jobs: # We are using only one Swift version swift: - image: ${{ inputs.matrix_linux_swift_container_image }} - swift_version: "6.0.1-amazonlinux2" + swift_version: "6.0.3-amazonlinux2" container: image: ${{ matrix.swift.image }} steps: @@ -98,6 +103,10 @@ jobs: name: Test archive plugin if: ${{ inputs.archive_plugin_enabled }} runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + examples: ${{ fromJson(inputs.archive_plugin_examples) }} steps: - name: Checkout repository uses: actions/checkout@v4 @@ -107,8 +116,10 @@ jobs: # https://github.com/actions/checkout/issues/766 run: git config --global --add safe.directory ${GITHUB_WORKSPACE} - name: Test the archive plugin + env: + EXAMPLE: ${{ matrix.examples }} run: | - .github/workflows/scripts/check-archive-plugin.sh + .github/workflows/scripts/check-archive-plugin.sh check-foundation: name: No dependencies on Foundation diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 2051c091..72c2a24c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -36,8 +36,8 @@ jobs: # We pass the list of examples here, but we can't pass an array as argument # Instead, we pass a String with a valid JSON array. # The workaround is mentioned here https://github.com/orgs/community/discussions/11692 - examples: "[ 'APIGateway', 'APIGateway+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Testing', 'Tutorial' ]" - + examples: "[ 'APIGateway', 'APIGateway+LambdaAuthorizer', 'BackgroundTasks', 'HelloJSON', 'HelloWorld', 'ResourcesPackaging', 'S3_AWSSDK', 'S3_Soto', 'Streaming', 'Testing', 'Tutorial' ]" + archive_plugin_examples: "[ 'HelloWorld', 'ResourcesPackaging' ]" archive_plugin_enabled: true swift-6-language-mode: diff --git a/.github/workflows/scripts/check-archive-plugin.sh b/.github/workflows/scripts/check-archive-plugin.sh index 3a15c138..3c127be8 100755 --- a/.github/workflows/scripts/check-archive-plugin.sh +++ b/.github/workflows/scripts/check-archive-plugin.sh @@ -13,12 +13,17 @@ ## ##===----------------------------------------------------------------------===## -EXAMPLE=HelloWorld +log() { printf -- "** %s\n" "$*" >&2; } +error() { printf -- "** ERROR: %s\n" "$*" >&2; } +fatal() { error "$@"; exit 1; } + +test -n "${EXAMPLE:-}" || fatal "EXAMPLE unset" + OUTPUT_DIR=.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager OUTPUT_FILE=${OUTPUT_DIR}/MyLambda/bootstrap ZIP_FILE=${OUTPUT_DIR}/MyLambda/MyLambda.zip -pushd Examples/${EXAMPLE} || exit 1 +pushd "Examples/${EXAMPLE}" || exit 1 # package the example (docker and swift toolchain are installed on the GH runner) LAMBDA_USE_LOCAL_DEPS=../.. swift package archive --allow-network-connections docker || exit 1 @@ -33,5 +38,10 @@ file "${OUTPUT_FILE}" | grep --silent ELF # does the ZIP file contain the bootstrap? unzip -l "${ZIP_FILE}" | grep --silent bootstrap -echo "✅ The archive plugin is OK" +# if EXAMPLE is ResourcesPackaging, check if the ZIP file contains hello.txt +if [ "$EXAMPLE" == "ResourcesPackaging" ]; then + unzip -l "${ZIP_FILE}" | grep --silent hello.txt +fi + +echo "✅ The archive plugin is OK with example ${EXAMPLE}" popd || exit 1 diff --git a/.licenseignore b/.licenseignore index db42f1da..d47f45a2 100644 --- a/.licenseignore +++ b/.licenseignore @@ -33,4 +33,5 @@ Package.resolved *.yaml *.yml **/.npmignore -**/*.json \ No newline at end of file +**/*.json +**/*.txt \ No newline at end of file diff --git a/Examples/ResourcesPackaging/.gitignore b/Examples/ResourcesPackaging/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/Examples/ResourcesPackaging/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Examples/ResourcesPackaging/Package.swift b/Examples/ResourcesPackaging/Package.swift new file mode 100644 index 00000000..4680b74a --- /dev/null +++ b/Examples/ResourcesPackaging/Package.swift @@ -0,0 +1,57 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +// needed for CI to test the local version of the library +import struct Foundation.URL + +let package = Package( + name: "ResourcesPackaging", + platforms: [.macOS(.v15)], + products: [ + .executable(name: "MyLambda", targets: ["MyLambda"]) + ], + dependencies: [ + .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main") + ], + targets: [ + .executableTarget( + name: "MyLambda", + dependencies: [ + .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime") + ], + path: ".", + resources: [ + .process("hello.txt") + ] + ) + ] +) + +if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"], + localDepsPath != "", + let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]), + v.isDirectory == true +{ + // when we use the local runtime as deps, let's remove the dependency added above + let indexToRemove = package.dependencies.firstIndex { dependency in + if case .sourceControl( + name: _, + location: "https://github.com/swift-server/swift-aws-lambda-runtime.git", + requirement: _ + ) = dependency.kind { + return true + } + return false + } + if let indexToRemove { + package.dependencies.remove(at: indexToRemove) + } + + // then we add the dependency on LAMBDA_USE_LOCAL_DEPS' path (typically ../..) + print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)") + package.dependencies += [ + .package(name: "swift-aws-lambda-runtime", path: localDepsPath) + ] +} diff --git a/Examples/ResourcesPackaging/Sources/main.swift b/Examples/ResourcesPackaging/Sources/main.swift new file mode 100644 index 00000000..dccbd863 --- /dev/null +++ b/Examples/ResourcesPackaging/Sources/main.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright (c) 2025 Apple Inc. and the SwiftAWSLambdaRuntime project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import AWSLambdaRuntime +import Foundation + +let runtime = LambdaRuntime { + (event: String, context: LambdaContext) in + guard let fileURL = Bundle.module.url(forResource: "hello", withExtension: "txt") else { + fatalError("no file url") + } + return try String(contentsOf: fileURL, encoding: .utf8) +} + +try await runtime.run() diff --git a/Examples/ResourcesPackaging/hello.txt b/Examples/ResourcesPackaging/hello.txt new file mode 100644 index 00000000..557db03d --- /dev/null +++ b/Examples/ResourcesPackaging/hello.txt @@ -0,0 +1 @@ +Hello World diff --git a/Plugins/AWSLambdaPackager/Plugin.swift b/Plugins/AWSLambdaPackager/Plugin.swift index a8693945..f6ac2b03 100644 --- a/Plugins/AWSLambdaPackager/Plugin.swift +++ b/Plugins/AWSLambdaPackager/Plugin.swift @@ -249,11 +249,29 @@ struct AWSLambdaPackager: CommandPlugin { let resourcesDirectoryName = artifactURL.lastPathComponent let relocatedResourcesDirectory = workingDirectory.appending(path: resourcesDirectoryName) if FileManager.default.fileExists(atPath: artifactURL.path()) { - try FileManager.default.copyItem( - atPath: artifactURL.path(), - toPath: relocatedResourcesDirectory.path() - ) - arguments.append(resourcesDirectoryName) + do { + try FileManager.default.copyItem( + atPath: artifactURL.path(), + toPath: relocatedResourcesDirectory.path() + ) + arguments.append(resourcesDirectoryName) + } catch let error as CocoaError { + + // On Linux, when the build has been done with Docker, + // the source file are owned by root + // this causes a permission error **after** the files have been copied + // see https://github.com/swift-server/swift-aws-lambda-runtime/issues/449 + // see https://forums.swift.org/t/filemanager-copyitem-on-linux-fails-after-copying-the-files/77282 + + // because this error happens after the files have been copied, we can ignore it + // this code checks if the destination file exists + // if they do, just ignore error, otherwise throw it up to the caller. + if !(error.code == CocoaError.Code.fileWriteNoPermission + && FileManager.default.fileExists(atPath: relocatedResourcesDirectory.path())) + { + throw error + } // else just ignore it + } } } diff --git a/scripts/ubuntu-install-swift.sh b/scripts/ubuntu-install-swift.sh new file mode 100644 index 00000000..5ff58f46 --- /dev/null +++ b/scripts/ubuntu-install-swift.sh @@ -0,0 +1,70 @@ +#!/bin/bash +##===----------------------------------------------------------------------===## +## +## This source file is part of the SwiftAWSLambdaRuntime open source project +## +## Copyright (c) 2025 Apple Inc. and the SwiftAWSLambdaRuntime project authors +## Licensed under Apache License v2.0 +## +## See LICENSE.txt for license information +## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +## +## SPDX-License-Identifier: Apache-2.0 +## +##===----------------------------------------------------------------------===## + +sudo apt update && sudo apt -y upgrade + +# Install Swift 6.0.3 +sudo apt-get -y install \ + binutils \ + git \ + gnupg2 \ + libc6-dev \ + libcurl4-openssl-dev \ + libedit2 \ + libgcc-13-dev \ + libncurses-dev \ + libpython3-dev \ + libsqlite3-0 \ + libstdc++-13-dev \ + libxml2-dev \ + libz3-dev \ + pkg-config \ + tzdata \ + unzip \ + zip \ + zlib1g-dev + +wget https://download.swift.org/swift-6.0.3-release/ubuntu2404-aarch64/swift-6.0.3-RELEASE/swift-6.0.3-RELEASE-ubuntu24.04-aarch64.tar.gz + +tar xfvz swift-6.0.3-RELEASE-ubuntu24.04-aarch64.tar.gz + +export PATH=/home/ubuntu/swift-6.0.3-RELEASE-ubuntu24.04-aarch64/usr/bin:"${PATH}" + +swift --version + +# Install Docker +sudo apt-get update +sudo apt-get install -y ca-certificates curl +sudo install -m 0755 -d /etc/apt/keyrings +sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc +sudo chmod a+r /etc/apt/keyrings/docker.asc + +# Add the repository to Apt sources: +# shellcheck source=/etc/os-release +# shellcheck disable=SC1091 +. /etc/os-release +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ + $VERSION_CODENAME stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +sudo apt-get update + +sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + +# Add the current user to the docker group +sudo usermod -aG docker "$USER" + +# LOGOUT and LOGIN to apply the changes +exit 0 diff --git a/scripts/ubuntu-test-plugin.sh b/scripts/ubuntu-test-plugin.sh new file mode 100644 index 00000000..19d74609 --- /dev/null +++ b/scripts/ubuntu-test-plugin.sh @@ -0,0 +1,28 @@ +#!/bin/bash +##===----------------------------------------------------------------------===## +## +## This source file is part of the SwiftAWSLambdaRuntime open source project +## +## Copyright (c) 2025 Apple Inc. and the SwiftAWSLambdaRuntime project authors +## Licensed under Apache License v2.0 +## +## See LICENSE.txt for license information +## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +## +## SPDX-License-Identifier: Apache-2.0 +## +##===----------------------------------------------------------------------===## + +# Connect with ssh + +export PATH=/home/ubuntu/swift-6.0.3-RELEASE-ubuntu24.04-aarch64/usr/bin:"${PATH}" + +# clone a project +git clone https://github.com/swift-server/swift-aws-lambda-runtime.git + +# be sure Swift is install. +# Youc an install swift with the following command: ./scripts/ubuntu-install-swift.sh + +# build the project +cd swift-aws-lambda-runtime/Examples/ResourcesPackaging/ || exit 1 +LAMBDA_USE_LOCAL_DEPS=../.. swift package archive --allow-network-connections docker