chore(deps): bump shell-quote from 1.8.2 to 1.8.4 in /examples/datadog-zip/expressjs/lambda-asset/src #112
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Verify Examples | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - "examples/**" | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - "src/**" | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.13" | |
| - uses: aws-actions/setup-sam@v2 | |
| with: | |
| use-installer: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Validate all SAM templates | |
| run: | | |
| failed=0 | |
| for template in $(find examples -maxdepth 2 -name "template.yaml" | sort); do | |
| dir=$(dirname "$template") | |
| echo "Validating $dir..." | |
| if ! sam validate --template "$template" --lint 2>&1; then | |
| echo "FAIL: $dir" | |
| failed=1 | |
| fi | |
| done | |
| if [ "$failed" -eq 1 ]; then | |
| echo "Some templates failed validation" | |
| exit 1 | |
| fi | |
| build-layer: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install stable toolchain | |
| run: rustup target add x86_64-unknown-linux-musl | |
| - name: Install cargo lambda | |
| run: pip3 install cargo-lambda | |
| - name: Configure Rust cache | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Build x86_64 layer | |
| run: | | |
| cargo lambda build --release --extension --target x86_64-unknown-linux-musl | |
| mkdir -p layer-x86_64 | |
| cp layer/bootstrap layer-x86_64/ | |
| cp target/lambda/extensions/lambda-adapter layer-x86_64/ | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: layer-x86_64 | |
| path: layer-x86_64/ | |
| # Docker-based examples verified with sam local start-api. Each matrix entry | |
| # asserts an exact status (and optional body substring) via verify-http.sh, | |
| # not just liveness. | |
| # Excluded: nginx, php, flask, aspnet-mvc (web app hardcodes port 8080 | |
| # which conflicts with SAM's Lambda Runtime Interface Emulator). | |
| test-image: | |
| needs: [build-layer] | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| example: | |
| - { name: expressjs, path: /, expect_body: "Hi there!" } | |
| - { name: fastapi, path: /, expect_body: "message" } | |
| - { name: fastapi-background-tasks, path: /, expect_body: "message" } | |
| - { name: fasthtml, path: /, expect_body: "Hello World" } | |
| - { name: gin, path: /, expect_body: "message" } | |
| - { name: nextjs, path: /, expect_body: "Next.js Logo" } | |
| - { name: remix, path: /, expect_body: "Welcome to" } | |
| - { name: springboot, path: /v1/, expect_body: "Hello, world!" } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.13" | |
| - uses: aws-actions/setup-sam@v2 | |
| with: | |
| use-installer: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: layer-x86_64 | |
| path: /tmp/layer-x86_64 | |
| - name: Build local adapter image | |
| run: | | |
| # Build the local adapter under every tag the example Dockerfiles | |
| # reference, so each example's `docker build` (COPY --from=...:<tag>) | |
| # uses the locally compiled binary instead of pulling the released | |
| # image. Deriving the tags from the Dockerfiles keeps CI in lockstep | |
| # with them (no hardcoded version to forget at release time). | |
| chmod +x /tmp/layer-x86_64/lambda-adapter | |
| tags=$(grep -rhoE 'aws-lambda-adapter:[0-9A-Za-z._-]+' examples | sed 's/.*://' | sort -u) | |
| echo "Building local adapter for tags: $tags" | |
| for tag in $tags; do | |
| printf 'FROM scratch\nCOPY --chmod=755 lambda-adapter /lambda-adapter\n' | \ | |
| docker build -t public.ecr.aws/awsguru/aws-lambda-adapter:$tag -f- /tmp/layer-x86_64 | |
| done | |
| - name: Build | |
| working-directory: examples/${{ matrix.example.name }} | |
| run: | | |
| # Retry to tolerate transient registry rate limits (toomanyrequests: Rate | |
| # exceeded). public.ecr.aws throttles unauthenticated pulls at ~1 req/s per | |
| # source IP, and the matrix runs many jobs from shared runner IPs, so bursts | |
| # collide. Exponential backoff with random jitter desynchronizes retries | |
| # across jobs to avoid a thundering herd re-colliding on the same boundary. | |
| max_attempts=5 | |
| attempt=1 | |
| while true; do | |
| if sam build; then exit 0; fi | |
| if [ "$attempt" -ge "$max_attempts" ]; then | |
| echo "sam build failed after ${max_attempts} attempts" | |
| exit 1 | |
| fi | |
| delay=$(( 10 * 2 ** (attempt - 1) + RANDOM % 16 )) # ~10/20/40/80s + 0-15s jitter | |
| echo "sam build failed (attempt ${attempt}/${max_attempts}), retrying in ${delay}s..." | |
| sleep "$delay" | |
| attempt=$(( attempt + 1 )) | |
| done | |
| - name: Start local API and verify | |
| working-directory: examples/${{ matrix.example.name }} | |
| run: | | |
| # Set PORT=8000 to avoid conflict with SAM's RIE on port 8080. | |
| # Start and verify in the same step so the backgrounded sam local | |
| # stays attached to this shell while requests are served (splitting | |
| # it across steps races the container warm-up and yields 500s). | |
| echo '{"Parameters":{"PORT":"8000"}}' > /tmp/env.json | |
| sam local start-api --port 3000 --warm-containers EAGER --container-env-vars /tmp/env.json & | |
| echo "SAM_PID=$!" >> $GITHUB_ENV | |
| "$GITHUB_WORKSPACE/.github/scripts/verify-http.sh" \ | |
| http://127.0.0.1:3000 \ | |
| "${{ matrix.example.path }}" \ | |
| 200 \ | |
| "${{ matrix.example.expect_body }}" | |
| - name: Stop local API | |
| if: always() | |
| run: kill $SAM_PID 2>/dev/null || true | |
| # Zip-based examples verified with local layer. Each matrix entry asserts an | |
| # exact status (and optional body substring) via verify-http.sh. | |
| # `port` is the app's listening port, passed through to the adapter via the | |
| # PORT parameter; it must differ from SAM's RIE port (8080). | |
| # Excluded: aspnet-*-zip (hardcode port 8080), | |
| # bun/nginx/php-zip (need third-party layers), arm64 examples (javalin, rust-*), | |
| # nextjs-zip (Makefile produces a zip artifact incompatible with sam local). | |
| test-zip: | |
| needs: [build-layer] | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| example: | |
| - { name: deno-zip, path: /, expect_body: "success", port: "8000" } | |
| - { name: expressjs-zip, path: /, expect_body: "Hi there!", port: "8000" } | |
| - { name: fastapi-zip, path: /, expect_body: "message", port: "8000" } | |
| - { name: fasthtml-zip, path: /, expect_body: "Hello World", port: "8000" } | |
| - { name: flask-zip, path: /, expect_body: "message", port: "8000" } | |
| - { name: gin-zip, path: /, expect_body: "message", port: "8000" } | |
| - { name: remix-zip, path: /, expect_body: "Welcome to", port: "8000" } | |
| - { name: springboot-zip, path: /v1/, expect_body: "Hello, world!", port: "8000" } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.13" | |
| - uses: aws-actions/setup-sam@v2 | |
| with: | |
| use-installer: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: denoland/setup-deno@v2 | |
| if: matrix.example.name == 'deno-zip' | |
| with: | |
| deno-version: v2.x | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: layer-x86_64 | |
| path: /tmp/layer-x86_64 | |
| - name: Build | |
| working-directory: examples/${{ matrix.example.name }} | |
| run: | | |
| # Retry to tolerate transient registry rate limits (toomanyrequests: Rate | |
| # exceeded). public.ecr.aws throttles unauthenticated pulls at ~1 req/s per | |
| # source IP, and the matrix runs many jobs from shared runner IPs, so bursts | |
| # collide. Exponential backoff with random jitter desynchronizes retries | |
| # across jobs to avoid a thundering herd re-colliding on the same boundary. | |
| max_attempts=5 | |
| attempt=1 | |
| while true; do | |
| if sam build; then exit 0; fi | |
| if [ "$attempt" -ge "$max_attempts" ]; then | |
| echo "sam build failed after ${max_attempts} attempts" | |
| exit 1 | |
| fi | |
| delay=$(( 10 * 2 ** (attempt - 1) + RANDOM % 16 )) # ~10/20/40/80s + 0-15s jitter | |
| echo "sam build failed (attempt ${attempt}/${max_attempts}), retrying in ${delay}s..." | |
| sleep "$delay" | |
| attempt=$(( attempt + 1 )) | |
| done | |
| - name: Inject local layer into build output | |
| working-directory: examples/${{ matrix.example.name }} | |
| run: | | |
| # Prepare local layer directory with correct structure | |
| LAYER_DIR=".aws-sam/build/LocalAdapterLayer" | |
| mkdir -p "$LAYER_DIR/extensions" | |
| cp /tmp/layer-x86_64/bootstrap "$LAYER_DIR/" | |
| cp /tmp/layer-x86_64/lambda-adapter "$LAYER_DIR/extensions/" | |
| chmod +x "$LAYER_DIR/bootstrap" "$LAYER_DIR/extensions/lambda-adapter" | |
| # Add local layer resource and replace remote layer ARN refs | |
| pip install pyyaml -q | |
| python3 << 'PYEOF' | |
| import yaml | |
| with open(".aws-sam/build/template.yaml") as f: | |
| t = yaml.safe_load(f) | |
| t["Resources"]["LocalAdapterLayer"] = { | |
| "Type": "AWS::Lambda::LayerVersion", | |
| "Properties": {"Content": "LocalAdapterLayer", "LayerName": "local-adapter"}, | |
| } | |
| for r in t.get("Resources", {}).values(): | |
| layers = r.get("Properties", {}).get("Layers", []) | |
| for i, l in enumerate(layers): | |
| if "LambdaAdapterLayerX86" in str(l): | |
| layers[i] = {"Ref": "LocalAdapterLayer"} | |
| with open(".aws-sam/build/template.yaml", "w") as f: | |
| yaml.dump(t, f) | |
| PYEOF | |
| - name: Start local API and verify | |
| working-directory: examples/${{ matrix.example.name }} | |
| run: | | |
| # Pass the app's listening port via PORT (must differ from RIE's 8080). | |
| # Start and verify in the same step so the backgrounded sam local | |
| # stays attached to this shell while requests are served (splitting | |
| # it across steps races the container warm-up and yields 500s). | |
| echo '{"Parameters":{"PORT":"${{ matrix.example.port }}"}}' > /tmp/env.json | |
| sam local start-api --port 3000 --warm-containers EAGER --container-env-vars /tmp/env.json & | |
| echo "SAM_PID=$!" >> $GITHUB_ENV | |
| "$GITHUB_WORKSPACE/.github/scripts/verify-http.sh" \ | |
| http://127.0.0.1:3000 \ | |
| "${{ matrix.example.path }}" \ | |
| 200 \ | |
| "${{ matrix.example.expect_body }}" | |
| - name: Stop local API | |
| if: always() | |
| run: kill $SAM_PID 2>/dev/null || true | |
| # Response-streaming examples verified by building and running the real app, | |
| # then asserting an exact status/body with verify-http.sh. These use a Lambda | |
| # Function URL with RESPONSE_STREAM and have no API Gateway events, so | |
| # sam local start-api (used by test-image/test-zip) cannot drive them. | |
| # | |
| # `kind` selects how the app is launched: | |
| # image - build the app image (its Dockerfile COPYs the local adapter | |
| # image built below) and run it with PORT=8000. | |
| # zip-fasthtml - run `python app/main.py` (FastHTML's serve() binds 8000). | |
| # AWS_REGION is set so boto3 / AnthropicBedrock clients construct without real | |
| # creds; the verified routes only render UI and never call Bedrock. | |
| test-stream: | |
| needs: [build-layer] | |
| runs-on: ubuntu-24.04 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| example: | |
| - { name: fasthtml-response-streaming, kind: image, path: /, expect_body: "Serverless Bedtime" } | |
| - { name: fasthtml-response-streaming-zip, kind: zip-fasthtml, path: /, expect_body: "Click to stream" } | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.12" | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: layer-x86_64 | |
| path: /tmp/layer-x86_64 | |
| - name: Build local adapter image | |
| run: | | |
| # Build the local adapter under every tag the example Dockerfiles | |
| # reference, so each example's `docker build` (COPY --from=...:<tag>) | |
| # uses the locally compiled binary instead of pulling the released | |
| # image. Deriving the tags from the Dockerfiles keeps CI in lockstep | |
| # with them (no hardcoded version to forget at release time). | |
| chmod +x /tmp/layer-x86_64/lambda-adapter | |
| tags=$(grep -rhoE 'aws-lambda-adapter:[0-9A-Za-z._-]+' examples | sed 's/.*://' | sort -u) | |
| echo "Building local adapter for tags: $tags" | |
| for tag in $tags; do | |
| printf 'FROM scratch\nCOPY --chmod=755 lambda-adapter /lambda-adapter\n' | \ | |
| docker build -t public.ecr.aws/awsguru/aws-lambda-adapter:$tag -f- /tmp/layer-x86_64 | |
| done | |
| - name: Build and start app | |
| working-directory: examples/${{ matrix.example.name }} | |
| env: | |
| KIND: ${{ matrix.example.kind }} | |
| run: | | |
| case "$KIND" in | |
| image) | |
| docker build -t stream-app app | |
| docker run -d --name stream-app \ | |
| -e PORT=8000 -e AWS_REGION=us-east-1 -e AWS_DEFAULT_REGION=us-east-1 \ | |
| -p "8000:8000" stream-app | |
| ;; | |
| zip-fasthtml) | |
| pip install -r app/requirements.txt | |
| PORT=8000 nohup python app/main.py > /tmp/app.log 2>&1 & | |
| echo "APP_PID=$!" >> $GITHUB_ENV | |
| ;; | |
| *) | |
| echo "Unknown kind: $KIND"; exit 1 ;; | |
| esac | |
| - name: Verify | |
| run: | | |
| "$GITHUB_WORKSPACE/.github/scripts/verify-http.sh" \ | |
| http://127.0.0.1:8000 \ | |
| "${{ matrix.example.path }}" \ | |
| 200 \ | |
| "${{ matrix.example.expect_body }}" \ | |
| || { echo "--- app logs ---"; docker logs stream-app 2>/dev/null || cat /tmp/app.log 2>/dev/null || true; exit 1; } | |
| - name: Stop app | |
| if: always() | |
| run: | | |
| docker rm -f stream-app 2>/dev/null || true | |
| kill $APP_PID 2>/dev/null || true |