07.11 — CI#

Three workflows: main_ci.yml (fast, lint + typecheck), dev_ci.yml (same for dev/feature/fix branches), e2e.yml (manual, Redis service + Firefox).

Overview#

WorkflowTriggerPlatformEffect
main_ci.ymlpush/PR mainubuntu + windows × py 3.14make check-coding-style (mypy + ruff). No browser.
dev_ci.ymlpush dev / feature/_ / fix/_ubuntu × py 3.14id., but Ubuntu only
e2e.ymlworkflow_dispatch (manual)ubuntu + Redis service + Firefox + geckodriverFull run of the e2e suite. Upload .screenshots/, .ocarina_logs/, .reports/.

main_ci.yml#

name: Main branch CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

defaults:
  run:
    shell: bash

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        python-version: ["3.14"]
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with: { python-version: "3.14" }
      - uses: actions/cache@v5
        with:
          path: .venv
          key: venv-${{ runner.os }}-py3.14-${{ hashFiles('pyproject.toml') }}
      - name: Install
        run: make install-on-ci
      - name: Check coding style
        run: |
          . .venv/bin/activate
          make check-coding-style
  1. OS matrix: ubuntu + windows — verifies Python portability.
  2. No browser: just make check-coding-style (mypy + ruff). Fast, no Selenium / Redis / OTP.
  3. .venv cache: reused between runs.

dev_ci.yml#

on:
  push:
    branches: [dev, "feature/**", "fix/**"]
  workflow_dispatch:

jobs:
  test:
    runs-on: ubuntu-latest
    # … identical to main_ci.yml, just Ubuntu only …

Identical to main_ci.yml but Ubuntu only (Windows only runs on main).

e2e.yml#

name: e2e CI

on:
  workflow_dispatch:

jobs:
  e2e:
    runs-on: ubuntu-latest
    environment: OC # ← GitHub env that holds IGOR_API_KEY

    services:
      redis:
        image: redis:8.8.0
        ports: [6379:6379]
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-python@v6
        with: { python-version: 3.14 }
      - uses: actions/cache@v5
        with:
          path: .venv
          key: venv-${{ runner.os }}-py-${{ hashFiles('pyproject.toml') }}

      - name: Install geckodriver
        run: |
          GECKO_VERSION=0.35.0
          wget https://github.com/mozilla/geckodriver/releases/download/v$GECKO_VERSION/geckodriver-v$GECKO_VERSION-linux64.tar.gz
          tar -xvzf geckodriver-v$GECKO_VERSION-linux64.tar.gz
          chmod +x geckodriver

      - name: Setup Firefox
        uses: browser-actions/setup-firefox@...

      - name: Install project + ocarina
        run: |
          make install-on-ci

      - name: Run e2e cycle
        env:
          IGOR_API_KEY: ${{ secrets.IGOR_API_KEY }}
          DASH_USERNAME: ${{ secrets.DASH_USERNAME }}
          DASH_PASSWORD: ${{ secrets.DASH_PASSWORD }}
          REDIS_URL: redis://localhost:6379
        run: |
          . .venv/bin/activate
          python -u src/main.py --driver-path ./geckodriver --browser firefox --workers 3

      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-traces
          path: |
            .screenshots/
            .ocarina_logs/
            .reports/

Flow#

1. services.redis#

services:
  redis:
    image: redis:8.8.0
    ports: [6379:6379]
    options: >-
      --health-cmd "redis-cli ping"
      --health-interval 10s
      ...

GitHub Actions spins up a Redis container as a sidecar, reachable at localhost:6379. Health-checked via redis-cli ping.

2. environment: OC#

environment: OC

Binds the workflow to a GitHub Environment named OC.

  1. Holds the secrets (IGOR_API_KEY, DASH_USERNAME, DASH_PASSWORD).
  2. Can require manual approval before running — useful for quota-consuming workflows.

3. Install geckodriver#

- name: Install geckodriver
  run: |
    GECKO_VERSION=0.35.0
    wget https://github.com/mozilla/geckodriver/releases/download/v$GECKO_VERSION/geckodriver-v$GECKO_VERSION-linux64.tar.gz
    tar -xvzf geckodriver-v$GECKO_VERSION-linux64.tar.gz
    chmod +x geckodriver

4. Setup Firefox#

- uses: browser-actions/setup-firefox@<sha>

Installs a Firefox version compatible with geckodriver.

5. Run e2e cycle#

python -u src/main.py --driver-path ./geckodriver --browser firefox --workers 3
  • -u: unbuffered stdout (real-time logs).
  • --workers 3: 3 browsers in parallel.
  • --driver-path ./geckodriver: relative path from the root.
  • --browser firefox.

--logger not specified; defaults to terminal+file.

6. Env vars#

env:
  IGOR_API_KEY: ${{ secrets.IGOR_API_KEY }}
  DASH_USERNAME: ${{ secrets.DASH_USERNAME }}
  DASH_PASSWORD: ${{ secrets.DASH_PASSWORD }}
  REDIS_URL: redis://localhost:6379

IGOR_API_KEYAPI_SECRET (see ../06-tests-workers/). REDIS_URL points at the sidecar.

7. Upload artifacts#

- name: Upload artifacts
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: e2e-traces
    path: |
      .screenshots/
      .ocarina_logs/
      .reports/

if: always(): uploads even on failure — so you can analyze why it failed.

  1. .screenshots/: all screenshots.
  2. .ocarina_logs/: full log tree (campaigns/suites/tests).
  3. .reports/: DOCX + JSON plugin output.

workflow_dispatch#

Why only workflow_dispatch?

  1. Cost: an e2e run takes several minutes, spins up 3 browsers, hundreds of requests to Igoristan and tests-workers.
  2. Vercel quotas: tests-workers calls consume quota.
  3. Stability: e2e runs can fail for reasons outside the project (Vercel down, GitHub Pages down). No appetite for polluting CI on every PR.

Sample timeline#

0:00     Trigger workflow_dispatch
0:00     Lock environment "OC" (1 run at a time)
0:00     Spawn Redis sidecar container
0:00     Checkout
0:30     Setup Python 3.14
0:45     Cache restore .venv (hit or miss)
1:30     pip install requirements + ocarina (if miss)
2:00     Download + install geckodriver 0.35.0
2:30     Setup Firefox
3:00     Warmup Redis client
3:00     ──── E2E cycle starts ────
3:00     Smoke campaigns (global + corsicamon)
4:00     Main campaigns:
           - Dashboard login (3 suites, ~10 tests)
           - Randomness (1 suite, 6 tests)
           - Sacred upload (2 suites, ~4 tests)
           - Corsicamon (2 suites, ~6 tests)
8:00     ──── E2E cycle ends ────
8:00     Post-exec plugins (DOCX + JSON in parallel)
8:30     pretty_print_results + sys.exit
8:30     Upload artifacts (screenshots, logs, reports)
9:00     Done.