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
