fix(providers/uv): treat uv.lock as optional#1979
Open
bearomorphism wants to merge 2 commits intocommitizen-tools:masterfrom
Open
fix(providers/uv): treat uv.lock as optional#1979bearomorphism wants to merge 2 commits intocommitizen-tools:masterfrom
bearomorphism wants to merge 2 commits intocommitizen-tools:masterfrom
Conversation
Closes commitizen-tools#1383. `UvProvider.set_lock_version()` unconditionally read `uv.lock`, which made `cz bump` crash with `FileNotFoundError` whenever the lock had not been written yet. The two reproducers in commitizen-tools#1383 are: - a freshly initialised uv project (`uv init`) before `uv sync` has run, - a uv workspace member where the lock lives at the workspace root, not alongside the package's own `pyproject.toml`. Add an `if not self.lock_file.exists(): return` early-out so updating `pyproject.toml` is enough; the lock is rewritten only when present. A regression test creates a `pyproject.toml` without a sibling `uv.lock` and asserts that `set_version` updates the project file and does not write a lock. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1979 +/- ##
=======================================
Coverage 98.23% 98.23%
=======================================
Files 61 61
Lines 2779 2780 +1
=======================================
+ Hits 2730 2731 +1
Misses 49 49 ☔ View full report in Codecov by Sentry. |
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes cz bump crashing for uv projects when uv.lock is absent by treating the lockfile as optional and adding a regression test for the missing-lock scenario.
Changes:
- Add an early return in
UvProvider.set_lock_version()whenuv.lockis missing. - Add a regression test ensuring bumping updates
pyproject.tomland does not error when nouv.lockexists.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
commitizen/providers/uv_provider.py |
Skip lock rewrite when uv.lock is not present to avoid FileNotFoundError. |
tests/providers/test_uv_provider.py |
Add regression coverage for projects without a lockfile. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+29
to
+33
| if not self.lock_file.exists(): | ||
| # `uv.lock` is optional: a freshly initialised project (or a uv | ||
| # workspace member, since the lock lives at the workspace root) | ||
| # may not have one yet. Updating `pyproject.toml` is enough. | ||
| return |
Address GitHub Copilot review feedback on PR commitizen-tools#1979: switch the optional-lock guard from `Path.exists()` to `Path.is_file()` and hoist it from `set_lock_version()` to `set_version()`. This matches the existing pattern in `commitizen/providers/cargo_provider.py:42` and `commitizen/providers/npm_provider.py:53,62`, and also avoids crashing if `uv.lock` happens to be a directory or symlink to a non-file (`exists()` is true for both, `is_file()` is not). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced May 9, 2026
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.
Description
Closes #1383.
Why
UvProvider.set_lock_version()unconditionally readuv.lockviaself.lock_file.read_text(), socz bumpcrashed withFileNotFoundError: [Errno 2] No such file or directory: 'uv.lock'whenever the lock had not been written yet. Two real-world reproducers:uv syncrun (the original reporter's reproduction, posted in the issue thread).pyproject.toml. A user reported this in a follow-up comment using uv 0.7.9 / cz 4.8.0.The maintainer (
@Lee-W) confirmed in the issue thread: "hmmm... I thought we have it long ago. yep, we'll definitely need it."What changed
commitizen/providers/uv_provider.pyset_version()now wraps theset_lock_version(version)call inif self.lock_file.is_file():, mirroring the convention used bycargo_provider.py:42andnpm_provider.py:53,62.set_lock_version()itself is unchanged so external callers that already verified the lock exists keep working.tests/providers/test_uv_provider.pytest_uv_provider_without_lock_file— creates apyproject.tomlwithout a siblinguv.lock, runsset_version("100.100.100"), assertspyproject.tomlwas updated and nouv.lockwas created.How it works
Path.is_file(), notPath.exists().exists()is also true for directories and symlinks-to-non-files, both of which would still tripread_text(). The cargo and npm providers already make this distinction; the uv provider was the outlier.set_version()rather than placed at the top ofset_lock_version(). This matchescargo_provider.py's structure and means the lock-rewrite path can keep its precondition: "if you callset_lock_version, the lock must exist."lock_fileproperty here resolves to the cwd-relativeuv.lock, which won't exist for a workspace member running from its own subdirectory; theis_file()guard skips the rewrite cleanly. A future PR could add proper workspace-root resolution, but that's a feature, not part of this fix — uv reconciles the lock on the nextuv synceither way.is_file()andread_text()) is benign — the subsequentread_text()would simply succeed with the new content.Backward compatibility
test_uv_provider[hyphenated|underscore]and produces byte-identical output.set_lock_version()keeps the same signature.Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and tests (poe lintclean; 3/3 uv_provider tests pass — 2 existing + 1 new)Expected Behavior
uv.lockyetcz bumpcrashes withFileNotFoundError: 'uv.lock'pyproject.tomlis updated, no lock written, exit 0.FileNotFoundErrorpyproject.tomlupdated, lock-rewrite skipped (workspace root will be reconciled by nextuv sync).uv.lockuv.lockis a directory or symlink to a non-fileis_file()(matches cargo/npm convention).Steps to Test This Pull Request
Additional Context
Surfaced while triaging open issues in #1976 (round 3). Switched from
Path.exists()toPath.is_file()in fixup commitf204942eafter GitHub Copilot's review noted that the existing convention incargo_provider.py:42andnpm_provider.py:53,62isis_file()(and thatexists()would still trip if the lock path happened to be a directory). The issue had been open for 11 months and was already labelledgood first issue+wait-for-implementation; the previously assigned contributor confirmed they were happy for someone else to take it over.