Skip to content

[pull] master from useblocks:master - #225

Merged
pull[bot] merged 1 commit into
boschglobal:masterfrom
useblocks:master
Sep 17, 2026
Merged

pull[bot] merged 1 commit into
boschglobal:masterfrom
useblocks:master

Conversation

@pull

@pull pull Bot commented Sep 17, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

…pgrade` job (#1951)

### Why

Dependabot's uv updater cannot update this lock, for two measured
reasons.

**It pins one candidate version on every requirement line before it
relocks.** This
workspace legitimately locks some packages at two versions across its
`[tool.uv] conflicts`
splits — `docutils` 0.20.1 in the `typing` group beside the newest
elsewhere,
`myst-parser` 4.x in the `sphinx-7` cell beside 5.x elsewhere. uv
refuses the universal
pin, and the updater treats the refusal as fatal to the whole job: every
group refresh
died with "Dependabot can't resolve your Python dependency files".

**Its default `versioning-strategy` rewrites manifest specifiers it has
no reason to
touch.** #1940 raised `matplotlib>=3.3.0` to `>=3.11.2` on a user-facing
extra whose floor
already allowed the release, and widened `click < 8.2` past the comment
explaining the cap.

Three merged pull requests (#1942, #1944, #1946) and a fourth (#1948,
six more ignore rules,
closed unmerged in favour of this) went into working around that.
Meanwhile `uv lock --upgrade` does the job in
one command: on master today it resolves cleanly across every split and
moves 13 packages,
including `docutils v0.20.1, v0.21.2 -> v0.20.1, v0.22.4` — the exact
thing dependabot
cannot express — and it never touches a manifest.

### What this does

Adds `.github/workflows/uv-update.yaml` — `UV update`, monthly on `0 7 1
* *` (an hour
after `Prek update`, so both land on the same morning and neither
depends on the other)
plus `workflow_dispatch` — modelled step for step on `prek-update.yaml`:
the bot-app token,
`actions/checkout@v7`, `astral-sh/setup-uv@v10.1.0` on the root
`.python-version`, and one
pull request from the fixed branch `uv-update` titled `⬆️ Update the uv
lock (monthly)`.

The steps: `uv lock --upgrade`, capturing uv's lock-diff lines (stderr,
so `2>&1` into
`$RUNNER_TEMP`); an assertion that **no manifest changed**, which fails
the job loudly if
one did; `uv sync --frozen` and `uv run --frozen prek run --all-files`,
recording whether
the hooks came out clean; the held-back list; and the composed body.

The body reports **all three of uv's lock-diff verbs**, not just
`Updated`. `Added` and
`Removed` get bullets of their own — a package entering the dependency
graph is the most
review-worthy line a lock update can carry, and a body that drops it
silently is worse than
one that says nothing — and any other `<Verb> <name> v<version>` line uv
may invent is
printed as `unrecognised uv line:` rather than discarded, so the body
announces a new verb
instead of quietly shrinking. Measured on a real 18-line upgrade log: 18
lines in, 18
bullets out.

Then `.github/dependabot.yml` loses its uv section — ignore rules, `ty`
group and all —
and gains ten lines saying why. `AGENTS.md`'s dependabot-review
paragraph is replaced by
one paragraph on the job, the same length as the one it replaces, with
the history left in
`dependabot.yml` where it belongs.
`tools/tests/test_dependabot_config.py` fences the
configuration so the uv entry cannot come back, and fences the
replacement *as a command*
in the `update` step — `uv lock --upgrade` also appears in prose in the
same file, so a
substring check would stay green while the job was gutted to a no-op
relock.

### What stays with dependabot

The **`github-actions`** ecosystem, untouched: that half has never had a
problem here.
And **security alerts and security-update pull requests**, which are a
repository feature
reading `uv.lock` independently of `dependabot.yml`'s version-update
config — removing the
uv section does not stop them.

### Two decisions, with their measurements

**No cooldown**, unlike `prek update --cooldown-days 7`. `uv lock
--upgrade --exclude-newer`
is a *global index cutoff*, not a per-package delay, and it failed both
halves of the test:
at seven days it made the lock unresolvable (`Because only ty<=0.0.79 is
available and
sphinx-needs-workspace:typing depends on ty>=0.0.80,<0.1.dev0 …` — the
`typing` group's own
floor is younger than the window, and so would be any floor a release
bump had just
raised); and with a cutoff recent enough to resolve, it wrote `[options]
exclude-newer` into
`uv.lock`, after which a plain `uv lock --check` reported "The lockfile
at `uv.lock` needs
to be updated" and the `uv-lock` prek hook rewrote the lock and failed —
on the job's own
pull request. The workflow carries a comment saying so. Dependabot had
no cooldown here
either, so nothing is lost.

**The held-back list comes from `uv tree --outdated --frozen --depth
1`**, run after the
upgrade. `uv pip list --outdated` was tried first and cannot do it: it
reads one installed
environment, so `pydata-sphinx-theme` (behind the `theme-pds` extra) is
invisible to it —
as is every conflicting group. `uv tree` reads the lock, `--depth 1` is
each member's own
requirements, and running it after the upgrade leaves only what is held
back rather than
what was merely pending. Scored against the known positives: it flags
`jsonschema-rs`
(0.53.0, capped `<0.54.0`) and `pydata-sphinx-theme` (0.17.1, capped
`<0.20.0`), and
correctly does *not* flag `comment-parser` (already newest wherever
installable) or
`myst-parser` (already 5.1.0 by default; 4.0.1 survives only in the
`sphinx-7` and `typing`
cells, held by `sphinx~=7.4`). A grep over the manifests puts the
specifier beside the
versions — and the body says plainly what that column is: every
specifier this workspace
writes for that name, while what actually blocks the newer release may
be one of those, a
cap elsewhere in the graph, or a `[tool.uv] conflicts` split, with
`uv lock --upgrade-package <name>` as the way to find out. The list is
the **default
resolution's** view; a package held back only in a matrix split is not
in it, and the body
says that too.

### Proving it without merging it

A `workflow_dispatch` needs the file on the default branch, so the five
`run:` blocks were
extracted from the YAML *verbatim* (parsed with pyyaml, the way
`tools/tests/test_ci_plantuml_steps.py` reads workflow YAML) and run
locally under
`bash -e` with the runner's environment variables. Three runs:

* **on today's lock** — `changed=true`, the 13 `Updated` lines captured,
manifests
untouched, all eight hooks Passed against the upgraded lock (ty
included), the held-back
  list carrying both known positives, and the body composed in full;
* **with the lock already current** — `changed=false`, "no lock updates
available; nothing
to open a pull request for", and every later step skipped by its `if:`;
* **with a floor edited by hand** (dependabot's own `matplotlib>=3.3.0`
→ `>=3.11.2`) — the
assert step fails with `::error::uv lock --upgrade edited a manifest;
this job only ever
  moves uv.lock` and `exit 1`.

Plus, for the body's new verbs: a constructed log covering
`Updated`/`Added`/`Removed`/an
unknown verb, and a real 18-line upgrade log rebuilt from a two-week-old
`uv.lock`. And for
the fence: weakening the update step's `uv lock --upgrade` to `uv lock`
turns the test red.

`uv.lock` is deliberately **not** part of this pull request; the tree
was returned to clean
before anything was committed.

### After the merge

Dispatch `UV update` once by hand as the rehearsal; it opens the first
real pull request.
Then delete the `ci/dependabot-ignore-unlockable` branch, kept on origin
as the fallback
until this landed.

### Review

Built by one agent against a binding brief, reviewed by a second that
re-ran every proof from its own extraction of the YAML and added 15
mutations of its own; its three findings (the dropped `Added`/`Removed`
lines, the fence matching prose, the misleading "no upper bound"
sentence) are the fix round folded in above, and it re-verified the two
mechanisms afterwards (the invented verbs `Downgraded`/`Yanked` come out
loud, five uv summary shapes stay ignored; the fence is red for a
weakened command and green for weakened prose). Two constructed, not
observed, residuals remain and are accepted: a version *pair* on an
`Added`/`Removed` line would malform that bullet's PyPI link (the
package is still named), and a hyphenated verb would slip the catch-all.
Gates re-run by hand on the final head: `uv run poe lint` 8/8, `pytest
tools/tests` 316 passed.
@pull pull Bot locked and limited conversation to collaborators Sep 17, 2026
@pull pull Bot added the ⤵️ pull label Sep 17, 2026
@pull
pull Bot merged commit 58bcb59 into boschglobal:master Sep 17, 2026
28 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant