Skip to content

Commit d125b28

Browse files
committed
Add workflow to verify examples without deployment
- Validates all 41 SAM templates with sam validate --lint - Builds adapter layer from source for local testing - Tests 8 Docker-based examples: build, start-api, HTTP health check - Tests 9 zip-based examples: local layer, build, start-api, HTTP health check - Overrides PORT to avoid conflict with SAM's RIE on port 8080 - Triggers on PRs touching examples/ and via workflow_dispatch
1 parent 3ae8421 commit d125b28

1 file changed

Lines changed: 243 additions & 0 deletions

File tree

‎.github/workflows/examples.yaml‎

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
name: Verify Examples
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
paths:
8+
- "examples/**"
9+
workflow_dispatch:
10+
11+
permissions:
12+
contents: read
13+
14+
env:
15+
CARGO_TERM_COLOR: always
16+
17+
jobs:
18+
validate:
19+
runs-on: ubuntu-24.04
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: actions/setup-python@v4
24+
with:
25+
python-version: "3.13"
26+
27+
- uses: aws-actions/setup-sam@v2
28+
with:
29+
use-installer: true
30+
token: ${{ secrets.GITHUB_TOKEN }}
31+
32+
- name: Validate all SAM templates
33+
run: |
34+
failed=0
35+
for template in $(find examples -maxdepth 2 -name "template.yaml" | sort); do
36+
dir=$(dirname "$template")
37+
echo "Validating $dir..."
38+
if ! sam validate --template "$template" --lint 2>&1; then
39+
echo "FAIL: $dir"
40+
failed=1
41+
fi
42+
done
43+
if [ "$failed" -eq 1 ]; then
44+
echo "Some templates failed validation"
45+
exit 1
46+
fi
47+
48+
build-layer:
49+
runs-on: ubuntu-24.04
50+
steps:
51+
- uses: actions/checkout@v4
52+
53+
- name: Install stable toolchain
54+
run: rustup target add x86_64-unknown-linux-musl
55+
56+
- name: Install cargo lambda
57+
run: pip3 install cargo-lambda
58+
59+
- name: Configure Rust cache
60+
uses: Swatinem/rust-cache@v2
61+
62+
- name: Build x86_64 layer
63+
run: |
64+
cargo lambda build --release --extension --target x86_64-unknown-linux-musl
65+
mkdir -p layer-x86_64
66+
cp layer/bootstrap layer-x86_64/
67+
cp target/lambda/extensions/lambda-adapter layer-x86_64/
68+
69+
- uses: actions/upload-artifact@v4
70+
with:
71+
name: layer-x86_64
72+
path: layer-x86_64/
73+
74+
# Docker-based examples verified with sam local start-api.
75+
# Excluded: nginx, php, flask, aspnet-mvc (web app hardcodes port 8080
76+
# which conflicts with SAM's Lambda Runtime Interface Emulator).
77+
test-image:
78+
needs: [build-layer]
79+
runs-on: ubuntu-24.04
80+
strategy:
81+
fail-fast: false
82+
matrix:
83+
example:
84+
- expressjs
85+
- fastapi
86+
- fastapi-background-tasks
87+
- fasthtml
88+
- gin
89+
- nextjs
90+
- remix
91+
- springboot
92+
steps:
93+
- uses: actions/checkout@v4
94+
95+
- uses: actions/setup-python@v4
96+
with:
97+
python-version: "3.13"
98+
99+
- uses: aws-actions/setup-sam@v2
100+
with:
101+
use-installer: true
102+
token: ${{ secrets.GITHUB_TOKEN }}
103+
104+
- uses: actions/download-artifact@v4
105+
with:
106+
name: layer-x86_64
107+
path: /tmp/layer-x86_64
108+
109+
- name: Build local adapter image
110+
run: |
111+
printf 'FROM scratch\nCOPY lambda-adapter /lambda-adapter\n' | \
112+
docker build -t public.ecr.aws/awsguru/aws-lambda-adapter:1.0.0-rc1 -f- /tmp/layer-x86_64
113+
114+
- name: Build
115+
working-directory: examples/${{ matrix.example }}
116+
run: sam build
117+
118+
- name: Start local API and verify
119+
working-directory: examples/${{ matrix.example }}
120+
run: |
121+
# Set PORT=8000 to avoid conflict with SAM's RIE on port 8080
122+
echo '{"Parameters":{"PORT":"8000"}}' > /tmp/env.json
123+
sam local start-api --port 3000 --warm-containers EAGER --container-env-vars /tmp/env.json &
124+
SAM_PID=$!
125+
echo "SAM_PID=$SAM_PID" >> $GITHUB_ENV
126+
127+
echo "Waiting for local API to start..."
128+
for i in $(seq 1 90); do
129+
if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/ 2>/dev/null | grep -q "^[2345]"; then
130+
echo "API is ready after ${i}s"
131+
break
132+
fi
133+
if [ "$i" -eq 90 ]; then
134+
echo "API failed to start within 90s"
135+
kill $SAM_PID 2>/dev/null || true
136+
exit 1
137+
fi
138+
sleep 1
139+
done
140+
141+
status=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/)
142+
echo "HTTP status: $status"
143+
if [[ "$status" -ge 200 && "$status" -lt 500 ]]; then
144+
echo "OK: Got valid response"
145+
else
146+
echo "FAIL: Unexpected status $status"
147+
kill $SAM_PID 2>/dev/null || true
148+
exit 1
149+
fi
150+
151+
- name: Stop local API
152+
if: always()
153+
run: kill $SAM_PID 2>/dev/null || true
154+
155+
# Zip-based examples verified with local layer.
156+
# Excluded: aspnet-*-zip (hardcode port 8080),
157+
# bun/nginx/php-zip (need third-party layers), arm64 examples (javalin, rust-*).
158+
test-zip:
159+
needs: [build-layer]
160+
runs-on: ubuntu-24.04
161+
strategy:
162+
fail-fast: false
163+
matrix:
164+
example:
165+
- deno-zip
166+
- expressjs-zip
167+
- fastapi-zip
168+
- fasthtml-zip
169+
- flask-zip
170+
- gin-zip
171+
- nextjs-zip
172+
- remix-zip
173+
- springboot-zip
174+
steps:
175+
- uses: actions/checkout@v4
176+
177+
- uses: actions/setup-python@v4
178+
with:
179+
python-version: "3.13"
180+
181+
- uses: aws-actions/setup-sam@v2
182+
with:
183+
use-installer: true
184+
token: ${{ secrets.GITHUB_TOKEN }}
185+
186+
- uses: actions/download-artifact@v4
187+
with:
188+
name: layer-x86_64
189+
path: /tmp/layer-x86_64
190+
191+
- name: Prepare local layer
192+
run: |
193+
LAYER_DIR="/tmp/local-layers/LambdaAdapterLayerX86"
194+
mkdir -p "$LAYER_DIR/extensions"
195+
cp /tmp/layer-x86_64/bootstrap "$LAYER_DIR/"
196+
cp /tmp/layer-x86_64/lambda-adapter "$LAYER_DIR/extensions/"
197+
chmod +x "$LAYER_DIR/extensions/lambda-adapter" "$LAYER_DIR/bootstrap"
198+
199+
- name: Patch template to use local layer
200+
working-directory: examples/${{ matrix.example }}
201+
run: |
202+
sed -i 's|!Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:[0-9]*|/tmp/local-layers/LambdaAdapterLayerX86|' template.yaml
203+
204+
- name: Build
205+
working-directory: examples/${{ matrix.example }}
206+
run: sam build
207+
208+
- name: Start local API and verify
209+
working-directory: examples/${{ matrix.example }}
210+
run: |
211+
# Set PORT=8000 to avoid conflict with SAM's RIE on port 8080
212+
echo '{"Parameters":{"PORT":"8000"}}' > /tmp/env.json
213+
sam local start-api --port 3000 --warm-containers EAGER --container-env-vars /tmp/env.json &
214+
SAM_PID=$!
215+
echo "SAM_PID=$SAM_PID" >> $GITHUB_ENV
216+
217+
echo "Waiting for local API to start..."
218+
for i in $(seq 1 90); do
219+
if curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/ 2>/dev/null | grep -q "^[2345]"; then
220+
echo "API is ready after ${i}s"
221+
break
222+
fi
223+
if [ "$i" -eq 90 ]; then
224+
echo "API failed to start within 90s"
225+
kill $SAM_PID 2>/dev/null || true
226+
exit 1
227+
fi
228+
sleep 1
229+
done
230+
231+
status=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/)
232+
echo "HTTP status: $status"
233+
if [[ "$status" -ge 200 && "$status" -lt 500 ]]; then
234+
echo "OK: Got valid response"
235+
else
236+
echo "FAIL: Unexpected status $status"
237+
kill $SAM_PID 2>/dev/null || true
238+
exit 1
239+
fi
240+
241+
- name: Stop local API
242+
if: always()
243+
run: kill $SAM_PID 2>/dev/null || true

0 commit comments

Comments
 (0)