From 3470c91e3a6777ffac2ffa56d04ace5b65454c81 Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sun, 31 May 2026 19:54:51 +0900 Subject: [PATCH] ci: fix recurring Verify Examples workflow failures The "Verify Examples" workflow had been failing on every run. Log analysis across many runs surfaced three independent root causes: 1. validate job: nextjs-zip and remix-zip pinned the EOL runtime nodejs20.x (deprecated 2026-04-30), so `sam validate --lint` matched cfn-lint rule W2531 and failed every run. Bumped both to nodejs22.x. EOL deprecations are intentionally left to fail CI so example runtimes get updated promptly when they age out. 2. test-image(fasthtml) / test-zip(fasthtml-zip): requirements.txt left python-fasthtml unpinned. Newer releases (>=0.14.0) dropped `Card` from fasthtml.common, so the app raised `NameError: name 'Card' is not defined` and returned HTTP 500. Pinned to ==0.13.4 (latest release that still exports Card and installs cleanly on python:3.12-slim). Verified end-to-end: the real app's index route now returns HTTP 200. 3. test-image/test-zip build steps: intermittent `toomanyrequests: Rate exceeded` while sam build pulled base images from public.ecr.aws. Wrapped both `sam build` invocations in a 3-attempt retry loop. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/examples.yaml | 40 ++++++++++++++++++++-- examples/fasthtml-zip/app/requirements.txt | 2 +- examples/fasthtml/app/requirements.txt | 2 +- examples/nextjs-zip/template.yaml | 2 +- examples/remix-zip/template.yaml | 2 +- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/.github/workflows/examples.yaml b/.github/workflows/examples.yaml index 392eec8b..0c196a5e 100644 --- a/.github/workflows/examples.yaml +++ b/.github/workflows/examples.yaml @@ -119,7 +119,25 @@ jobs: - name: Build working-directory: examples/${{ matrix.example }} - run: sam build + 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 }} @@ -201,7 +219,25 @@ jobs: - name: Build working-directory: examples/${{ matrix.example }} - run: sam build + 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 }} diff --git a/examples/fasthtml-zip/app/requirements.txt b/examples/fasthtml-zip/app/requirements.txt index 3a46ffff..8afeaefa 100644 --- a/examples/fasthtml-zip/app/requirements.txt +++ b/examples/fasthtml-zip/app/requirements.txt @@ -1 +1 @@ -python-fasthtml +python-fasthtml==0.13.4 diff --git a/examples/fasthtml/app/requirements.txt b/examples/fasthtml/app/requirements.txt index 3a46ffff..8afeaefa 100644 --- a/examples/fasthtml/app/requirements.txt +++ b/examples/fasthtml/app/requirements.txt @@ -1 +1 @@ -python-fasthtml +python-fasthtml==0.13.4 diff --git a/examples/nextjs-zip/template.yaml b/examples/nextjs-zip/template.yaml index c6ae6e3f..5b79bf9f 100644 --- a/examples/nextjs-zip/template.yaml +++ b/examples/nextjs-zip/template.yaml @@ -16,7 +16,7 @@ Resources: CodeUri: app/ MemorySize: 256 Handler: run.sh - Runtime: nodejs20.x + Runtime: nodejs22.x Architectures: - x86_64 Environment: diff --git a/examples/remix-zip/template.yaml b/examples/remix-zip/template.yaml index eb10fccc..6a45c8a1 100644 --- a/examples/remix-zip/template.yaml +++ b/examples/remix-zip/template.yaml @@ -16,7 +16,7 @@ Resources: Properties: CodeUri: . Handler: run.sh - Runtime: nodejs20.x + Runtime: nodejs22.x MemorySize: 1024 Architectures: - x86_64