fix(dev-env): give the container its own Python venv - #1340
Merged
Conversation
Host and container bind-mount one checkout, so they contended for a
single `big-code-analysis-py/.venv`. A venv records absolute
interpreter paths in `pyvenv.cfg` and in every console-script
shebang, so the directory cannot serve both: whichever side ran
`uv sync` last owned it and the other got
bash: .venv/bin/mypy: /…/.venv/bin/python: bad interpreter
from py-typecheck, py-test and py-stubtest. `make worktree-setup` on
the host is enough to take ownership, so the two environments
silently broke each other.
`dev-env-run` now mounts a host cache directory over that path. The
in-container path is still spelled `.venv`, so none of the fourteen
recipe lines resolving `$(BCA_PY_DIR)/.venv/bin/*` need to change,
and no gate mechanism is refactored. Ownership lines up through the
UID/GID args `dev-env-build` already passes.
Two consequences of the nested mount, both handled:
Docker mounts the repo first and then creates a missing deeper
target itself — as root, and on the host, since the path sits inside
the bind mount. Verified in an isolated scratch mount: without a
pre-created directory the host gets a root-owned `.venv` and
host-side `uv sync` fails with permission denied. `dev-env-run`
therefore pre-creates it.
A mount point cannot be unlinked, so `py-clean`'s `rm -rf` aborted
the recipe with EBUSY inside the container, taking `distclean` with
it. It now empties the directory in place when removal fails.
Unchanged on the host, where `.venv` is an ordinary directory:
removed as before, exit 0, and no mount-point message.
Verified with `make pre-commit` (BCA_GATE: pass) plus `py-typecheck`
passing in the container and on the host simultaneously — the state
that was previously impossible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The host and the dev container bind-mount the same checkout, so they
contended for a single
big-code-analysis-py/.venv. A venv recordsabsolute interpreter paths in
pyvenv.cfgand in every console-scriptshebang, so one directory cannot serve both sides:
Whichever environment ran
uv synclast owned it and the other got thatfrom
py-typecheck,py-testandpy-stubtest.make worktree-setuponthe host is enough to take ownership, so the two silently broke each
other — which is how this surfaced.
Fix
dev-env-runmounts a host cache directory over that path, so each sidegets its own virtualenv. The in-container path is still spelled
.venv,so none of the fourteen recipe lines resolving
$(BCA_PY_DIR)/.venv/bin/*change and no gate mechanism isrefactored. Ownership lines up through the UID/GID build args
dev-env-buildalready passes.Two consequences of the nested mount, both handled:
on the host, since the path sits inside the bind mount. Verified in
an isolated scratch mount: without a pre-created directory the host
gets a root-owned
.venvand host-sideuv syncfails withpermission denied.
dev-env-runpre-creates it.py-clean'srm -rfabortedthe recipe with
EBUSYinside the container, takingdistcleanwithit. It now empties the directory in place when removal fails.
Unchanged on the host, where
.venvis an ordinary directory: removedas before, exit 0, and no mount-point message.
docker/README.mddocuments the split, the symptom it prevents, and thehost-side reset for a stale cached venv after a
dev-env-rm/dev-env-runcycle (the cache outlives the container, but theuv-managed interpreter it names does not).
Verification
make pre-commit→BCA_GATE: passmake py-typecheckpassing in the container and on the hostsimultaneously — mypy 32 files, pyright 0 errors on both sides. That
state was previously impossible.
make py-cleanexits 0; host.venvintact and user-owned.py-cleanexercised against a populated directory, an absent one, anda real mount point — exit 0 in all three.
Scope: dev tooling only. No library, CLI, or web code is touched.