Skip to content

ci: retry npm ci in e2e workflow on transient failures #217

ci: retry npm ci in e2e workflow on transient failures

ci: retry npm ci in e2e workflow on transient failures #217

Workflow file for this run

name: E2E
on:
push:
branches: [main, development, nightly, 'feature/**']
pull_request:
branches: [main, development, nightly]
jobs:
e2e:
runs-on: ubuntu-latest
# No step here should ever legitimately run long — a genuine hang
# (e.g. total loss of runner connectivity) should fail fast instead
# of tying up the runner for hours. Without this, GitHub's default
# 360m job timeout is the only backstop.
timeout-minutes: 20
env:
# docker-compose.yml's base AUTH_SECRET is a required (`:?`) variable
# interpolated from the shell environment before docker-compose.e2e.yml's
# override ever merges in — so compose fails at parse time on a CI
# runner with no .env file, even though the override replaces this
# value with its own literal AUTH_SECRET for the actual container.
# Any non-empty placeholder satisfies the base file's interpolation.
AUTH_SECRET: unused-placeholder-overridden-by-docker-compose.e2e.yml
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Build and start the app (real Docker image, per docs/current_spec.md Decision 4)
run: docker compose -f docker-compose.yml -f docker-compose.e2e.yml build
- name: Start containers
run: docker compose -f docker-compose.yml -f docker-compose.e2e.yml up -d
- name: Wait for the app to become healthy
run: |
for i in $(seq 1 60); do
if curl -sf http://127.0.0.1:8080/api/health >/dev/null; then
echo "App is up"
exit 0
fi
sleep 2
done
echo "App did not become healthy in time" >&2
docker compose -f docker-compose.yml -f docker-compose.e2e.yml logs
exit 1
# Runs npm ci + the suite inside Microsoft's official Playwright
# image, whose tag is derived from the resolved @playwright/test
# version in frontend/package-lock.json so the pre-baked browsers
# always match the npm-installed client (a hardcoded tag silently
# drifts the moment a dep bump lands on one branch but not the
# other). This replaces `npx playwright install --with-deps` on
# the bare runner. The prior approach repeatedly hit severe
# `azure.archive.ubuntu.com` mirror flakiness fetching apt's font
# packages (one run saw a 107KB package take 7 minutes, then
# timed out entirely on the next one) — a real, recurring
# infrastructure flake, not something a longer timeout reliably
# fixes. This image ships every browser + system dependency
# pre-baked, so no apt install happens at all. `--network host`
# lets the container reach the app/Mailpit via the host ports
# docker-compose.e2e.yml already publishes, exactly like running
# Playwright natively on the runner would.
- name: Run e2e suite (official Playwright image — pre-baked browsers/deps)
timeout-minutes: 10
run: |
PW_VERSION=$(jq -r '.packages["node_modules/@playwright/test"].version' frontend/package-lock.json)
echo "Using Playwright image mcr.microsoft.com/playwright:v${PW_VERSION}-noble"
docker run --rm --network host \
-v "$PWD/frontend:/work" -w /work \
-e PLAYWRIGHT_BASE_URL \
"mcr.microsoft.com/playwright:v${PW_VERSION}-noble" \
bash -c '
for i in 1 2 3; do
npm ci && exit_code=0 && break || exit_code=$?
echo "npm ci failed (attempt $i/3), retrying..." >&2
sleep 5
done
[ "$exit_code" = 0 ] || exit "$exit_code"
npx playwright test
'
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: playwright-report
path: frontend/playwright-report/
retention-days: 7
- name: Tear down containers
if: always()
run: docker compose -f docker-compose.yml -f docker-compose.e2e.yml down -v