Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 14 additions & 43 deletions .github/workflows/infra-compose-full-stack-smoke.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
name: full-stack-smoke

# Boots the entire dev stack (postgres + valkey + api-dev + ui-dev + traefik)
# via docker compose and exercises the register → login → /me happy path.
# Requires monorepo apps/api and apps/ui (compose build contexts).
# Triggered manually or on relevant pushes; slower than validate-compose.
# Boots the dev compose stack and exercises the login → /me → register →
# dashboard round-trip through curl. Catches real integration breakage
# (compose service wiring, Vite/nginx → API proxy, Postgres + Valkey
# readiness, auth cookie path) on every PR within a few minutes.
#
# Deliberately curl-only — the browser E2E surface lives in a separate
# advisory workflow (`infra-compose-playwright-e2e.yml`) so a Playwright
# flake or browser quirk doesn't block PR merges through this required
# check.

on:
push:
Expand Down Expand Up @@ -35,7 +40,11 @@ jobs:
smoke:
name: register → login → /me through the full stack
runs-on: ubuntu-latest
timeout-minutes: 20
# 8 min upper bound: with Playwright moved out, the curl-only path
# completes in ~3–4 min on a warm runner. A short ceiling means a
# stuck step fails fast instead of running for 20 min like the old
# combined gate.
timeout-minutes: 8

steps:
- name: Checkout monorepo
Expand Down Expand Up @@ -163,33 +172,6 @@ jobs:
exit 1
}

- name: Set up Bun
if: steps.filter.outputs.code == 'true'
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14

- name: Check UI OpenAPI schema is up to date
if: steps.filter.outputs.code == 'true'
working-directory: apps/ui
run: |
# The ui-dev container runs as root and writes into the bind-mounted
# apps/ui node_modules from the container, so the host runner can't recreate it.
# Drop root-owned bits before installing as the runner user.
sudo rm -rf node_modules
bun install --frozen-lockfile
bun run generate:api:check

- name: Run UI Playwright E2E
if: steps.filter.outputs.code == 'true'
working-directory: apps/ui
timeout-minutes: 12
env:
PLAYWRIGHT_REUSE_SERVER: "true"
run: |
bun run e2e:install:chromium
bun run e2e:ci:chromium

- name: Smoke — register a brand-new user
if: steps.filter.outputs.code == 'true'
run: |
Expand Down Expand Up @@ -228,17 +210,6 @@ jobs:
docker logs "$c" 2>&1 || true
done

- name: Upload Playwright artifacts on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-results
path: |
apps/ui/test-results
apps/ui/playwright-report
if-no-files-found: ignore
retention-days: 7

- name: Tear down
if: always()
working-directory: infra/compose/compose
Expand Down
151 changes: 151 additions & 0 deletions .github/workflows/infra-compose-playwright-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
name: playwright-e2e

# Browser-based end-to-end smoke. Boots the same compose stack as
# `full-stack-smoke`, then runs the apps/ui Playwright Chromium suite
# against it.
#
# NOT a required check in branch protection — intentionally advisory.
# The Playwright surface flakes occasionally (browser timing, animation,
# transient network) and forcing every PR to wait on a re-run is more
# friction than the signal is worth. The fast curl smoke
# (`full-stack-smoke`) gates merges; this workflow catches the
# regressions curl can't see, and a failure here means "go look".
#
# Path-filtered so docs-only PRs don't waste runner time.

on:
push:
branches: [main]
paths:
- "infra/compose/**"
- "apps/api/**"
- "apps/ui/**"
- ".github/workflows/infra-compose-playwright-e2e.yml"
pull_request:
paths:
- "infra/compose/**"
- "apps/api/**"
- "apps/ui/**"
- ".github/workflows/infra-compose-playwright-e2e.yml"
workflow_dispatch:

concurrency:
group: infra-compose-playwright-e2e-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
playwright:
name: UI Playwright E2E (browser-based smoke)
runs-on: ubuntu-latest
# 15 min upper bound: Playwright's own inner timeout is 12 min; the
# buffer covers compose build + image pulls + browser install. A
# genuine hang surfaces within minutes of the inner timeout rather
# than the runner's default 6 h.
timeout-minutes: 15

steps:
- name: Checkout monorepo
uses: actions/checkout@v6

- name: Seed compose/.env
working-directory: infra/compose/compose
run: |
cp .env.example .env
{
echo ""
echo "# Playwright E2E only — never use these in real deployments."
echo "SUPERUSER_EMAIL=demo@example.com"
echo "SUPERUSER_PASSWORD=password123"
} >> .env

- name: Make scripts executable
run: |
chmod +x infra/compose/compose/dev.sh
chmod +x infra/compose/scripts/*.sh

- name: Boot the smoke stack
working-directory: infra/compose/compose
env:
STACK: smoke
run: ./dev.sh up -d --build

- name: Wait for API health
run: |
for i in {1..60}; do
if curl -fsS http://localhost:7330/health > /dev/null 2>&1; then
echo "API healthy after ${i}s"
break
fi
sleep 1
done
curl -fsS http://localhost:7330/health

- name: Wait for UI
run: |
for i in {1..60}; do
if curl -fsS http://localhost:7331/ > /dev/null 2>&1; then
echo "UI ready after ${i}s"
break
fi
sleep 1
done
curl -fsSI http://localhost:7331/ | head -1

- name: Set up Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14

- name: Install apps/ui deps
working-directory: apps/ui
run: |
# The ui-dev container runs as root and writes into the bind-mounted
# apps/ui node_modules from the container, so the host runner can't
# recreate it. Drop root-owned bits before installing as the runner user.
sudo rm -rf node_modules
bun install --frozen-lockfile

- name: Run UI Playwright E2E
working-directory: apps/ui
timeout-minutes: 12
env:
PLAYWRIGHT_REUSE_SERVER: "true"
run: |
bun run e2e:install:chromium
bun run e2e:ci:chromium

- name: Compose ps on failure
if: failure()
working-directory: infra/compose/compose
run: |
docker compose -f docker-compose.yml -f docker-compose.development-labels.yml --profile smoke ps

- name: Container logs on failure
if: failure()
run: |
for c in $(docker ps -aq); do
echo "===== $(docker inspect --format '{{.Name}}' "$c") ====="
docker logs "$c" 2>&1 || true
done

- name: Upload Playwright artifacts on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-results
path: |
apps/ui/test-results
apps/ui/playwright-report
if-no-files-found: ignore
retention-days: 7

- name: Tear down
if: always()
working-directory: infra/compose/compose
env:
STACK: smoke
run: |
./dev.sh down -v || true
Loading