From 0d568adeca0e8141efacd9f5683bf7fc5c5aeb4b Mon Sep 17 00:00:00 2001 From: Maxime David Date: Fri, 14 Aug 2026 13:01:57 +0000 Subject: [PATCH 1/2] feat: add simple smoke test after draft release --- ci/integ/docker/Dockerfile.oci-smoke | 20 +++++++ ci/integ/oci/main.cpp | 19 +++++++ ci/integ/run-oci-smoke.sh | 81 ++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 ci/integ/docker/Dockerfile.oci-smoke create mode 100644 ci/integ/oci/main.cpp create mode 100755 ci/integ/run-oci-smoke.sh diff --git a/ci/integ/docker/Dockerfile.oci-smoke b/ci/integ/docker/Dockerfile.oci-smoke new file mode 100644 index 0000000..d64048c --- /dev/null +++ b/ci/integ/docker/Dockerfile.oci-smoke @@ -0,0 +1,20 @@ +# Post-release OCI smoke test image. +# +# This links a dummy handler against the PREBUILT static library downloaded +# from the release (context file `runtime.a`), the point is we do NOT rebuild the runtime +FROM public.ecr.aws/amazonlinux/amazonlinux:2023 AS builder + +RUN dnf install -y gcc-c++ libcurl-devel && dnf clean all + +WORKDIR /build +COPY include/ include/ +COPY runtime.a runtime.a +COPY main.cpp main.cpp + +RUN g++ -std=c++11 -O2 -Iinclude main.cpp runtime.a -lcurl -pthread -o bootstrap + +FROM public.ecr.aws/lambda/provided:al2023 + +COPY --from=builder /build/bootstrap ${LAMBDA_RUNTIME_DIR}/bootstrap + +CMD [ "function.handler" ] diff --git a/ci/integ/oci/main.cpp b/ci/integ/oci/main.cpp new file mode 100644 index 0000000..0394d7c --- /dev/null +++ b/ci/integ/oci/main.cpp @@ -0,0 +1,19 @@ +#include + +using namespace aws::lambda_runtime; + +// Dummy handler used by the post-release OCI smoke test. It echoes the incoming +// payload back and reports its length, which the smoke test asserts on. +static invocation_response my_handler(invocation_request const& req) +{ + return invocation_response::success( + R"({"message":"hello from aws-lambda-cpp","echo":)" + (req.payload.empty() ? std::string("null") : req.payload) + + R"(,"payload_length":)" + std::to_string(req.payload.length()) + "}", + "application/json"); +} + +int main() +{ + run_handler(my_handler); + return 0; +} diff --git a/ci/integ/run-oci-smoke.sh b/ci/integ/run-oci-smoke.sh new file mode 100755 index 0000000..c7f7e00 --- /dev/null +++ b/ci/integ/run-oci-smoke.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Post-release OCI smoke test. +# +# Downloads the published static library from the (draft) release, verifies its +# checksum against the published SHA256SUMS, links a dummy handler against it, +# and invokes it through the Lambda Runtime Interface Emulator baked into +# public.ecr.aws/lambda/provided:al2023 — i.e. the exact happy path a downstream +# consumer of the artifact would follow. +# +# Requires: gh (with GH_TOKEN), docker, curl. Runs on stock GitHub-hosted +# runners; needs no AWS credentials or CodeArtifact access. +# +# Usage: run-oci-smoke.sh +set -euo pipefail + +ARCH=${1:?usage: run-oci-smoke.sh } +TAG=${2:?usage: run-oci-smoke.sh } +REPO=${GITHUB_REPOSITORY:-awslabs/aws-lambda-cpp} +PORT=9000 + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +REPO_ROOT=$(cd "$SCRIPT_DIR/../.." && pwd) + +LIB="libaws-lambda-runtime-${ARCH}.a" +IMAGE="lambda-cpp-oci-smoke:${ARCH}" +CONTAINER="lambda-cpp-oci-smoke-${ARCH}" + +WORKDIR=$(mktemp -d) +trap 'docker rm -f "$CONTAINER" >/dev/null 2>&1 || true; rm -rf "$WORKDIR"' EXIT + +echo "== Downloading $LIB + SHA256SUMS from release '$TAG' ==" +gh release download "$TAG" --repo "$REPO" \ + -p "$LIB" -p SHA256SUMS --dir "$WORKDIR" --clobber + +echo "== Verifying checksum ==" +( cd "$WORKDIR" && grep -F "$LIB" SHA256SUMS | sha256sum -c - ) + +echo "== Assembling build context ==" +CTX="$WORKDIR/ctx" +mkdir -p "$CTX" +cp -r "$REPO_ROOT/include" "$CTX/include" +cp "$REPO_ROOT/ci/integ/oci/main.cpp" "$CTX/main.cpp" +cp "$WORKDIR/$LIB" "$CTX/runtime.a" + +echo "== Building OCI image ==" +docker build \ + -f "$REPO_ROOT/ci/integ/docker/Dockerfile.oci-smoke" \ + -t "$IMAGE" "$CTX" + +echo "== Starting container (RIE) ==" +docker run -d --name "$CONTAINER" -p "$PORT:8080" "$IMAGE" >/dev/null + +INVOKE_URL="http://localhost:${PORT}/2015-03-31/functions/function/invocations" + +echo "== Waiting for RIE to become ready ==" +ready=false +for _ in $(seq 1 30); do + if curl -sf -XPOST "$INVOKE_URL" -d '{}' >/dev/null 2>&1; then + ready=true + break + fi + sleep 1 +done +if [ "$ready" != true ]; then + echo "::error::RIE did not become ready in time" + docker logs "$CONTAINER" || true + exit 1 +fi + +echo "== Invoking via RIE ==" +RESPONSE=$(curl -s -XPOST "$INVOKE_URL" -d '{"answer":42}') +echo "Response: $RESPONSE" + +EXPECTED='"payload_length":13' +if ! echo "$RESPONSE" | grep -qF "$EXPECTED"; then + echo "::error::Smoke test assertion failed for $ARCH. Expected response to contain: $EXPECTED" + docker logs "$CONTAINER" || true + exit 1 +fi + +echo "Smoke test passed for $ARCH" From d4c2d317e5af9afa4b81de788326aa98fc9012d0 Mon Sep 17 00:00:00 2001 From: Maxime David Date: Fri, 14 Aug 2026 13:04:14 +0000 Subject: [PATCH 2/2] feat: use the new test in relese.yml --- .github/workflows/release.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f5680f..b2a9b1c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,3 +156,27 @@ jobs: gh release upload "$TAG" \ --repo "$GITHUB_REPOSITORY" \ artifacts/* + + smoke-test: + needs: upload + runs-on: ${{ matrix.runner }} + timeout-minutes: 15 + permissions: + contents: write + strategy: + fail-fast: true + matrix: + include: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.ref_name }} + + - name: OCI + RIE smoke test against the published artifact + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: ci/integ/run-oci-smoke.sh "${{ matrix.arch }}" "${{ github.ref_name }}"