Files
romm/.github/workflows/e2e.yml
T
Georges-Antoine AssiandClaude Opus 5.5 a874e4d012 ci: keep CI jobs off Ubuntu apt mirrors (#5182)
* ci: bound apt network timeouts for Playwright's dependency install

`npx playwright install --with-deps` runs its own `apt-get update`, so the
`-o` flags from #5181 could not reach it, and a stalled Ubuntu mirror hung
the frontend test job until the 15 minute limit cancelled it. Move the
retry/timeout options into an apt.conf.d file written by a small composite
action, used by setup-backend and the frontend test job.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* ci: install Playwright's headless shell without apt

The apt.conf.d timeouts did not stop the hang: on a runner whose Azure
mirror is unreachable, apt falls back to archive.ubuntu.com and stalls
while downloading the package indexes, slowly enough that no timeout
fires. The ubuntu-latest image already ships Chrome's system libraries,
so drop --with-deps from the frontend and e2e jobs and keep apt out of
their path. Revert the bound-apt-timeouts action, and run the frontend
checks when frontend.yml itself changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* ci: install setup-backend's apt packages from a cache

The MariaDB connector .debs (plus each job's extra apt packages) were
fetched from an Ubuntu mirror on every backend job, so a stalled mirror
could hang any of them until the 15 minute limit, the same way it hung
Playwright's dependency install. Cache the downloaded .debs keyed on the
runner image OS and package list, saved from master only, and install
from them without apt-get update on a hit. A miss still downloads them
with the #5181 retry and timeout options.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* ci: install cached apt packages without re-downloading them

With package lists present, apt fetched the mirror's copy of each local
.deb instead of installing the file, so even the cached path reached the
mirror. Point apt's archives dir at the .deb directory and pass
--no-download. Also read ImageOS in a shell step: it is a runner shell
variable, not in the `env` context, so the cache key came out empty.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

* ci: pin workflow runners to ubuntu-24.04

The apt .deb cache in setup-backend only holds packages built for one
Ubuntu release. Pin every job to ubuntu-24.04 so a GitHub move of
ubuntu-latest can't restore noble packages onto a newer image, and
hardcode the release in the cache key instead of reading ImageOS.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-07 16:56:47 -04:00

173 lines
5.9 KiB
YAML

name: Run E2E Tests
on:
pull_request:
paths:
- "frontend/**"
- "backend/**"
- ".github/workflows/e2e.yml"
- ".github/actions/setup-frontend/**"
- ".github/scripts/**"
- pyproject.toml
- uv.lock
- .python-version
push:
branches:
- "master"
paths:
- "frontend/**"
- "backend/**"
- ".github/workflows/e2e.yml"
- ".github/actions/setup-frontend/**"
- ".github/scripts/**"
- pyproject.toml
- uv.lock
- .python-version
# A new push cancels a PR's superseded run; each master commit keeps its own.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
e2e:
runs-on: ubuntu-24.04
timeout-minutes: 20
services:
mariadb:
image: mariadb:13.0.2
ports:
- 3306
env:
MYSQL_USER: romm_e2e
MYSQL_PASSWORD: passwd
MYSQL_DATABASE: romm_e2e
MYSQL_ROOT_PASSWORD: passwd
options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=5s --health-timeout=2s --health-retries=3 --health-start-period=30s
valkey:
image: valkey/valkey:9.0.6
ports:
- 6379
options: >-
--health-cmd="redis-cli ping" --health-interval=5s --health-timeout=2s --health-retries=3
env:
DEV_PORT: 5000
DB_HOST: 127.0.0.1
DB_NAME: romm_e2e
DB_USER: romm_e2e
DB_PASSWD: passwd
ROMM_DB_DRIVER: mariadb
REDIS_HOST: 127.0.0.1
# Throwaway signing key for an ephemeral runner
ROMM_AUTH_SECRET_KEY: "e2e0000000000000000000000000000000000000000000000000000000000000"
# Fixture-user password, shared by the seeder and the specs
E2E_PASSWORD: e2e-Passw0rd!
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up the backend
uses: ./.github/actions/setup-backend
- name: Set up the frontend
uses: ./.github/actions/setup-frontend
# Builds while the backend is set up; "Wait for the frontend build" checks it.
- name: Start the frontend build
run: |
(
npm run build > "$RUNNER_TEMP/frontend-build.log" 2>&1
echo $? > "$RUNNER_TEMP/frontend-build.tmp"
mv "$RUNNER_TEMP/frontend-build.tmp" "$RUNNER_TEMP/frontend-build.exit"
) &
working-directory: frontend
# Headless runs use only the headless shell, so skip the full Chrome build. The runner
# image already has its system libraries, and --with-deps can hang on a stalled apt mirror.
- name: Install Playwright browser
run: npx playwright install --only-shell chromium
working-directory: frontend
# ROMM_BASE_PATH must be writable (the app creates config/, resources/ and
# assets/ under it), so copy the fixture library out of the repo rather
# than scanning it in place.
- name: Stage the library fixture
env:
MARIADB_PORT: ${{ job.services.mariadb.ports['3306'] }}
VALKEY_PORT: ${{ job.services.valkey.ports['6379'] }}
run: |
mkdir -p "$RUNNER_TEMP/romm/resources" "$RUNNER_TEMP/romm/assets"
cp -R backend/romm_test/library "$RUNNER_TEMP/romm/library"
# The fixture library is laid out platform-folder-first, which its
# config.yml declares.
cp -R backend/romm_test/config "$RUNNER_TEMP/romm/config"
echo "ROMM_BASE_PATH=$RUNNER_TEMP/romm" >> "$GITHUB_ENV"
echo "DB_PORT=$MARIADB_PORT" >> "$GITHUB_ENV"
echo "REDIS_PORT=$VALKEY_PORT" >> "$GITHUB_ENV"
# A migration failure is its own red step rather than a confusing server-start failure.
- name: Apply migrations
run: uv run alembic upgrade head
working-directory: backend
- name: Seed the library
run: uv run python .github/scripts/seed_e2e_library.py
# Seed user and clear the setup wizard
- name: Seed the e2e fixture users
run: uv run python .github/scripts/seed_e2e_users.py
- name: Start the backend
run: |
uv run main.py > "$RUNNER_TEMP/backend.log" 2>&1 &
echo "Waiting for the API to answer..."
for i in $(seq 1 60); do
if curl -sf "http://127.0.0.1:${DEV_PORT}/api/heartbeat" > /dev/null; then
echo "Backend up after ${i}s"
exit 0
fi
sleep 1
done
echo "Backend did not come up; last 100 log lines:"
tail -100 "$RUNNER_TEMP/backend.log"
exit 1
working-directory: backend
- name: Wait for the frontend build
run: |
while [ ! -f "$RUNNER_TEMP/frontend-build.exit" ]; do sleep 2; done
cat "$RUNNER_TEMP/frontend-build.log"
exit "$(cat "$RUNNER_TEMP/frontend-build.exit")"
# Serves that build through playwright.config.ts's webServer.
- name: Run Playwright tests
run: npm run test:e2e
working-directory: frontend
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report
path: |
frontend/e2e/.output/specs/
retention-days: 7
- name: Dump backend log
if: failure()
run: tail -200 "$RUNNER_TEMP/backend.log" || true
# `uv run` holds the uv cache lock for as long as it lives, so leaving the
# backend up makes setup-uv's post-job `uv cache prune` block until the
# runner kills cleanup after 5 minutes, failing an otherwise green job.
- name: Stop the backend
if: always()
run: pkill -f "main.py" || true