You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Production image installs from pyproject.toml, not uv.lock #57
Context: surfaced by Copilot review on #56 (IDC v25), where it caused a concrete, user-visible drift risk. That PR fixed the symptom for one dependency; this issue is the root cause.
Summary
uv.lock constrains CI but not the shipped image. The Docker build resolves dependencies fresh from the lower bounds in pyproject.toml, so the same git commit can produce different images at different times, and the versions CI tested and audited are not necessarily the versions that ship.
Mechanism
The two install paths diverge:
Command
Resolves from
CI
uv sync --extra dev (.github/workflows/ci.yml:49)
uv.lock
Image
uv pip install --system . (Dockerfile:14-16)
pyproject.toml bounds
The Dockerfile copies pyproject.toml, README.md and src — never uv.lock. Every runtime dependency except idc-index is a lower bound (duckdb>=1.5.5, pyarrow>=25.0.0, pydantic>=2.7, fastapi>=0.141.1, uvicorn[standard]>=0.52.1, mcp>=1.29.0,<2), so each rebuild re-resolves to whatever is newest at that moment.
Why it matters
CI's guarantees do not transfer to the artifact.pytest, bandit and — most importantly — pip-audit all run against the locked set. A clean vulnerability scan says nothing about the image, which may contain a different, never-audited version of any dependency.
Rebuilds are not reproducible. Per dev/deployment.md, prod runs test's exact bytes and never rebuilds, so the exposure window is the image build in test. But "build the canonical image from this commit" is not currently a deterministic operation, which is the property the promotion model assumes.
Data-version drift was the visible case.idc-index transitively pins idc-index-data, which is the IDC release advertised at /v3/version. Under idc-index>=0.13.0, rebuilding the v25 commit after a later release shipped would have served a different dataset than the changelog claimed. feat: serve IDC v25 (idc-index 0.13.0) #56 pins idc-index==0.13.0 to stop that — a point fix for the one dependency where drift is user-visible, not a general solution.
Options
A. uv sync --frozen in the image. Most direct, but uv sync builds a project .venv rather than installing system-wide, so the DuckDB bake step (RUN python -c ...) and the CMD both need repointing at /app/.venv/bin/. Larger diff.
B. Export the lock to a requirements file, then install as today.
Keeps --system and leaves the rest of the Dockerfile untouched. Probably the smaller change.
C. Status quo — pin each dependency exactly in pyproject.toml. Works, but duplicates the lockfile by hand and drifts from it silently.
Preference is B unless the .venv layout is wanted for other reasons.
Notes
Whichever option lands, Dockerfile needs uv.lock in its build context, and the uv.lock path filters already present in ci.yml and build-and-deploy-dev.yml mean lock changes will correctly trigger a rebuild.
Worth deciding at the same time whether the idc-index==0.13.0 pin from feat: serve IDC v25 (idc-index 0.13.0) #56 stays. It remains useful as an explicit statement of the advertised data release even once the lock is authoritative, and dev/deployment.md § Updating for a new IDC release already describes bumping it as the way to move IDC versions.
Verify after the change that the image still reports the expected /v3/version and that the baked DuckDB build step runs in the same environment the deps landed in.
Context: surfaced by Copilot review on #56 (IDC v25), where it caused a concrete, user-visible drift risk. That PR fixed the symptom for one dependency; this issue is the root cause.
Summary
uv.lockconstrains CI but not the shipped image. The Docker build resolves dependencies fresh from the lower bounds inpyproject.toml, so the same git commit can produce different images at different times, and the versions CI tested and audited are not necessarily the versions that ship.Mechanism
The two install paths diverge:
uv sync --extra dev(.github/workflows/ci.yml:49)uv.lockuv pip install --system .(Dockerfile:14-16)pyproject.tomlboundsThe Dockerfile copies
pyproject.toml,README.mdandsrc— neveruv.lock. Every runtime dependency exceptidc-indexis a lower bound (duckdb>=1.5.5,pyarrow>=25.0.0,pydantic>=2.7,fastapi>=0.141.1,uvicorn[standard]>=0.52.1,mcp>=1.29.0,<2), so each rebuild re-resolves to whatever is newest at that moment.Why it matters
pytest,banditand — most importantly —pip-auditall run against the locked set. A clean vulnerability scan says nothing about the image, which may contain a different, never-audited version of any dependency.dev/deployment.md, prod runs test's exact bytes and never rebuilds, so the exposure window is the image build in test. But "build the canonical image from this commit" is not currently a deterministic operation, which is the property the promotion model assumes.idc-indextransitively pinsidc-index-data, which is the IDC release advertised at/v3/version. Underidc-index>=0.13.0, rebuilding the v25 commit after a later release shipped would have served a different dataset than the changelog claimed. feat: serve IDC v25 (idc-index 0.13.0) #56 pinsidc-index==0.13.0to stop that — a point fix for the one dependency where drift is user-visible, not a general solution.Options
A.
uv sync --frozenin the image. Most direct, butuv syncbuilds a project.venvrather than installing system-wide, so the DuckDB bake step (RUN python -c ...) and theCMDboth need repointing at/app/.venv/bin/. Larger diff.B. Export the lock to a requirements file, then install as today.
Keeps
--systemand leaves the rest of the Dockerfile untouched. Probably the smaller change.C. Status quo — pin each dependency exactly in
pyproject.toml. Works, but duplicates the lockfile by hand and drifts from it silently.Preference is B unless the
.venvlayout is wanted for other reasons.Notes
Dockerfileneedsuv.lockin its build context, and theuv.lockpath filters already present inci.ymlandbuild-and-deploy-dev.ymlmean lock changes will correctly trigger a rebuild.idc-index==0.13.0pin from feat: serve IDC v25 (idc-index 0.13.0) #56 stays. It remains useful as an explicit statement of the advertised data release even once the lock is authoritative, anddev/deployment.md § Updating for a new IDC releasealready describes bumping it as the way to move IDC versions./v3/versionand that the baked DuckDB build step runs in the same environment the deps landed in.Drafted with Claude Code.