Recommend the smallest upgrade that clears an advisory, not the highest
All checks were successful
CI / naming (push) Successful in 4s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 42s
CI / backend (push) Successful in 4m18s

The rollup took the highest fix version across a component's advisories. That
is wrong whenever an advisory is patched on more than one release branch.

nanoid is the case that exposed it. Both advisories against it are fixed on two
branches - 3.3.16 and 5.1.16, 3.3.18 and 5.1.6 - and the rollup recommended
5.1.16 with a MAJOR warning. But nanoid is transitive, pulled in by postcss at
`^3.3.11`, a range no 5.x release can satisfy. The recommendation was not merely
a bigger job than necessary, it was unreachable: the actual target is 3.3.18, a
patch inside the existing range.

Per advisory the target is now the lowest published fix above the version in
use; per component it is the highest of those, since one upgrade has to satisfy
every advisory against it. Fixes on older branches are skipped rather than
offered as a downgrade - axios advisories list 0.30.x fixes that would have been
a downgrade from 1.13.2.

This also gives the MAJOR label meaning. It now fires only when no fix exists on
the current branch, rather than whenever some branch happened to be higher.
This commit is contained in:
cproudlock
2026-08-29 17:24:37 -04:00
parent c4e2db4755
commit 1cc2a6b782
3 changed files with 74 additions and 14 deletions

View File

@@ -76,10 +76,17 @@ add an entry to make a build pass.
## What the report tells you to do
The report ends with a per-component upgrade rollup: the highest fix version
across that component's advisories, and how many it clears. Upgrading once to
the top of the list clears the ones below it.
The report ends with a per-component upgrade rollup: the smallest upgrade that
clears every advisory against that component, and how many it clears.
A target on a different major version is labelled `MAJOR`. It is a different
job from a patch bump - it can be ESM-only, or drop an API - and it is marked
rather than listed as though the two cost the same.
"Smallest" is doing real work there. An advisory patched on several release
branches lists a fix per branch - nanoid's is fixed in both 3.3.18 and 5.1.16 -
and taking the highest recommends a major upgrade when a patch on the current
branch would do. Where the package is transitive that major may not even be
reachable: nanoid arrives under postcss, pinned `^3.3.11`, a range 5.x cannot
satisfy. So the target is the lowest published fix above the version in use.
Fixes on older branches are ignored rather than offered as a downgrade.
A target on a different major version is still labelled `MAJOR`, but now that
label means something: no fix exists on the current branch, so the upgrade is
genuinely a different job - it can be ESM-only, or drop an API.