Repository navigation
fix(update): detect a half-replaced install instead of reporting its version - #850
Conversation
…version A package manager replaces files in place, so an interrupted upgrade leaves a venv whose metadata reads perfectly and whose imports are dead. `gflow update` only ever re-read the version, so it called that state "still 0.69.0" and sent the user to a plain `uv tool upgrade` — which cannot repair it: it reads the same intact metadata, finds it current and changes nothing. Measured on Windows (#848): an aborted native-dependency swap left every command dying on `AttributeError: module 'greenlet' has no attribute 'greenlet'`, and only `uv tool install "gflow-cli==<version>" --force --reinstall` completed. After the manager runs, import gflow-cli in a fresh isolated interpreter before looking at any version. `-I` so a `gflow_cli` directory in the caller's CWD cannot answer for the venv; a probe that could not run at all is never reported as breakage. Refs #848
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. 📝 WalkthroughWalkthrough
ChangesUpdate validation
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant run_update
participant package_manager
participant isolated_interpreter
participant version_validation
run_update->>package_manager: install update
package_manager-->>run_update: installation result
run_update->>isolated_interpreter: import installed package
isolated_interpreter-->>run_update: import status and diagnostic
run_update->>version_validation: validate version when import succeeds
Merge Risk: ⚪ Minimal · up to The update command now detects unusable installations and provides repair guidance without introducing a supported current-head risk. It is ready to merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 2 files. (3 skipped: 3 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
Summary
gflow updatecould report a version for an install that no longer starts.A package manager replaces files in place, so an interrupted upgrade leaves a venv whose metadata reads perfectly and whose imports are dead.
gflow updateonly ever re-read the version (importlib.metadata, which reads*.dist-infoand never touches a module), so it called that state "exited 1 and gflow-cli is still 0.69.0" and sent the user to a plainuv tool upgrade— which cannot repair it: it reads the same intact metadata, finds it current and changes nothing.After the manager runs,
gflow updatenow imports gflow-cli in a fresh isolated interpreter before it looks at any version. A failed import is reported as an unusable install (exit 11,ConfigurationError) quoting that interpreter's own last line, and the remediation is a forced reinstall — the command that actually completed in the reported incident.Verification
Run against a really half-replaced
greenlet, not a stand-in — the probe reproduces the issue's own error string verbatim:Offline gates:
283 passed, 1 skippedon the update-adjacent slice; hygiene, doc-links, website-docs mirror, council-memory,ruff check,ruff format --check,pyright src(0 errors) all green.No Flow surface is touched, so no e2e test applies and none was written — this is local package management, not a generation path. The load-bearing new logic (the probe) is verified by a real subprocess against a real broken venv, plus 9 unit tests.
Findings that change two of the three suggestions in #848
Suggestion 3 — "detect the no-op" — already exists.
update_check.pyraisesConfigurationErrorwhen the manager exits 0 and the version did not move, andtests/test_update_check.py::test_manager_exit_0_but_version_unchanged_is_configuration_errorpins it. The no-op described in the issue ("Modified gflow-cli environment", exit 0, version unchanged) was a rawuv tool upgrade, notgflow update.Suggestion 1 — "pre-flight the lock" — measured, and there is no signal to act on.
chrome.execannot hold a Python.pyd; only a Python process can. The issue attributes the lock to "a Playwright-driven Chrome", which is not a mechanism that can produce it.gflowprocess maps 12 of its venv's.pydfiles,_greenlet.cp313-win_amd64.pydamong them —structlogimportsgreenletunconditionally (python -c "import structlog, sys; print('greenlet' in sys.modules)"printsTrue). Sogflow updateis itself always a holder; a pre-flight that refuses while a native module is held would refuse every single time.uv pip install --reinstall-package greenletreplaced 3.5.4 with 3.5.6 while another process had_greenlet.pydloaded, exit 0. A mapped.pydcannot be opened for write cross-process (PermissionError13), but it can be unlinked and replaced.Why uv failed with
os error 5in the reported run is therefore still unexplained — not our process, not Chrome, and not reproducible here. That is stated as unresolved rather than guessed at. This PR implements suggestion 2 instead, in its strongest form: the state the install is actually in, detected rather than inferred.Refs #848
Summary by CodeRabbit
Bug Fixes
gflow updatenow verifies that the upgraded package can be imported in a fresh interpreter.Documentation