name: CI on: pull_request: push: branches: [dev, main] # Cancel superseded runs only on PR updates (rapid pushes to a PR branch). NEVER cancel a # push to dev/main: deploy.yml gates on THIS commit's CI, so a cancelled run makes the deploy # fail-closed. Back-to-back merges used to cancel each other's CI → spurious deploy failures. concurrency: group: ci-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: lint-and-test: runs-on: ubuntu-latest permissions: contents: read env: MPLBACKEND: Agg steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 with: python-version: "3.11" cache: pip cache-dependency-path: requirements-api.lock # CPU-only torch, pinned to the Dockerfile versions, so the editable install # below finds torch already satisfied and never pulls the ~2 GB CUDA build. - name: Install CPU torch (pinned to match the image) run: pip install --index-url https://download.pytorch.org/whl/cpu torch==2.6.0 torchvision==0.21.0 # Runtime deps from the same hash-pinned lock the image uses — CI now tests # against the exact versions that deploy, not a fresh re-resolve. - name: Install pinned runtime deps run: pip install --require-hashes -r requirements-api.lock - name: Install package + dev tools run: | pip install --no-deps -e . pip install pytest ruff httpx "pyjwt[crypto]>=2.8,<3" "boto3>=1.34,<2" - name: Lint (ruff) run: ruff check src tests - name: Test (pytest, zero-network, <30s) run: pytest