.woodpecker/main.yaml — CI test-FTs step splits into parallel siblings test-FTs-non-room (22 files via `ls functional_tests/test_*.py | grep -v 'test_game_room_'`) + test-FTs-room (9 files via `ls functional_tests/test_game_room_*.py`); room cluster is the heaviest (~70% of the pre-split ~40-min wall-clock) and now runs concurrently w. the rest instead of in series; DAG explicit via depends_on on every step (Woodpecker mixes default-sequential w. depends_on awkwardly, so each step pins its prerequisite); collectstatic stays in test-two-browser-FTs only — the shared workspace propagates assets to both parallel FT steps, no race + no duplication; screendumps + build-and-push fan back in (depends_on both parallel steps); deploy-staging + deploy-prod depend on build-and-push smoke-import: 31/31 FT modules green after the rename pass Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
209 lines
7.1 KiB
YAML
209 lines
7.1 KiB
YAML
services:
|
|
- name: postgres
|
|
image: postgres:16
|
|
environment:
|
|
POSTGRES_DB: python_tdd_test
|
|
POSTGRES_USER: postgres
|
|
POSTGRES_PASSWORD: postgres
|
|
|
|
- name: redis
|
|
image: redis:7
|
|
|
|
steps:
|
|
- name: test-UTs-n-ITs
|
|
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
|
|
environment:
|
|
DATABASE_URL: postgresql://postgres:postgres@postgres/python_tdd_test
|
|
CELERY_BROKER_URL: redis://redis:6379/0
|
|
REDIS_URL: redis://redis:6379/1
|
|
PIP_CACHE_DIR: .pip-cache
|
|
commands:
|
|
# `requirements.dev.txt` is the pinned superset Dockerfile.ci pre-
|
|
# installs; pinning here means pip skips resolver+download and just
|
|
# verifies "already satisfied" (~5-10s) instead of resolving unpinned
|
|
# requirements.txt against PyPI from scratch (~30-60s). Drift safety
|
|
# net: if requirements.dev.txt has changed since the CI image was
|
|
# last rebuilt + pushed, pip installs the delta — slower for that
|
|
# run but never broken. See TDD SKILL.md § CI dependency discipline.
|
|
- pip install -r requirements.dev.txt
|
|
- cd ./src
|
|
- python manage.py test apps
|
|
when:
|
|
- event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: test-two-browser-FTs
|
|
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
|
|
depends_on:
|
|
- test-UTs-n-ITs
|
|
environment:
|
|
HEADLESS: 1
|
|
CELERY_BROKER_URL: redis://redis:6379/0
|
|
REDIS_URL: redis://redis:6379/1
|
|
STRIPE_SECRET_KEY:
|
|
from_secret: stripe_secret_key
|
|
STRIPE_PUBLISHABLE_KEY:
|
|
from_secret: stripe_publishable_key
|
|
PIP_CACHE_DIR: .pip-cache
|
|
commands:
|
|
- pip install -r requirements.dev.txt
|
|
- cd ./src
|
|
# Also collectstatic'd here; output sits in the shared workspace so
|
|
# the downstream parallel FT steps don't have to repeat it.
|
|
- python manage.py collectstatic --noinput
|
|
- python manage.py test functional_tests --tag=two-browser
|
|
- python manage.py test functional_tests --tag=sequential
|
|
- python manage.py test functional_tests --tag=channels
|
|
when:
|
|
- event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
# ── Parallel FT split ─────────────────────────────────────────────────
|
|
#
|
|
# test_game_room_* is the heaviest cluster — 9 Selenium-driven room-flow
|
|
# FTs that historically dominate the FT step wall-clock (~70% of the
|
|
# ~40-min single-step runs). Split off into its own step (`test-FTs-room`)
|
|
# that runs in parallel with the rest (`test-FTs-non-room`). Both depend
|
|
# on test-two-browser-FTs (which leaves collectstatic'd assets in the
|
|
# shared workspace), so neither parallel step re-runs collectstatic.
|
|
#
|
|
# Tradeoff: 2 concurrent Selenium/Firefox containers on the runner
|
|
# instead of 1, in exchange for roughly halved test-FT wall-clock when
|
|
# the partition stays balanced. If the runner thrashes, drop the
|
|
# `depends_on` from one to serialize them again.
|
|
|
|
- name: test-FTs-non-room
|
|
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
|
|
depends_on:
|
|
- test-two-browser-FTs
|
|
environment:
|
|
HEADLESS: 1
|
|
CELERY_BROKER_URL: redis://redis:6379/0
|
|
REDIS_URL: redis://redis:6379/1
|
|
STRIPE_SECRET_KEY:
|
|
from_secret: stripe_secret_key
|
|
STRIPE_PUBLISHABLE_KEY:
|
|
from_secret: stripe_publishable_key
|
|
PIP_CACHE_DIR: .pip-cache
|
|
commands:
|
|
- pip install -r requirements.dev.txt
|
|
- cd ./src
|
|
# Every FT file EXCEPT test_game_room_* — that cluster runs in
|
|
# test-FTs-room. Channels + two-browser tags already covered upstream.
|
|
# `ls | grep -v | sed` enumerates module dotted-paths from filenames.
|
|
- python manage.py test --parallel --exclude-tag=channels --exclude-tag=two-browser $(ls functional_tests/test_*.py | grep -v 'test_game_room_' | sed 's|/|.|g;s|\.py||')
|
|
when:
|
|
- event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: test-FTs-room
|
|
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
|
|
depends_on:
|
|
- test-two-browser-FTs
|
|
environment:
|
|
HEADLESS: 1
|
|
CELERY_BROKER_URL: redis://redis:6379/0
|
|
REDIS_URL: redis://redis:6379/1
|
|
STRIPE_SECRET_KEY:
|
|
from_secret: stripe_secret_key
|
|
STRIPE_PUBLISHABLE_KEY:
|
|
from_secret: stripe_publishable_key
|
|
PIP_CACHE_DIR: .pip-cache
|
|
commands:
|
|
- pip install -r requirements.dev.txt
|
|
- cd ./src
|
|
# Heavy Selenium room flows — 9 files (deck_contrib, gatekeeper,
|
|
# invite, select_role/sea/sig/sky, tray, tray_tooltip) isolated into
|
|
# their own parallel sub-step.
|
|
- python manage.py test --parallel --exclude-tag=channels --exclude-tag=two-browser $(ls functional_tests/test_game_room_*.py | sed 's|/|.|g;s|\.py||')
|
|
when:
|
|
- event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: screendumps
|
|
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
|
|
depends_on:
|
|
- test-FTs-non-room
|
|
- test-FTs-room
|
|
commands:
|
|
- cat ./src/functional_tests/screendumps/*.html || echo "No screendumps found"
|
|
when:
|
|
- event: push
|
|
status: failure
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: build-and-push
|
|
image: docker:cli
|
|
depends_on:
|
|
- test-FTs-non-room
|
|
- test-FTs-room
|
|
environment:
|
|
REGISTRY_PASSWORD:
|
|
from_secret: gitea_registry_password
|
|
commands:
|
|
- echo "$REGISTRY_PASSWORD" | docker login gitea.earthmanrpg.me -u discoman --password-stdin
|
|
- docker build -t gitea.earthmanrpg.me/discoman/gamearray:latest .
|
|
- docker push gitea.earthmanrpg.me/discoman/gamearray:latest
|
|
when:
|
|
- branch: main
|
|
event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- "Dockerfile"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: deploy-staging
|
|
image: alpine
|
|
depends_on:
|
|
- build-and-push
|
|
environment:
|
|
SSH_KEY:
|
|
from_secret: deploy_ssh_key
|
|
commands:
|
|
- apk add --no-cache openssh-client
|
|
- mkdir -p ~/.ssh
|
|
- printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519
|
|
- chmod 600 ~/.ssh/id_ed25519
|
|
- ssh -o StrictHostKeyChecking=no discoman@staging.earthmanrpg.me /opt/gamearray/deploy.sh
|
|
when:
|
|
- branch: main
|
|
event: push
|
|
path:
|
|
- "src/**"
|
|
- "requirements.txt"
|
|
- "Dockerfile"
|
|
- "infra/**"
|
|
- ".woodpecker/main.yaml"
|
|
|
|
- name: deploy-prod
|
|
image: alpine
|
|
depends_on:
|
|
- build-and-push
|
|
environment:
|
|
SSH_KEY:
|
|
from_secret: deploy_ssh_key
|
|
commands:
|
|
- apk add --no-cache openssh-client
|
|
- mkdir -p ~/.ssh
|
|
- printf '%s\n' "$SSH_KEY" > ~/.ssh/id_ed25519
|
|
- chmod 600 ~/.ssh/id_ed25519
|
|
- ssh -o StrictHostKeyChecking=no discoman@staging.earthmanrpg.me /opt/gamearray/deploy.sh
|
|
when:
|
|
- event: tag
|