build(deps): bump python from 2c941e8 to e5c9fa2
#196
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: UI tests | |
| # Runs the Playwright UI suite (-m ui) so it cannot rot silently the way two | |
| # settings tests did. The main CI job runs `-m "not ui"`, so without this the | |
| # UI tests would never execute. Path-filtered to frontend changes to spare the | |
| # single self-hosted host, plus a nightly run to catch drift, plus manual. | |
| # | |
| # Shares the host-global container lock with ci.yml's `test` job and | |
| # e2e-staging: all three bind the fixed-name test container on port 18888 and | |
| # must never run at once on the self-hosted host (#295). | |
| on: | |
| pull_request: | |
| branches: [ main, develop ] | |
| paths: | |
| - 'templates/**' | |
| - 'static/**' | |
| - 'tests/test_ui.py' | |
| - 'tests/conftest.py' | |
| - 'Dockerfile' | |
| - '.github/workflows/ui-tests.yml' | |
| schedule: | |
| - cron: '41 4 * * *' # nightly drift catch | |
| workflow_dispatch: | |
| permissions: read-all | |
| concurrency: | |
| group: ui-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| ui: | |
| name: ui | |
| runs-on: [self-hosted, Linux, X64] | |
| # Same host-global lock ci.yml's `test` job uses: the UI suite binds the | |
| # fixed-name test container on port 18888, so it must queue behind (never | |
| # run beside) any other test job on the self-hosted host. Putting this | |
| # shared group at workflow level instead made a co-triggered workflow cancel | |
| # this run at 1s; keep it job-level, cancel-in-progress false so it queues. | |
| concurrency: | |
| group: certmate-selfhosted-test-container | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - name: Cache pip dependencies | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install -r requirements-test.txt | |
| - name: Install Playwright Chromium | |
| # --with-deps installs the system libraries (libnss3, libasound2, ...) | |
| # Chromium needs, but it shells out to sudo — which this self-hosted | |
| # host does not grant passwordlessly. Use it when it is available and | |
| # fall back to the plain install otherwise; either way the launch check | |
| # below is what decides, so a host missing those libraries fails the | |
| # job instead of skipping every test and reporting green (#414). | |
| run: | | |
| if sudo -n true 2>/dev/null; then | |
| python -m playwright install --with-deps chromium | |
| else | |
| echo "passwordless sudo unavailable; installing browser only" | |
| python -m playwright install chromium | |
| fi | |
| - name: Verify Chromium actually launches | |
| # Fail here, loudly and in one line, rather than inside 17 fixtures. | |
| run: | | |
| python - <<'PY' | |
| from playwright.sync_api import sync_playwright | |
| with sync_playwright() as p: | |
| b = p.chromium.launch(headless=True) | |
| print("chromium", b.version) | |
| b.close() | |
| PY | |
| - name: UI tests | |
| # CERTMATE_UI_REQUIRE_BROWSER=1 turns "browser unavailable" from a skip | |
| # into a failure, so this job can no longer report success without | |
| # having driven a browser. | |
| env: | |
| CERTMATE_UI_REQUIRE_BROWSER: '1' | |
| run: python -m pytest -v -m ui --junitxml=ui-results.xml | |
| - name: Refuse a green run that asserted nothing | |
| if: always() | |
| # The last line of defence: if every collected UI test skipped (or none | |
| # were collected at all), the job fails. A suite that runs zero tests is | |
| # not a passing suite. | |
| run: | | |
| python - <<'PY' | |
| import sys, xml.etree.ElementTree as ET | |
| try: | |
| root = ET.parse("ui-results.xml").getroot() | |
| except Exception as e: | |
| sys.exit(f"no UI test results to check: {e}") | |
| suites = root.iter("testsuite") | |
| total = skipped = 0 | |
| for s in suites: | |
| total += int(s.get("tests", 0)) | |
| skipped += int(s.get("skipped", 0)) | |
| print(f"collected={total} skipped={skipped}") | |
| if total == 0: | |
| sys.exit("UI suite collected 0 tests") | |
| if skipped == total: | |
| sys.exit("every UI test skipped - the suite proved nothing") | |
| PY |