Skip to content

Pin contributor uv version to CI and document deliberate uv upgrades - #2451

Open
Flix6x with Copilot wants to merge 7 commits into
mainfrom
copilot/pin-uv-for-contributors
Open

Pin contributor uv version to CI and document deliberate uv upgrades#2451
Flix6x with Copilot wants to merge 7 commits into
mainfrom
copilot/pin-uv-for-contributors

Conversation

Copilot AI commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

uv.lock was being rewritten by contributors using a newer uv than CI, causing large non-semantic lockfile diffs and avoidable merge/review churn. This makes the required uv version part of the project, so that it is enforced where the damage is done rather than caught in review.

Note (updated after review): this PR pins uv to 0.12.7, not to CI's previous 0.10.9. That is a deliberate upgrade — 0.10.9 is from March and two minor releases behind, and pinning it would have frozen every contributor, plugin developer, self-hoster and CI job to it. All uv pins move together to 0.12.7 here.

What it does

  • Enforce the version at the source (pyproject.toml):

    [tool.uv]
    required-version = "==0.12.7"

    uv then refuses to run on a mismatch, with an actionable message.

  • Pin every place uv runs, since required-version gates every uv invocation and not just uv lock:

    • the version input of astral-sh/setup-uv in lint-and-test.yml, docker-build.yml, docker-qa.yml, pypi-publish.yml and actions/setup-test-env/action.yml
    • ARG UV_VERSION in the Dockerfile (it used the floating 0.10 image tag, which now ships a different patch release)
    • the asdf commands in .readthedocs.yaml (they installed 0.10.3)
  • Regenerate uv.lock with the pinned uv, in its own commit. 0.12.7 writes markers in a more compact canonical form, so the first write under the new pin reformats the file. Doing it here means it does not land on top of the next real dependency change — which is exactly the churn Pin uv for contributors too, and make upgrading it a deliberate step #2449 is about. No package is added, removed or re-versioned in that commit: git show ee9c2ea68 -- uv.lock | grep -E '^[+-](name|version) = ' is empty.

  • Run pre-commit without tox-dev/action-pre-commit-uv. That action runs pre-commit as uv run --with pre-commit-uv ..., and pre-commit-uv depends on uv>=0.9.1, so the overlay environment put the latest uv ahead of the pinned one on PATH — where our entry: uv run black hooks picked it up. All our hooks are language: system, so it had no hook environments to speed up anyway. pre-commit itself now comes from the locked dev group, so CI does not follow its latest release either.

  • Document it: dependency-management.rst lists every place the version is pinned so a bump moves them together, notes that uv self update is unavailable for pip/brew installs, and the deploy guide notes that servers running uv sync --frozen need the pinned version too.

Trade-off to be aware of

The pin reaches beyond contributors: anyone running uv in a FlexMeasures checkout needs that exact version, including plugin developers and self-hosters following automated-deploy-via-GHActions.rst. The changelog entry says so. A range like >=0.12,<0.13 would be softer, but lets patch-level marker churn back in, which is the thing being prevented.

@read-the-docs-community

read-the-docs-community Bot commented Aug 31, 2026

Copy link
Copy Markdown

Documentation build overview

📚 flexmeasures | 🛠️ Build #34318397 | 📁 Comparing 1a6afff against latest (c33bc71)

  🔍 Preview build  

5 files changed · ± 5 modified

± Modified

Co-authored-by: Flix6x <30658763+Flix6x@users.noreply.github.com>
Copilot AI changed the title [WIP] Pin uv version for contributors to ensure consistency Pin contributor uv version to CI and document deliberate uv upgrades Aug 31, 2026
Copilot AI requested a review from Flix6x August 31, 2026 15:42
@Flix6x Flix6x added this to the 1.1.0 milestone Aug 31, 2026
Flix6x added 2 commits August 31, 2026 19:30
required-version gates every uv invocation, not only `uv lock`, so pinning it
to ==0.10.9 broke every build that ran a different uv:

- the Docker build used the floating `0.10` image tag, which now ships 0.10.12
- Read the Docs installed 0.10.3 via asdf
- the pre-commit job ran through tox-dev/action-pre-commit-uv, which invokes
  pre-commit with `--with pre-commit-uv`; that pulls in the latest uv (0.12.7)
  and puts it ahead of the pinned one on PATH, where our `uv run ...` hooks
  pick it up

Pin the Dockerfile and Read the Docs to 0.10.9, and run pre-commit with plain
`--with pre-commit` (all our hooks are `language: system`, so pre-commit-uv has
no hook environments to speed up anyway).

Also document every place the version is pinned, so a uv bump moves them
together, note that `uv self update` is unavailable for pip/brew installs, and
fill in the changelog PR number.

Signed-off-by: F.N. Claessen <felix@seita.nl>
The documented deploy hook runs 'uv sync --frozen' in a checkout of this repo,
so required-version applies there as well.

Signed-off-by: F.N. Claessen <felix@seita.nl>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Aligns contributor tooling with CI by pinning an exact uv version at the source (pyproject.toml) and documenting the workflow for deliberate uv upgrades, reducing noisy/non-semantic uv.lock diffs.

Changes:

  • Enforce an exact uv version via [tool.uv].required-version and update Docker/RTD/CI to match
  • Update contributor and dependency-management docs to explain the pin and how to upgrade uv deliberately
  • Add a changelog entry describing the new uv pinning behavior for reproducible lockfile output

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
pyproject.toml Enforces exact uv version via [tool.uv].required-version
documentation/dev/setup-and-guidelines.rst Adds contributor guidance to pin uv locally
documentation/dev/dependency-management.rst Documents the enforced uv version and the coordinated upgrade procedure
documentation/dev/automated-deploy-via-GHActions.rst Notes server-side uv must match the pinned version
documentation/changelog.rst Adds Infrastructure / Support changelog entry for uv pinning
Dockerfile Pins the uv base image tag to the exact required version
.readthedocs.yaml Pins uv version in Read the Docs build environment
.github/workflows/lint-and-test.yml Runs pre-commit without action-pre-commit-uv to avoid overriding the pinned uv

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pyproject.toml Outdated
Comment thread Dockerfile Outdated
Comment thread documentation/dev/dependency-management.rst Outdated
0.10.9 dates from March and predates two minor releases, and pinning it made
that the version every contributor, plugin developer, self-hoster and CI job
has to run. So bump it to current uv while we are touching all the pin sites
anyway. uv.lock is unchanged: 0.12.7 accepts it as up to date and rewrites
nothing.

Also add .github/workflows/docker-build.yml, which pins setup-uv too, to the
list of places to bump (spotted by Copilot), rewrap the new comments so lines
break only after punctuation, and say in the changelog who the pin reaches.

Signed-off-by: F.N. Claessen <felix@seita.nl>
@Flix6x

Flix6x commented Aug 31, 2026

Copy link
Copy Markdown
Member

Reviewed and pushed corrections. Two things worth deciding on explicitly before merge.

1. The pin reaches further than contributors. [tool.uv].required-version gates every uv invocation in a checkout, not just uv lock — I verified that uv venv, uv pip list and uv sync all refuse to run on a mismatch. So it applies to:

  • contributors (the intent),
  • plugin developers who clone FlexMeasures and use uv in it,
  • self-hosters: the deploy hook in documentation/dev/automated-deploy-via-GHActions.rst runs uv sync --frozen in a checkout, so those servers need the pinned version too (there is now a note in that doc),
  • every build surface: the Docker image, Read the Docs, and all five astral-sh/setup-uv pins.

The issue framed the strict-vs-range trade-off around lockfile writers only. It is wider than that, so the changelog entry now says so. If that reach is unwanted, >=0.12,<0.13 would still stop the big jumps while letting patch releases through — but then patch-level marker churn can slip back in, which is the thing we are trying to prevent.

2. The version is now 0.12.7, not 0.10.9. 0.10.9 is from March and two minor releases behind, and pinning it would have frozen everyone above to it. uv.lock is unchanged: 0.12.7 accepts it as up to date (uv lock --check passes) and uv lock rewrites nothing, so there is no lockfile churn in this PR.

