Skip to content

Remove uv version requirements in third-party tests - #800

Merged
JelleZijlstra merged 2 commits into
mainfrom
alex/remove-uv-version-requirements
Oct 12, 2026
Merged

JelleZijlstra merged 2 commits into
mainfrom
alex/remove-uv-version-requirements

Conversation

@AlexWaygood

@AlexWaygood AlexWaygood commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

A third-party checkout can require a newer uv version than the one pinned in CI, causing the third-party tests to fail before they run.

Before running each third-party test suite, remove any uv required-version from the checkout's root pyproject.toml or uv.toml, preserving other uv settings. The helper leaves files unchanged when there is no requirement and fails without updating either file if it cannot safely remove one as a single-line assignment.

Fixes #798.

Related: hauntsaninja/mypy_primer#262.

Alternative design considered: a PEP-723 script

I initially tried making this helper script a PEP-723 script that used tomlkit. That design is preserved as the first commit on this branch, and it would be more general and more robust. I decided against it, however, on YAGNI grounds. For our purposes, I think introducing a PEP-723 script -- which would require its own lockfile, which would need to be checked and updated in CI -- would be too much complexity for a CI helper that has a very specific, limited purpose.

Alternative design considered: an off-the-shelf GitHub workflow

I also considered using cssnr/toml-action v2.0.0, which could be used to update the tool.uv.required-version fields in pyproject.toml files to >=0.0.0, for example:

- name: Allow the CI uv version in pyproject.toml
  if: ${{ hashFiles('pydantic/pyproject.toml') != '' }}
  uses: cssnr/toml-action@fde2b2f22f30923c248d1bddc1e431437d6e314f  # v2.0.0
  with:
    file: pydantic/pyproject.toml
    path: $.tool.uv.required-version
    value: ">=0.0.0"
    write: true
- name: Allow the CI uv version in uv.toml
  if: ${{ hashFiles('pydantic/uv.toml') != '' }}
  uses: cssnr/toml-action@fde2b2f22f30923c248d1bddc1e431437d6e314f  # v2.0.0
  with:
    file: pydantic/uv.toml
    path: $.required-version
    value: ">=0.0.0"
    write: true
Codex's comparison of the two approaches

If we used cssnr/toml-action v2.0.0, each job would need equivalent steps for its checkout. The conditions skip missing files; if a file exists but the key does not, the action creates the key. Neither approach guarantees that the pinned uv actually supports every feature a checkout needs, so a checkout could still fail later for compatibility reasons.

Current checkouts. I tested both approaches against root configuration snapshots from the nine repositories in the workflow, including both SQLAlchemy branches. Only pydantic had a root uv requirement, >=0.13.0. The helper succeeded on all ten snapshots and changed only pydantic’s file. The action succeeded on the nine existing pyproject.toml files, with no changes to parsed values or their types except for the intended requirement. It rewrote all nine files, removed their comments, and added the requirement to the other eight. The remaining checkout, typing_inspect, had neither root configuration file; none had a root uv.toml.

Preserving configuration. The action parses and serializes the whole file, so it can silently change unrelated values. In a synthetic example, it changed the TOML float 1.0 to the integer 1 and shortened the timestamp 1979-05-27T07:32:00.123456Z to 1979-05-27T07:32:00.123Z. Formatting and comment changes alone are unlikely to matter in these temporary CI checkouts, but changed values could matter if uv, the build, or the tests use them. I found no such value changes in the current snapshots.

The helper removes a single line and checks that the parsed configuration otherwise matches. It prepares changes to both files before writing them, so an unsupported requirement in either file causes it to stop without changing either. An I/O error during the later writes could still leave a partial update.

Supported layouts. The action handled an inline table such as:

[tool]
uv = { required-version = ">=0.13.0", no-cache = true }

The helper intentionally supports standalone single-line assignments and stops if it cannot safely remove the requirement. Thus the action handles more valid layouts, while the helper can make a job fail if a checkout changes to an unsupported layout.

Configuration discovery. Adding a previously absent [tool.uv] table can change which configuration uv discovers. In a synthetic hierarchy, uv initially honored a parent uv.toml; after the action created [tool.uv] in the checkout’s pyproject.toml, uv no longer discovered that parent file. I have not established that the actual runner workspace has an affected parent configuration. See uv’s configuration discovery rules.

Supply chain. The helper uses Python’s standard library and introduces no additional Python package. The action adds a separate third-party code supply chain: the example pins its approximately 1.1 MB JavaScript bundle to a full commit SHA, and it does not install npm packages at runtime. Pinning prevents the referenced bundle from changing, but the action and its bundled dependencies still need to be trusted and reviewed.

Parser availability. The action’s bundle contains smol-toml 1.7.1, which is affected by a quadratic-time parsing advisory; the fix is in 1.9.0. In a local probe, files with 32,000, 64,000 and 128,000 synthetic keys took approximately 0.18, 0.46 and 1.56 seconds respectively. The current configuration snapshots are at most 510 lines. A sufficiently large TOML file could stall a job until its existing 10- or 60-minute timeout. This is an availability issue, not evidence of code execution.

Runner commands in logs. The action prints the original selected value without escaping it. I verified locally that a multiline required-version can put ::warning::... at the start of an output line. GitHub processes such lines as workflow commands, so spoofed annotations or other supported log commands are plausible; I did not test runner interpretation. The helper rejects that multiline assignment without printing the value. The workflow already executes code from the checked-out repositories, so this does not establish a new isolation boundary against a compromised checkout.

The PR’s third-party CI run passed all 43 matrix jobs. My action probes ran locally under Node 26, rather than the action’s Node 24 runtime, and did not run the full third-party test suites. The workflow clones moving branches, so the snapshots can change.

I would keep the helper: the action handles more TOML layouts, but it can silently change unrelated values and adds a separate code supply chain. Those drawbacks have not caused a failure in the current snapshots, so this is a tradeoff rather than evidence that the action would break the present workflow.

@codecov

codecov Bot commented Oct 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.26%. Comparing base (3ae9b75) to head (8040f33).

@@           Coverage Diff           @@
##             main     #800   +/-   ##
=======================================
  Coverage   97.26%   97.26%           
=======================================
  Files           3        3           
  Lines        7859     7859           
=======================================
  Hits         7644     7644           
  Misses        215      215           
Flag Coverage Δ
3.10 88.85% <ø> (ø)
3.10.4 88.85% <ø> (ø)
3.11 88.02% <ø> (ø)
3.11.0 87.27% <ø> (ø)
3.12 87.96% <ø> (ø)
3.12.0 87.94% <ø> (ø)
3.13 83.39% <ø> (ø)
3.13.0 84.09% <ø> (ø)
3.14 79.53% <ø> (ø)
3.9 89.53% <ø> (ø)
3.9.12 89.53% <ø> (ø)
pypy3.10 88.68% <ø> (ø)
pypy3.11 87.88% <ø> (ø)
pypy3.9 89.37% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlexWaygood
AlexWaygood marked this pull request as ready for review October 12, 2026 11:32
@AlexWaygood

Copy link
Copy Markdown
Member Author

I expanded the PR description with some other alternative designs I considered here. I think this is probably the best route for now.

@JelleZijlstra
JelleZijlstra merged commit f31feb4 into main Oct 12, 2026
70 checks passed
@JelleZijlstra
JelleZijlstra deleted the alex/remove-uv-version-requirements branch October 12, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Third-party tests failed on Mon Oct 12 2026

2 participants