Bug: postUpgradeTasks commits stale content when updateArtifacts() produces duplicate entries for the same file path #42729
Replies: 5 comments 1 reply
|
i would love some feedback, if we need to provide a target repo as a means to make it reproducable then we should be able to somehow create that, though it might not be as straight forward since it is based on an inner source project with many Nuget Packages with internal among them. |
|
Hi there, Please help this Discussion progress by creating a minimal reproduction. This means a repository dedicated to reproducing this issue with the minimal dependencies and config possible. Before we start working on your issue we need to know exactly what's causing the current behavior. A minimal reproduction helps us with this. Discussions without reproductions are less likely to be converted to Issues. Please follow these steps:
If you need help with running Renovate on your minimal reproduction repository, please refer to our Running Renovate guide. The Renovate team |
|
We hit this independently on a Go workspace monorepo, so here is a minimal reproduction and a fix. Reproduction: https://github.com/dlm6693/renovate-repro-42729 Two modules in a Fix: #45891 — Option A from the original report, plus a unit test that fails on How the duplicates arise for gomod, in case it is useful alongside the .NET case: Production evidence. On a grouped non-major bump across a 10-module workspace, Renovate logged |
|
Both branches Renovate pushed during that verification are preserved in the reproduction repo, so the result can be checked without re-running anything:
The only difference between them is the single line the bug dropped: $ git diff origin/evidence/unpatched-main origin/evidence/with-fix
--- a/app/go.sum
+++ b/app/go.sum
@@ -1,2 +1,3 @@
+// renovate-post-upgrade-markerUse a two-dot diff; GitHub's compare view is three-dot and also shows the dependency bump against
|
|
Adding a third ecosystem: uv workspaces (pep621 manager). We reproduced it end to end with self-hosted 44.104.0. The code cited below is unchanged in 44.126.0 and on When uv produces duplicate entriesEvery member of a uv workspace shares the one
So a second
From there it is the path already described above:
Minimal reproductionThis needs self-hosted Renovate, because the task must match
[project]
name = "root"
version = "0"
requires-python = ">=3.12"
[tool.uv.workspace]
members = ["a", "b"]
[project]
name = "a"
version = "0"
requires-python = ">=3.12"
dependencies = ["six>=1.15"]
[project]
name = "b"
version = "0"
requires-python = ">=3.12"
dependencies = ["idna>=3.0"]
#!/bin/sh
printf '# post-upgrade-marker\n' >> uv.lock
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"enabledManagers": ["pep621"],
"rangeStrategy": "update-lockfile",
"groupName": "all dependencies",
"postUpgradeTasks": {
"commands": ["sh post-upgrade.sh"],
"fileFilters": ["uv.lock"],
"executionMode": "branch"
}
}We ran it with Expected: branch Actual (44.104.0):
Controls (same binary and setup):
With #45891 applied, the committed A task that fails after writing. Same tree, but
Fix#45891 fixes both cases on this tree. Collapsing the duplicate |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
How are you running Renovate?
self-hosted
Which platform you running Renovate on?
Azure
Which version of Renovate are you using?
43.111.1
Please tell us more about your question or problem
Describe the bug
When multiple
updateArtifacts()calls produce duplicate entries in theupdatedArtifactsarray for the same file path,postUpgradeCommandsExecutoronly updates the first matching entry (because it uses.find()).The second (duplicate) entry keeps the stale pre-script content.
Then
prepareCommit()writes every entry sequentially (last write wins), so the stale second entry overwrites the correctly updated first one on disk.Result:
postUpgradeTasksscripts run correctly and modify files on disk"Post-upgrade file saved"How duplicates arise (very common in some ecosystems)
In a .NET CPM monorepo this happens naturally:
nuget.updateArtifacts(Directory.Packages.props)→ regenerates ~125 lock filesnuget.updateArtifacts(global.json)→ regenerates the same ~125 lock filesnuget.updateArtifacts(SomeApp.csproj)→ regenerates them again→
updatedArtifactsends up with 248 entries for ~125 unique paths (123 paths duplicated).This pattern also appears in pnpm catalogs, Flutter, and any datasource where multiple package files trigger overlapping artifact regeneration.
Root cause (with code locations)
In
lib/workers/repository/update/branch/execute-post-upgrade-commands.ts(v43.111.1):In
lib/util/git/index.ts→prepareCommit()/commitFilesToBranch:(Note: the cosmetic
new Set()deduplication only affects the log line, not the actual array.)Proof
We ran a “marker test”: a post-upgrade script injected a unique string into the lock files. Renovate logged “Post-upgrade file saved” (it read the new content), but
git showon the committed branch showed the old content with no marker.Suggested fix
Option A (minimal & safe – recommended)
Change
.find()→.filter()inexecute-post-upgrade-commands.tsand update all matching entries:Option B – Deduplicate
updatedArtifactsearlier (before committing).Option C – Prevent duplicate appends at the
updateArtifacts()layer (cleanest long-term).We verified Option A with a patch on our self-hosted Docker instance — all post-upgrade changes now land correctly in the committed branch.
Links
.find()in execute-post-upgrade-commands only updates first duplicate updatedArtifact — stale content committed #42710Disclosure
This bug was discovered through the use of Agentic AI (GitHub Copilot + Claude) with a developer guiding the investigation on a real production .NET monorepo.
Logs (if relevant)
Logs
All reactions