10.04 — ocarina-with-ai-example workflows#

See also ../08-ai-example/09-ci-matrix.md

Overview#

WorkflowTriggerOSEffectNote
ai_proof_ci.ymlpush main, PR, dispatchubuntu × py 3.14ruff format src/ --check + ruff check src/ + mypy src/PR gate, fast
ai_proof_e2e.ymldispatchubuntu × firefox+chrome matrixHeroku warm-up + run + sed filter ChromeDriver C++ stacks + uploadManual only

Differences vs ocarina-example#

Aspectocarina-exampleocarina-with-ai-example
Browser matrixFirefox only (e2e.yml)Firefox + Chrome (parallelized)
SUTIgoristan (GitHub Pages)CURA (Heroku eco-dyno)
Pre-runwarm-up Redis clientwarm-up Heroku dyno
Output filternonesed filters ChromeDriver C++ stack frames
WAIT_TIMEOUTdefault 1015 (compensates for Heroku latency)

Heroku warm-up#

curl -sf --retry 6 --retry-delay 5 --retry-all-errors \
  --max-time 30 https://katalon-demo-cura.herokuapp.com/ > /dev/null
echo "Dyno is warm"

CURA runs on a Heroku dyno that sleeps after inactivity. Tests hitting a cold dyno produce timeouts (false fails) and slow confirmations (false passes on gap tests). Wake it explicitly before any browser opens.

The sed filter for ChromeDriver C++ stacktraces#

.venv/bin/python -u src/main.py ... 2>&1 | sed -u \
  -e '/Stacktrace:/{N;/\n#[0-9][0-9]* 0x[0-9a-f][0-9a-f]* <unknown>/{s/Stacktrace:\n[^\n]*/HIDDEN/;b};P;D}' \
  -e '/^#[0-9][0-9]* 0x[0-9a-f][0-9a-f]* <unknown>/d'

ChromeDriver release builds ship without debug symbols. On errors it dumps raw C++ stack frames ("#N 0x") into the WebDriver response, which Selenium surfaces as the exception message — polluting both the live log and the pretty-print summary with unreadable hex noise. No Python-side fix exists. Strip the frames with sed before they land. pipefail preserves Python’s exit code through the pipe.

  • ChromeDriver, on error, dumps unreadable C++ stack frames (#0 0x7f3b... <unknown>).
  • Selenium surfaces them in the exception message.
  • Pollutes logs and the pretty_print_results output.
  • No relevant fix directly in Python code.
  • sed strips them before they reach the user, straight from the shell.

set -o pipefail preserves Python’s exit code.

fail-fast: false#

strategy:
  fail-fast: false
  matrix:
    include:
      - browser: firefox
        driver: geckodriver
      - browser: chrome
        driver: chromedriver

If Firefox fails, Chrome continues. The value is in comparing both runs.

WAIT_TIMEOUT: 15#

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
  WAIT_TIMEOUT: 15

15 seconds instead of default 10. Compensates for Heroku dyno latency that can exceed the average even after cold-start (even with curl warm-up, subsequent requests may be slow).

Reminder: _NOT_CONFIRMED_TIMEOUT = 10 (in confirmation.py) is not affected (see ../08-ai-example/06-data-gaps.md).

Browsers in CI#

Firefox#

- name: Install geckodriver
  if: matrix.browser == 'firefox'
  run: |
    GECKO_VERSION=0.36.0
    wget -q https://github.com/mozilla/geckodriver/releases/download/v$GECKO_VERSION/geckodriver-v$GECKO_VERSION-linux64.tar.gz
    tar -xzf geckodriver-v$GECKO_VERSION-linux64.tar.gz
    chmod +x geckodriver

- name: Setup Firefox
  if: matrix.browser == 'firefox'
  uses: browser-actions/setup-firefox@fcf821c621167805dd63a29662bd7cb5676c81a8

Chrome#

- name: Install chromedriver
  if: matrix.browser == 'chrome'
  run: |
    CHROME_VER=$(google-chrome-stable --version | grep -oP '\d+\.\d+\.\d+\.\d+')
    wget -q -O chromedriver.zip \
      "https://storage.googleapis.com/chrome-for-testing-public/${CHROME_VER}/linux64/chromedriver-linux64.zip"
    unzip -q chromedriver.zip
    mv chromedriver-linux64/chromedriver ./chromedriver
    chmod +x ./chromedriver

ubuntu-latest ships google-chrome-stable. We extract its exact version, download the matching chromedriver from Google’s official chrome-for-testing-public CDN.

This guarantees Chrome ↔ ChromeDriver version compatibility.

Enumerate traces#

- name: Enumerate traces
  if: always()
  run: |
    sync
    find $GITHUB_WORKSPACE/.screenshots -type f 2>/dev/null || true
    find $GITHUB_WORKSPACE/.ocarina_logs -type f 2>/dev/null || true
    find $GITHUB_WORKSPACE/.reports -type f 2>/dev/null || true

Step that forces a filesystem sync and lists produced traces before upload. Lets you see directly in GitHub logs which files were generated.

Per-browser artifact upload#

- name: Upload screenshots
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: screenshots-${{ matrix.browser }}
    path: .screenshots/
    retention-days: 7

Names including the browser (screenshots-firefox, screenshots-chrome). No collision between the two parallel runs.

Retained 7 days.