Skip to content

Lock only runtimeClasspath so lockfiles stay reproducible - #4808

Merged
rajkumar-rangaraj merged 1 commit into
microsoft:mainfrom
xiang17:xiang17/fix/lock-only-runtime-classpath
Aug 1, 2026
Merged

Lock only runtimeClasspath so lockfiles stay reproducible#4808
rajkumar-rangaraj merged 1 commit into
microsoft:mainfrom
xiang17:xiang17/fix/lock-only-runtime-classpath

Conversation

@xiang17

@xiang17 xiang17 commented Jul 31, 2026

Copy link
Copy Markdown
Member

#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 matches testRuntimeClasspath. Our
resolveAndLockAll task only resolves runtimeClasspath, so test lock state was
never actually written — every lockfile on main contains runtimeClasspath
entries and nothing else.

That inconsistency was dormant while resolveAndLockAll was the only command in
use. Gradle 9.6.0 added a header to every lockfile (gradle/gradle#37735, a
usability improvement) advertising a different regeneration command:

# To regenerate this file, run: ./gradlew :project:dependencies --write-locks

The dependencies report task resolves every configuration in the project in
order to print them, so following that hint writes testRuntimeClasspath lock
state 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-locks behaved identically before 9.6; the upgrade only
published 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 resolveAndLockAll leaves them
untouched. 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 runtimeClasspath exactly, so both commands produce identical output.

Verification

  • ./gradlew :agent:agent-bootstrap:dependencies --write-locks (the command that
    caused the churn) now leaves the lockfile unchanged; before this change it
    rewrote the file with test entries.
  • ./gradlew resolveAndLockAll --write-locks across the repo produces zero drift
    from main.
  • No coverage is lost: runtimeClasspath is the only configuration value present
    in any checked-in lockfile.

Follow-up

After this merges, comment @dependabot rebase on #4797 and #4798; they should
come 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.

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.
@rajkumar-rangaraj
rajkumar-rangaraj merged commit b5552e3 into microsoft:main Aug 1, 2026
146 checks passed
@xiang17
xiang17 deleted the xiang17/fix/lock-only-runtime-classpath branch August 1, 2026 03:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants