Skip to content

refactor: make Webhook Detail acquisition truthful #408

refactor: make Webhook Detail acquisition truthful

refactor: make Webhook Detail acquisition truthful #408

Workflow file for this run

name: Nightly E2E
# Exercises the full published stack (dashboard image + cycles-server +
# cycles-server-admin + redis) through the dashboard's built-in nginx.
# Catches plumbing-layer defects that unit tests and Vite-based dev work
# don't see — specifically the class of bugs that produced v0.1.25.22's
# nginx proxy_pass path-stripping regression.
# Cancel in-flight runs when a newer commit lands on the same ref.
# Three rapid pushes to the same PR don't need three parallel compose
# spin-ups — the latest one supersedes the others.
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
on:
schedule:
# Daily at 06:00 UTC — off-hours for most contributors and avoids
# the GitHub Actions queue rush at the top of the hour.
- cron: '0 6 * * *'
workflow_dispatch:
# Also run on PRs that touch the highest-risk plumbing files, so
# regressions are caught at review time. Scoped narrowly on purpose —
# we don't want to add 3+ minutes of compose stack spin-up to every PR.
pull_request:
paths:
- 'default.conf.template'
- 'Dockerfile'
- 'docker-compose*.yml'
- 'src/api/client.ts'
- 'src/views/**'
- 'src/components/**'
# Style changes can break visual rendering (e.g. dark-mode cascade
# regressions from @apply-inlined component classes) — Vitest and
# axe both miss these, Playwright flows catch them by running the
# actual dashboard in a real browser.
- 'src/style.css'
- 'src/stores/**'
- 'src/router.ts'
- '.github/workflows/e2e.yml'
- 'scripts/e2e-probes.sh'
- 'tests/e2e/**'
- 'playwright.config.ts'
permissions: read-all
jobs:
e2e:
name: Compose stack + HTTP probes
runs-on: ubuntu-latest
permissions:
contents: read
# Needed for ghcr.io image pulls (cycles-server, cycles-server-admin).
packages: read
timeout-minutes: 15
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
# Authenticate to ghcr so we can pull the backend images. The
# dashboard image is built locally from this checkout so we test
# the current source, not a previously-published build.
- name: Log in to ghcr.io
uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4
# Build the dashboard image from current source. docker-compose.yml
# uses `build: .` for the dashboard service, so `compose up --build`
# will pick this up; explicit build step keeps build output in the
# logs for easier diagnosis on failure.
- name: Build dashboard image
run: docker compose -f docker-compose.yml build dashboard
# `--wait` blocks until every service is "ready" — healthy for
# services with a healthcheck defined, started for ones without.
# This is exactly the semantics we want, and matches what
# `depends_on: condition: service_healthy` uses internally.
# `--wait-timeout 120` caps cold-start waits at 2 minutes; each
# JVM service declares start_period: 30s + interval: 10s, so 120s
# is a comfortable ceiling on a CI runner. Without --wait, we
# previously hand-rolled a polling loop that misread services
# lacking a healthcheck (e.g. dashboard) as "never healthy" and
# looped forever.
- name: Start compose stack (waits for ready)
env:
ADMIN_API_KEY: admin-bootstrap-key
run: |
docker compose -f docker-compose.yml up -d --wait --wait-timeout 120
docker compose -f docker-compose.yml ps
- name: Run E2E probes
env:
ADMIN_API_KEY: admin-bootstrap-key
DASHBOARD_URL: http://localhost:8080
run: bash scripts/e2e-probes.sh
# Playwright runs after the HTTP probes pass. Probes catch
# routing/plumbing regressions; Playwright catches JS-layer bugs
# (e.g. the v0.1.25.22 sort-accessor class). Both layers share
# the same compose stack — no extra spin-up cost.
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: 20
cache: npm
- name: Install npm dependencies
run: npm ci
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install --with-deps chromium
- name: Install Playwright system deps (when cached)
if: steps.playwright-cache.outputs.cache-hit == 'true'
run: npx playwright install-deps chromium
- name: Run Playwright tests
env:
ADMIN_API_KEY: admin-bootstrap-key
DASHBOARD_URL: http://localhost:8080
CI: 'true'
run: npx playwright test
# On failure, surface each container's logs as a workflow artifact
# so the person triaging doesn't need to reproduce locally.
- name: Capture compose logs on failure
if: failure()
run: |
mkdir -p e2e-logs
for svc in dashboard cycles-admin cycles-server redis; do
docker compose -f docker-compose.yml logs "$svc" > "e2e-logs/$svc.log" 2>&1 || true
done
docker compose -f docker-compose.yml ps > e2e-logs/compose-ps.txt 2>&1 || true
- name: Upload compose logs on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: e2e-compose-logs
path: e2e-logs/
retention-days: 14
# Playwright produces its own HTML report + traces/screenshots/
# videos (retained on failure per playwright.config.ts). Upload
# separately so triage can open the Playwright report directly
# and click through to the failing step's trace.
- name: Upload Playwright report on failure
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: playwright-report
path: |
playwright-report/
test-results/
retention-days: 14
- name: Tear down
if: always()
run: docker compose -f docker-compose.yml down -v