Originally posted by fxwx23 June 30, 2026
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?
43.242.2
Please tell us more about your question or problem
Hi! First, thanks a lot for the recent work on Swift Package.resolved support (#41534) — it's been really helpful.
I ran into some behavior I wanted to check before assuming it's a bug. After a dependency update, Renovate seems to rewrite a dependency's pin in every Package.resolved in the repo, including separate packages where that library is only a transitive pin on a different (incompatible) version range. Because the pin is rewritten directly without running swift package resolve, the result can be a version that the other package doesn't actually allow. I wasn't sure whether this is intended, so I thought I'd ask here first.
What I'm seeing
A repo with two independent SwiftPM packages:
a/Package.swift depends on Yams directly (exact: "6.2.1") → a/Package.resolved pins yams 6.2.1.
b/Package.swift doesn't mention Yams at all; it depends on XcodeGen 2.45.4, which pulls Yams transitively with from: "5.0.0" (i.e. <6.0.0) → b/Package.resolved pins yams 5.0.1.
Renovate opens a PR to bump Yams in a to 6.2.2. The PR's dependency table lists only that single update, but the diff also changes b/Package.resolved: yams goes 5.0.1 → 6.2.2, which XcodeGen 2.45.4 forbids. Running swift package resolve in b puts it back to a 5.x version, so the value written there appears to be incorrect. I'd have expected b to be left untouched.
Where it seems to come from
In lib/modules/manager/swift/artifacts.ts, findPackageResolvedFiles() collects every Package.resolved in the repo, and pins are matched by URL only. The only guard is "skip if already at the target version" (pin.state.version === resolvedVersion), so there doesn't seem to be anything checking which package declared the dep, or whether the new version is allowed in that particular resolved file.
This looks like a different symptom from the other recent reports about this feature (#41780 for the v prefix, #42186 / #42188 for from: ranges) — those are about the same pin's value being malformed, whereas here a pin in an unrelated package is being changed.
A possible direction (for exact specs)
One idea that would cover the case above: for exact (isSingleVersion) specs, only rewrite a pin when its current version matches the version being upgraded from:
if (dep.currentValue && versioningApi.isSingleVersion(dep.currentValue)) {
if (pin.state.version !== dep.currentVersion) {
continue; // not the pin being upgraded from — leave it alone
}
}
In the example, a's pin (6.2.1) matches currentVersion and updates, while b's pin (5.0.1) doesn't and is skipped. A pin already at 6.2.1 elsewhere still updates, so this wouldn't regress the repo-wide sweep that Xcode workspaces seem to rely on (their Package.resolved lives under *.xcworkspace/... and isn't a manifest sibling, so scoping purely by path doesn't look viable — see #9735). It also needs no Swift toolchain, and the isSingleVersion gate is in the same spirit as the guard discussed in #42188.
The part I'm unsure about: range / from: specs
I don't think this idea extends cleanly to range specs, and I'd appreciate guidance. Since the swift manager never reads Package.resolved, config.lockedVersion is unset and currentVersion ends up derived from the manifest range + published releases (getCurrentVersion), not from the pin. So a plain equality check would skip legitimately-owned range pins (false negative), while a matches(pin, range) check would still over-match the same library's transitive pins in unrelated packages. Handling ranges correctly seems to need some association between a Package.resolved and the manifest(s) that own it (related to #9735), which feels bigger than this.
Is the cross-package rewrite considered expected behavior, or worth treating as a bug? If it's the latter, I'd be happy to open a PR with the exact-spec guard above plus tests (the transitive/unrelated-package case and the Xcode-workspace case), and leave the range case for discussion. Thanks!
Refs: #41534, #41780, #42186, #42188, #9735.
Logs (if relevant)
Logs
Replace this text with your logs, between the starting and ending triple backticks
Discussed in #44311
Originally posted by fxwx23 June 30, 2026
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?
43.242.2
Please tell us more about your question or problem
Hi! First, thanks a lot for the recent work on Swift
Package.resolvedsupport (#41534) — it's been really helpful.I ran into some behavior I wanted to check before assuming it's a bug. After a dependency update, Renovate seems to rewrite a dependency's pin in every
Package.resolvedin the repo, including separate packages where that library is only a transitive pin on a different (incompatible) version range. Because the pin is rewritten directly without runningswift package resolve, the result can be a version that the other package doesn't actually allow. I wasn't sure whether this is intended, so I thought I'd ask here first.What I'm seeing
A repo with two independent SwiftPM packages:
a/Package.swiftdepends on Yams directly (exact: "6.2.1") →a/Package.resolvedpins yams 6.2.1.b/Package.swiftdoesn't mention Yams at all; it depends on XcodeGen 2.45.4, which pulls Yams transitively withfrom: "5.0.0"(i.e.<6.0.0) →b/Package.resolvedpins yams 5.0.1.Renovate opens a PR to bump Yams in
ato 6.2.2. The PR's dependency table lists only that single update, but the diff also changesb/Package.resolved: yams goes5.0.1→6.2.2, which XcodeGen 2.45.4 forbids. Runningswift package resolveinbputs it back to a 5.x version, so the value written there appears to be incorrect. I'd have expectedbto be left untouched.Where it seems to come from
In
lib/modules/manager/swift/artifacts.ts,findPackageResolvedFiles()collects everyPackage.resolvedin the repo, and pins are matched by URL only. The only guard is "skip if already at the target version" (pin.state.version === resolvedVersion), so there doesn't seem to be anything checking which package declared the dep, or whether the new version is allowed in that particular resolved file.This looks like a different symptom from the other recent reports about this feature (#41780 for the
vprefix, #42186 / #42188 forfrom:ranges) — those are about the same pin's value being malformed, whereas here a pin in an unrelated package is being changed.A possible direction (for exact specs)
One idea that would cover the case above: for exact (
isSingleVersion) specs, only rewrite a pin when its current version matches the version being upgraded from:In the example,
a's pin (6.2.1) matchescurrentVersionand updates, whileb's pin (5.0.1) doesn't and is skipped. A pin already at 6.2.1 elsewhere still updates, so this wouldn't regress the repo-wide sweep that Xcode workspaces seem to rely on (theirPackage.resolvedlives under*.xcworkspace/...and isn't a manifest sibling, so scoping purely by path doesn't look viable — see #9735). It also needs no Swift toolchain, and theisSingleVersiongate is in the same spirit as the guard discussed in #42188.The part I'm unsure about: range /
from:specsI don't think this idea extends cleanly to range specs, and I'd appreciate guidance. Since the swift manager never reads
Package.resolved,config.lockedVersionis unset andcurrentVersionends up derived from the manifest range + published releases (getCurrentVersion), not from the pin. So a plain equality check would skip legitimately-owned range pins (false negative), while amatches(pin, range)check would still over-match the same library's transitive pins in unrelated packages. Handling ranges correctly seems to need some association between aPackage.resolvedand the manifest(s) that own it (related to #9735), which feels bigger than this.Is the cross-package rewrite considered expected behavior, or worth treating as a bug? If it's the latter, I'd be happy to open a PR with the exact-spec guard above plus tests (the transitive/unrelated-package case and the Xcode-workspace case), and leave the range case for discussion. Thanks!
Refs: #41534, #41780, #42186, #42188, #9735.
Logs (if relevant)
Logs