Release notes stay compare-only once a lookup fails: the fallback is cached under repository:version and every hit refreshes its TTL
#46568
Unanswered
aslafy-z
asked this question in
Request Help
Replies: 1 comment
|
Thanks! It's a very nice catch. Fix coming #46612 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
How are you running Renovate?
A Mend.io-hosted app
Which platform you running Renovate on?
GitHub.com
Which version of Renovate are you using?
44.112.0 (hosted app job log and local reproduction)
Please tell us more about your question or problem
What I saw. In one repository on the hosted app, a few PRs render a Release Notes section that has only the version header and a
[Compare Source]link, while other PRs from the same runs render full bodies. One of them was an image bumpv3.2.2 → v3.2.4whose upstream GitHub release has a body; the PR body stayed compare-only over every run for more than a day. The job log showedFetching changelog: <sourceUrl> (v3.2.2 -> v3.2.4)on each run, thenPull Request #<n> does not need updating, so the fetch was attempted, kept returning nothing, and nothing in the log said why.How I traced it. I called
getChangeLogJSON()from renovate@44.112.0 directly (Node 24, RE2 engine, empty package cache) with the same upgrade config the job used. It returned the full release body on the first try. So the config, the tag matching, and the GitHub side were fine, and the only input left that differed from the hosted run was the package cache. ReadingaddReleaseNotes()inlib/workers/repository/update/pr/changelog/release-notes.tsshowed that release notes are cached underchangelog-github-notes@v2with the key<repository>:<version>(or<repository>:<sourceDirectory>:<version>whensourceDirectoryis set), and that a lookup which finds nothing is written to that cache too, as{ url: <compare url>, notesSourceUrl: '' }.How I confirmed it. On the hosted app I added
sourceDirectoryto the affected package rule, which only changes the cache key. On the next run the same PR rendered the full body, and the source link switched totree/HEAD/<dir>, proving the new key was in use. Nothing else changed. I then built a public repository that shows both outcomes side by side on the hosted app (below).Why it happens. Per version,
addReleaseNotes()does:packageCache.get(namespace, '<repository>:<version>')getReleaseNotesMd(), thengetReleaseNotes()releaseNotes = { url: v.compare.url, notesSourceUrl: '' }packageCache.set(namespace, key, releaseNotes, releaseNotesCacheMinutes(v.date)), unconditionallyThree things combine. The fallback object is truthy, so it is stored exactly like real notes. The key contains none of the inputs that decide whether a lookup can succeed (
packageName,extractVersion,versioning), so the first writer for a given upstream version decides for every configuration. Step 4 runs on hits as well, so each read rewrites the entry with a fresh TTL instead of retrying; an entry that is read at least once per TTL window never expires. The hosted app has one package cache for all repositories, so for a popular package the first run anywhere that stores the fallback, because its rule cannot match the tag or because of a transient failure, fixes the outcome for everyone until the next version.Public reproduction on the hosted app: https://github.com/aslafy-z/renovate-reproduction-stale-cache
versions.yamlpins therook-cephHelm chart (https://charts.rook.io/release) twice atv1.20.7, under two dependency names with the samepackageName. Both resolve togithub.com/rook/rookthrough the chart index and both update tov1.20.8, whose GitHub release has a body. The only difference is one package rule giving the second namesourceDirectory: deploy/charts/rook-ceph, the directory the chart is published from. It holds no changelog file, so Renovate falls back to the GitHub release as usual, but the cache key becomesrook/rook:deploy/charts/rook-ceph:v1.20.8instead ofrook/rook:v1.20.8.Same repository, same chart, same release, same run, Renovate 44.112.0:
rook/rook:v1.20.8rook/rook:deploy/charts/rook-ceph:v1.20.8Two notes for anyone reproducing. The key also appends the release
gitRefwhen the datasource provides one;github-releasesdoes, so a pin through that datasource usesrook/rook:v1.20.8:v1.20.8and did render notes in a first attempt, while Helm and Docker consumers share the bare key. And the same behaviour reproduces self-hosted without a shared cache: a first run with a rule that cannot match the tag (for examplenginxagainstrelease-<semver>tags withoutextractVersion) stores the fallback, and a second run against the samecacheDirwith a matchingextractVersionstill renders compare-only, while a freshcacheDirrenders the notes.Suggested fix, any one of which breaks the loop:
packageCache.set()only when one of the two fetchers returned notes. The compare URL is rebuilt from the tag list on every run anyway.packageCache.set()into the miss path.packageName,extractVersion,versioning) to the key. This would also cover Wrong changelog content when updating different dependencies that come from the same repository #36939 and Release notes: packages sharing one sourceUrl lose their notes and can be attributed to the wrong package #46302, which are about the same key colliding between packages of one repository.Related but different: #36939 and #46302 (key collision between packages sharing a repository), #34109 (deduplication of notes across upgrades of one repository), #44081 (LinuxServer images, configuration only).
Logs (if relevant)
Logs
Hosted app job log, identical on every run while the PR stayed compare-only:
Rendered section of that PR:
All reactions