mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-02 01:57:58 +09:00
A name used before it exists only fails when that line runs, so a rarely-taken branch ships broken. Two routes 500'd on every call and Stop crashed on compose-slot apps, both found by pyflakes in seconds. The check now runs in the ratchet job and in preflight.
169 lines
7.0 KiB
YAML
169 lines
7.0 KiB
YAML
name: Backend CI
|
|
|
|
# Runs the full backend pytest suite on every change to backend/. This is the
|
|
# net that catches regressions in the panel — including the agent gateway,
|
|
# registry, pairing, and the panel<->agent command loop exercised end-to-end
|
|
# by tests/test_agent_poll_e2e.py — instead of finding out at install time.
|
|
|
|
# main is deliberately absent from `push`: release.yml gates itself on this
|
|
# workflow via `ci-gate`, so a push to main already runs the suite once. Listing
|
|
# main here ran it a second time, standalone, on every merge.
|
|
on:
|
|
push:
|
|
branches: [dev]
|
|
paths:
|
|
- 'backend/**'
|
|
- '.github/workflows/backend-ci.yml'
|
|
pull_request:
|
|
branches: [main, dev]
|
|
paths:
|
|
- 'backend/**'
|
|
- '.github/workflows/backend-ci.yml'
|
|
workflow_call:
|
|
|
|
jobs:
|
|
# The ratchet needs to see the WHOLE suite, so it cannot live inside a shard —
|
|
# each shard only collects its own quarter. Its own job costs no wall-clock
|
|
# time: it runs beside the shards and finishes in seconds.
|
|
ratchet:
|
|
name: test-count ratchet
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
cache: pip
|
|
cache-dependency-path: backend/requirements.txt
|
|
- name: Install dependencies
|
|
working-directory: backend
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install pytest
|
|
- name: Test-count ratchet
|
|
# Fails if collected-test count drops below backend/tests/BASELINE_COUNT.
|
|
# Converts "code + its tests vanished together" (the 2026-07 data loss,
|
|
# where a green suite hid ~363 lost tests) from invisible to a red X.
|
|
# Lowering the floor requires editing BASELINE_COUNT in the same commit.
|
|
working-directory: backend
|
|
run: python tests/check_test_count.py
|
|
- name: Undefined names
|
|
# A name used before it exists only fails when that line runs, so a
|
|
# rarely-taken branch ships broken: two routes that always 500'd on a
|
|
# missing import, and a Stop button that crashed on a shadowing local
|
|
# import. Pyflakes catches both statically in about a second.
|
|
working-directory: backend
|
|
run: |
|
|
pip install ruff==0.14.14
|
|
ruff check --select F821,F823 app tests
|
|
|
|
# Sharded because this job WAS the entire wait: 21m50s of a ~22m pipeline,
|
|
# while every other workflow finished inside a minute. Splitting the suite
|
|
# over 4 runners cuts the critical path to roughly a quarter.
|
|
#
|
|
# Sharding rather than pytest-xdist is deliberate: each shard is its own VM,
|
|
# so the process-shared state that makes in-process parallelism unsafe here
|
|
# (templates.json, APPS_DIR — see the per-PID DB dance in tests/conftest.py)
|
|
# simply isn't shared. No test code has to change.
|
|
#
|
|
# NOTE: with no .test_durations file, pytest-split balances by test COUNT,
|
|
# not by time — and these tests range from 0.1s to 10s+. If one shard lands
|
|
# most of the slow ones it becomes the new critical path. If that shows up,
|
|
# run once with `--store-durations` and commit backend/.test_durations to
|
|
# switch it to duration-based balancing.
|
|
pytest:
|
|
name: pytest (${{ matrix.group }}/4)
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
# Report every failing shard in one pass instead of hiding shards 2-4
|
|
# behind the first failure — one round of fixes instead of four.
|
|
fail-fast: false
|
|
matrix:
|
|
group: [1, 2, 3, 4]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
cache: pip
|
|
cache-dependency-path: backend/requirements.txt
|
|
- name: Install dependencies
|
|
working-directory: backend
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install pytest pytest-split
|
|
- name: Run tests (shard ${{ matrix.group }} of 4)
|
|
# Scoped to `tests` rather than a bare `pytest`. This collects the same
|
|
# suite in CI (backend/dev-data/ is gitignored), while keeping the
|
|
# command reproducible on a dev box: a
|
|
# bare pytest there tries to collect the locally deployed apps under
|
|
# backend/dev-data/ and dies during collection. Copy this line verbatim
|
|
# to debug a red shard locally.
|
|
working-directory: backend
|
|
run: python -m pytest tests -v --splits 4 --group ${{ matrix.group }}
|
|
|
|
# The @real_binaries leg (plan 82 §E): the same service code the shards run
|
|
# against fake_subprocess meets the REAL parsers here — the panel's
|
|
# generated crontab text goes through `crontab -`/`crontab -l`, and every
|
|
# rendered vhost flavor through `nginx -t`. This is the class of bug the
|
|
# #117 cron fix taught us write-only assertions cannot see. The tests skip
|
|
# themselves everywhere unless SERVERKIT_REAL_BINARIES=1, so the shards
|
|
# above never mutate a runner's crontab by surprise.
|
|
real-binaries:
|
|
name: real binaries
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
cache: pip
|
|
cache-dependency-path: backend/requirements.txt
|
|
- name: Install nginx and cron
|
|
run: |
|
|
sudo apt-get update -qq
|
|
sudo apt-get install -y -qq nginx-core cron
|
|
- name: Install dependencies
|
|
working-directory: backend
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install pytest
|
|
- name: Run real-binaries tests
|
|
working-directory: backend
|
|
env:
|
|
SERVERKIT_REAL_BINARIES: '1'
|
|
run: python -m pytest tests -m real_binaries -v
|
|
|
|
# The @docker_builds leg: generated buildpack Dockerfiles meet the REAL
|
|
# docker daemon — detect() -> generate_dockerfile() -> `docker build` ->
|
|
# `docker run` -> HTTP probe, per stack. Same lesson as real-binaries:
|
|
# test_buildpacks.py asserts on Dockerfile TEXT, which cannot see a bug
|
|
# that only fails at build or boot time (the `npm install --omit=dev ||
|
|
# npm install` dead fallback exited 0 and shipped `vite: not found` to a
|
|
# user's deploy). The tests skip themselves everywhere unless
|
|
# SERVERKIT_DOCKER_BUILDS=1, so the shards above never pull images.
|
|
docker-builds:
|
|
name: docker builds
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/setup-python@v5
|
|
with:
|
|
python-version: '3.11'
|
|
cache: pip
|
|
cache-dependency-path: backend/requirements.txt
|
|
- name: Install dependencies
|
|
working-directory: backend
|
|
run: |
|
|
python -m pip install --upgrade pip
|
|
pip install -r requirements.txt
|
|
pip install pytest
|
|
- name: Run docker-builds tests
|
|
working-directory: backend
|
|
env:
|
|
SERVERKIT_DOCKER_BUILDS: '1'
|
|
run: python -m pytest tests -m docker_builds -v
|