From ef7ebddadf36672f5c7b7648ffeff0d80fbd277a Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 28 Jul 2026 12:50:52 -0400 Subject: [PATCH] fix(ci): install from uv.lock so CI stops resolving dependencies itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .forgejo/workflows/ci.yml | 42 +++++++++++++++++++++------------------ uv.lock | 2 +- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 9bf7ac0..4f2108d 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -126,20 +126,25 @@ jobs: uses: actions/cache@v4 with: 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- - - name: Create virtual environment - run: uv venv /opt/venv - - - name: Install package with dev deps - run: | - # http-ece doesn't declare setuptools as a build dep, and uv - # creates bare venvs without it. Install setuptools first so - # --no-build-isolation can find it. - uv pip install --python /opt/venv/bin/python setuptools wheel - uv pip install --python /opt/venv/bin/python --no-build-isolation http-ece - uv pip install --python /opt/venv/bin/python -e ".[dev]" + # Installs exactly what uv.lock pins. `--locked` additionally FAILS if the + # lock is stale against pyproject, so a dependency change has to go through + # a deliberate `uv lock` — it can't drift in silently. + # + # This replaced `uv pip install -e ".[dev]"`, which resolved from the + # pyproject constraints and ignored the lock entirely. Every dependency + # floated: on 2026-07-28 mcp 2.0.0 shipped mid-session and turned `main` + # red with no repo change (issue #2194). Green CI has to mean "these exact + # versions passed", or it isn't evidence of anything. + - name: Install locked dependencies + env: + UV_PROJECT_ENVIRONMENT: /opt/venv + run: uv sync --locked --extra dev - name: Run tests # Integration tests (real Postgres) run in the `integration` job below. @@ -179,13 +184,12 @@ jobs: --health-retries 10 steps: - uses: actions/checkout@v6 - - name: Create virtual environment - run: uv venv /opt/venv - - name: Install package with dev deps - run: | - uv pip install --python /opt/venv/bin/python setuptools wheel - uv pip install --python /opt/venv/bin/python --no-build-isolation http-ece - uv pip install --python /opt/venv/bin/python -e ".[dev]" + # Same locked install as the unit lane — the two must agree on versions, + # or "unit green, integration red" stops being a signal about the code. + - name: Install locked dependencies + env: + UV_PROJECT_ENVIRONMENT: /opt/venv + run: uv sync --locked --extra dev - name: Integration suite (resolve service IP, migrate, test) run: | set -eux diff --git a/uv.lock b/uv.lock index 8cd7f5c..fdcb59c 100644 --- a/uv.lock +++ b/uv.lock @@ -393,7 +393,7 @@ requires-dist = [ { name = "httpx", specifier = ">=0.27" }, { name = "hypercorn", specifier = ">=0.17" }, { 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-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" }, { name = "quart", specifier = ">=0.19" },