Lock only runtimeClasspath so lockfiles stay reproducible - #4808
Merged
rajkumar-rangaraj merged 1 commit intoAug 1, 2026
Merged
Conversation
Dependency locking was activated for any configuration whose name ends in "runtimeclasspath", which also matched testRuntimeClasspath. But resolveAndLockAll only resolves runtimeClasspath, so test lock state was never written by our own regeneration task and every lockfile on main contains runtimeClasspath entries exclusively. Gradle 9.6.1 (microsoft#4780) started emitting a header in each lockfile pointing at "./gradlew :project:dependencies --write-locks". That task resolves every configuration in the project, so anything following the hint writes testRuntimeClasspath lock state that resolveAndLockAll cannot subsequently refresh or remove, since Gradle only rewrites state for configurations resolved in the current build. microsoft#4797 and microsoft#4798 each picked up ~640 lines of such entries across 19 lockfiles while changing no resolved dependency. Narrowing the predicate to an exact match makes both commands produce identical output. This is a no-op for current coverage: runtimeClasspath is the only configuration present in any checked-in lockfile.
xiang17
requested review from
harsimar,
mattsains-msft,
rajkumar-rangaraj,
ramthi and
trask
as code owners
July 31, 2026 23:36
rajkumar-rangaraj
approved these changes
Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4797 and #4798 are simple version bumps. However, there are a ton of line additions for packages that seems unrelated to the version bump. They all have
testRuntimeClasspath.This PR is to prevent all those unrelated changes in those two PRs.
Note: merge this PR in main first, then reset those PRs by merging main into them.
Agent description
Problem
Dependency locking is activated for any configuration whose name ends in
runtimeclasspath, which also matchestestRuntimeClasspath. OurresolveAndLockAlltask only resolvesruntimeClasspath, so test lock state wasnever actually written — every lockfile on
maincontainsruntimeClasspathentries and nothing else.
That inconsistency was dormant while
resolveAndLockAllwas the only command inuse. Gradle 9.6.0 added a header to every lockfile (gradle/gradle#37735, a
usability improvement) advertising a different regeneration command:
The
dependenciesreport task resolves every configuration in the project inorder to print them, so following that hint writes
testRuntimeClasspathlockstate for the first time. #4797 and #4798 each carry ~640 added lines across 19
lockfiles despite changing no resolved dependency — the semconv bump in #4797
doesn't appear on any locked runtime classpath at all.
This is not a Gradle regression. Locking semantics are unchanged, and
dependencies --write-locksbehaved identically before 9.6; the upgrade onlypublished a command whose scope differs from ours. The wrapper bump (#4780)
changed lockfiles by exactly one line each: the new header. The bug is the
mismatch in our own predicate, so pinning Gradle back would only re-hide it.
The entries are also self-sustaining: Gradle only rewrites lock state for
configurations resolved in the current build, so
resolveAndLockAllleaves themuntouched. Running it against #4797's state changes 0 files. Once merged they
become permanent state that our documented regeneration command cannot refresh,
and the next JUnit or slf4j bump would fail lock validation with no supported way
to fix it.
Fix
Match
runtimeClasspathexactly, so both commands produce identical output.Verification
./gradlew :agent:agent-bootstrap:dependencies --write-locks(the command thatcaused the churn) now leaves the lockfile unchanged; before this change it
rewrote the file with test entries.
./gradlew resolveAndLockAll --write-locksacross the repo produces zero driftfrom
main.runtimeClasspathis the only configuration value presentin any checked-in lockfile.
Follow-up
After this merges, comment
@dependabot rebaseon #4797 and #4798; they shouldcome back as clean version bumps with no lockfile churn. #4800 and #4801 are open
Gradle PRs that predate the Gradle 9.6 upgrade and would pick up the same churn
once rebased.