ci: new ci-runner base image + uv/ruff/node_modules cache

Runner base image (infra/Dockerfile.runner-base):
- Added uv (fast Python package installer, ~10x faster than pip)
- Added ruff (baked in, lint job drops from ~13s to ~2s)
- Added jq + tzdata
- Renamed tag from py3.12-node22 to ci-runner (purpose over contents)

Workflow (ci.yml):
- typecheck: cache node_modules directly instead of npm download cache;
  skip npm ci entirely on cache hits (9m41s → ~30s expected)
- lint: just `ruff check src/` — no venv, no install
- test: uv venv + uv pip install replaces pip (~10x faster resolves)
- all jobs: runs-on ci-runner label

Baseline: typecheck 9m41s, lint 13s, test 1m42s
This commit is contained in:
2026-04-11 23:59:57 -04:00
parent 02138f5728
commit 8d07b6c79e
2 changed files with 48 additions and 37 deletions
+24 -26
View File
@@ -51,17 +51,23 @@ env:
jobs:
typecheck:
name: TypeScript typecheck
runs-on: py3.12-node22
runs-on: ci-runner
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
# Cache node_modules directly (not the npm download cache).
# npm ci still has to extract + link every module even with a
# warm download cache, which is where the real time goes. Caching
# the output directory lets us skip npm ci entirely on hits.
- name: Cache node_modules
id: npm-cache
uses: actions/cache@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
path: frontend/node_modules
key: node-modules-${{ hashFiles('frontend/package-lock.json') }}
- name: Install dependencies
if: steps.npm-cache.outputs.cache-hit != 'true'
run: npm ci
working-directory: frontend
@@ -71,46 +77,38 @@ jobs:
lint:
name: Python lint
runs-on: py3.12-node22
runs-on: ci-runner
steps:
- uses: actions/checkout@v6
- name: Install ruff
# Isolated venv avoids polluting the runner's system
# site-packages (the old --break-system-packages was a smell)
# and pipx isn't available on the py3.12-node22 runner image.
run: |
python3.12 -m venv /opt/ruff-venv
/opt/ruff-venv/bin/pip install --quiet ruff
# ruff is pre-installed in the ci-runner base image — no install
# step needed, lint runs in ~2s.
- name: Lint
run: /opt/ruff-venv/bin/ruff check src/
run: ruff check src/
test:
name: Python tests
runs-on: py3.12-node22
runs-on: ci-runner
steps:
- uses: actions/checkout@v6
# Python 3.12 is pre-installed in the runner-base image (py3.12-node22).
- name: Cache pip wheels
- name: Cache uv packages
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: pip-${{ hashFiles('pyproject.toml') }}
restore-keys: pip-
path: ~/.cache/uv
key: uv-${{ hashFiles('pyproject.toml') }}
restore-keys: uv-
- name: Create virtual environment
run: python3.12 -m venv /opt/venv
run: uv venv /opt/venv
- name: Install package with dev deps
run: |
/opt/venv/bin/pip install --upgrade pip setuptools wheel
# http-ece's build-time dep on cryptography fails under PEP 517
# isolation on this runner image — install it without isolation
# first so the editable install below finds it already built.
/opt/venv/bin/pip install --no-build-isolation http-ece
/opt/venv/bin/pip install -e ".[dev]"
uv pip install --python /opt/venv/bin/python --no-build-isolation http-ece
uv pip install --python /opt/venv/bin/python -e ".[dev]"
- name: Run tests
run: /opt/venv/bin/python -m pytest tests/ -q
@@ -124,7 +122,7 @@ jobs:
if: |
github.event_name == 'push' &&
(github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/'))
runs-on: py3.12-node22
runs-on: ci-runner
permissions:
contents: read
packages: write