fix(ci): install from uv.lock so CI stops resolving dependencies itself
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Failing after 10s
CI & Build / Python tests (push) Failing after 16s
CI & Build / TypeScript typecheck (push) Successful in 32s
CI & Build / Build & push image (push) Has been skipped

Closes the reproducibility hole that turned `main` red an hour ago. CI ran
`uv pip install -e ".[dev]"`, which resolves from the pyproject constraints
and ignores uv.lock completely — so every dependency floated. uv.lock pinned
mcp 1.27.2; CI installed the 2.0.0 published mid-session and the identical
tree that passed on `dev` failed on `main`.

Both Python lanes now run `uv sync --locked --extra dev`. `--locked` also
fails when the lock is stale against pyproject, so a dependency change has to
go through a deliberate `uv lock` instead of arriving on its own — which also
restores the point of the Renovate dashboard-approval flow.

Dropped the http-ece install and the setuptools/wheel step that existed only
to support it: nothing in src/ or tests/ imports http_ece. It is a leftover
from the web-push subsystem removed in the MCP-First pivot, and it was never
in pyproject or uv.lock — CI was installing an unused package and carrying a
--no-build-isolation workaround for it.

Cache key moves from pyproject.toml to uv.lock, since the lock is now what
determines the installed set.

uv.lock's recorded root requirement updated to match the mcp cap. Edited by
hand rather than regenerated: uv isn't installed on this workstation, the
resolved mcp 1.27.2 already satisfies `<2`, so no re-resolution is needed —
only the staleness check needed satisfying.

The Dockerfile still resolves independently (`pip install .`, and it doesn't
even copy uv.lock), so the shipped image is not yet covered. Following
separately so a build break can't strand `main`. Refs #2194.
This commit is contained in:
2026-07-28 12:50:52 -04:00
parent aa850ac1e1
commit ef7ebddadf
2 changed files with 24 additions and 20 deletions
+23 -19
View File
@@ -126,20 +126,25 @@ jobs:
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: ~/.cache/uv path: ~/.cache/uv
key: uv-${{ hashFiles('pyproject.toml') }} # Keyed on the LOCK, not pyproject: the lock is what determines the
# installed set now, and a pyproject edit that doesn't change
# resolution shouldn't throw the cache away.
key: uv-${{ hashFiles('uv.lock') }}
restore-keys: uv- restore-keys: uv-
- name: Create virtual environment # Installs exactly what uv.lock pins. `--locked` additionally FAILS if the
run: uv venv /opt/venv # lock is stale against pyproject, so a dependency change has to go through
# a deliberate `uv lock` — it can't drift in silently.
- name: Install package with dev deps #
run: | # This replaced `uv pip install -e ".[dev]"`, which resolved from the
# http-ece doesn't declare setuptools as a build dep, and uv # pyproject constraints and ignored the lock entirely. Every dependency
# creates bare venvs without it. Install setuptools first so # floated: on 2026-07-28 mcp 2.0.0 shipped mid-session and turned `main`
# --no-build-isolation can find it. # red with no repo change (issue #2194). Green CI has to mean "these exact
uv pip install --python /opt/venv/bin/python setuptools wheel # versions passed", or it isn't evidence of anything.
uv pip install --python /opt/venv/bin/python --no-build-isolation http-ece - name: Install locked dependencies
uv pip install --python /opt/venv/bin/python -e ".[dev]" env:
UV_PROJECT_ENVIRONMENT: /opt/venv
run: uv sync --locked --extra dev
- name: Run tests - name: Run tests
# Integration tests (real Postgres) run in the `integration` job below. # Integration tests (real Postgres) run in the `integration` job below.
@@ -179,13 +184,12 @@ jobs:
--health-retries 10 --health-retries 10
steps: steps:
- uses: actions/checkout@v6 - uses: actions/checkout@v6
- name: Create virtual environment # Same locked install as the unit lane — the two must agree on versions,
run: uv venv /opt/venv # or "unit green, integration red" stops being a signal about the code.
- name: Install package with dev deps - name: Install locked dependencies
run: | env:
uv pip install --python /opt/venv/bin/python setuptools wheel UV_PROJECT_ENVIRONMENT: /opt/venv
uv pip install --python /opt/venv/bin/python --no-build-isolation http-ece run: uv sync --locked --extra dev
uv pip install --python /opt/venv/bin/python -e ".[dev]"
- name: Integration suite (resolve service IP, migrate, test) - name: Integration suite (resolve service IP, migrate, test)
run: | run: |
set -eux set -eux
Generated
+1 -1
View File
@@ -393,7 +393,7 @@ requires-dist = [
{ name = "httpx", specifier = ">=0.27" }, { name = "httpx", specifier = ">=0.27" },
{ name = "hypercorn", specifier = ">=0.17" }, { name = "hypercorn", specifier = ">=0.17" },
{ name = "icalendar", specifier = ">=5.0" }, { name = "icalendar", specifier = ">=5.0" },
{ name = "mcp", extras = ["cli"], specifier = ">=1.0" }, { name = "mcp", extras = ["cli"], specifier = ">=1.0,<2" },
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0" },
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
{ name = "quart", specifier = ">=0.19" }, { name = "quart", specifier = ">=0.19" },