What was broken. The pin as originally proposed only covered the place the lock is written, so every build that ran a different uv failed — all four red checks had the same root cause:

Where uv it ran Fix
Dockerfile floating ghcr.io/astral-sh/uv:0.10 tag, i.e. 0.10.12 ARG UV_VERSION, exact tag
.readthedocs.yaml asdf install uv 0.10.3 bumped
lint-and-test.yml check job 0.12.7 see below
changelog PR #XXXX placeholder filled in

The CI one is the subtle one: tox-dev/action-pre-commit-uv@v1 runs pre-commit as uv run --with pre-commit-uv ..., and pre-commit-uv depends on uv>=0.9.1, so the overlay environment puts the latest uv ahead of the pinned one on PATH — which is what our entry: uv run black hooks then pick up. Verified directly: with --with pre-commit-uv, uv resolves into the ephemeral cache; with plain --with pre-commit, it stays the pinned binary. All our hooks are language: system, so pre-commit-uv had no hook environments to speed up here anyway.

All checks are green now. Also documented every place the version is pinned, so a future bump moves them together, and noted that uv self update is unavailable for pip/brew installs (uv's own error message suggests it unconditionally).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread pyproject.toml
Comment thread .github/workflows/lint-and-test.yml Outdated
Flix6x added 2 commits August 31, 2026 21:22
uv 0.12.7 writes markers in a more compact canonical form than 0.10.9 did, so
the first lockfile write under the new pin reformats the whole file. Do that
here, on its own, rather than leaving it to land on top of the next real
dependency change, which is the churn issue #2449 is about.

No dependency changes: no package is added, removed or moved to another
version, and no wheel or sdist entry changes. The diff is entirely uv dropping
markers that are redundant given [tool.uv].environments, e.g.

    { name = "mako", marker = "sys_platform == 'darwin' or sys_platform == 'linux' or sys_platform == 'win32'" }

becomes

    { name = "mako" }

Verify with: git show --stat, then
  git show -- uv.lock | grep -E '^[+-](name|version) = '   # empty

Signed-off-by: F.N. Claessen <felix@seita.nl>
The check job ran `uv run --with pre-commit`, which resolves whatever
pre-commit is latest at run time, so CI could change behaviour without any
commit here (spotted by Copilot). Add pre-commit to the dev dependency group
instead, where uv.lock pins it and the periodic dependency-upgrade PR bumps it
along with everything else.

The dev group is already installed in that job, so the step is just
`uv run --no-sync pre-commit run --all-files`.

Signed-off-by: F.N. Claessen <felix@seita.nl>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedpre-commit@​4.6.293100100100100

View full report

@Flix6x
Flix6x marked this pull request as ready for review August 31, 2026 22:59
@Flix6x
Flix6x requested a review from nhoening August 31, 2026 23:00

@Flix6x Flix6x left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nhoening there seems to be a tradeoff that I'd like a second opinion on from you.

@nhoening

nhoening commented Sep 1, 2026

Copy link
Copy Markdown
Member

If one can have multiple uv versions on a system, then I guess the inconvenience to others isn't a big problem. Can one?

Also, I'd like to know if @TeaDrinkingProgrammer has an opinion here.

Flix6x added a commit that referenced this pull request Sep 1, 2026
Context:
- Only uv.lock conflicted. Both sides listed flexmeasures' own dependencies, main having added limits and this branch croniter and cron-descriptor.
- The two lockfiles were also written by different uv versions, which normalise environment markers differently, so the sides disagreed on nearly every line rather than only on those three packages.

Change:
- Took main's uv.lock and regenerated it with uv 0.10.9, the version that wrote it (see #2451, which pins this and will later move everything to 0.12.7). That adds cron-descriptor and nothing else: 16 insertions, no deletions, and no package re-versioned.
- Resolving it by hand was not viable: keying on the package name drops the Python 3.10 halves of version-split entries such as pint 0.24.4, and keying on the whole line keeps both marker spellings of every package.

Signed-off-by: F.N. Claessen <felix@seita.nl>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pin uv for contributors too, and make upgrading it a deliberate step

4 participants