Files
python-tdd/.woodpecker/main.yaml
Disco DeDisco df9cf1eee8
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
CI: route trinket FTs to test-FTs-room stage alongside game_room_*
User ask: ensure trinket FTs (test_trinket_carte_blanche.py, test_trinket_coin_on_a_string.py, test_trinket_backstage_pass.py) run in the room-stage CI bucket so they exercise the same room-template surface as test_game_room_* — relevant when a sprint touches the table hex SCSS / chair geometry / gatekeeper flow, since trinket FTs create rooms + walk thru the gate. Previously they fell into test-FTs-non-room by elimination (the non-room glob was `grep -v 'test_game_room_'`) ; main.yaml updates: non-room step's grep is now `-vE 'test_(game_room|trinket)_'` to exclude both clusters; room step's ls now globs BOTH `test_game_room_*.py` AND `test_trinket_*.py` ; companion memory tweak in feedback_ft_naming_prefix.md documents the new convention so future trinket FTs end up in the right bucket without re-asking ; pipeline behavior unchanged otherwise — both steps still depend_on test-two-browser-FTs only, parallel re-enable still blocked by the shared-sqlite issue from 2026-05-12 (cf. memo note)

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 19:41:37 -04:00

230 lines
8.4 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 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"
# ── FT split (stage-parallel, intra-stage sequential) ─────────────────
#
# 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`)
# so the partition is visible in the pipeline view; the non-room bucket
# is `test-FTs-non-room`. Both depend on test-two-browser-FTs only, so
# they fan out + run concurrently.
#
# The previous SQLite-collision blocker (pipeline #296: second step
# started against the first step's half-created `src/test_db.sqlite3`
# → Django interactive prompt → EOFError under non-interactive CI
# stdin) is resolved by giving each step a distinct `DATABASE_URL`
# pointing at its own sqlite file under /tmp — outside the shared
# workspace mount so the two stages can't see each other's DB.
#
# `--parallel` is dropped from both steps. Empirically (pipelines
# #302-304) it was giving ~1-1.5x speedup at most on these Selenium
# FTs — Firefox spawn cost + RAM pressure + SQLite file-lock contention
# eat most of the gain — while amplifying every transient-DOM flake
# (login-race, gecko-perms, ElementNotInteractable, Jasmine-timeout).
# Stage-level parallelism gives the same wall-clock reduction without
# contention amplification.
- name: test-FTs-non-room
image: gitea.earthmanrpg.me/discoman/python-tdd-ci:latest
depends_on:
- test-two-browser-FTs
environment:
HEADLESS: 1
# /tmp path (not workspace-relative) so the parallel test-FTs-room
# step can't see this DB + vice versa. See split-rationale above.
DATABASE_URL: sqlite:////tmp/test_db_non_room.sqlite3
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_* and test_trinket_* — both
# clusters run 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 --exclude-tag=channels --exclude-tag=two-browser $(ls functional_tests/test_*.py | grep -vE 'test_(game_room|trinket)_' | 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
# /tmp path (not workspace-relative) so test-FTs-non-room can't see
# this DB + vice versa. See split-rationale above.
DATABASE_URL: sqlite:////tmp/test_db_room.sqlite3
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 — test_game_room_* (deck_contrib,
# gatekeeper, invite, select_role/sea/sig/sky, tray, tray_tooltip)
# AND test_trinket_* (carte_blanche, coin_on_a_string, backstage_pass)
# since trinket FTs create rooms + load the room template (where the
# table hex SCSS + chair geometry live), so they exercise the same
# surface as test_game_room_*. Runs in parallel w. test-FTs-non-room
# (distinct DATABASE_URL paths under /tmp; see split-rationale).
- python manage.py test --exclude-tag=channels --exclude-tag=two-browser $(ls functional_tests/test_game_room_*.py functional_tests/test_trinket_*.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