chore(usersrole): hash the JDK and stop caching Gradle state - #160
Merged
Conversation
sourceCompatibility/targetCompatibility only assert the language level; Gradle still compiles with whatever JVM launched it. CI pins temurin 21, a workstation may be on 23 or 25, and both produce the same Nx hash from the same sources -- across machines that is a wrong artifact, not a cache miss. Verified: with a JDK 25 launcher the emitted class files are major version 65 (Java 21), not 69.
outputs was the whole build directory, which carries machine-specific state: Gradle's previous-compilation-data.bin incremental cache, test reports, and a build-info.properties stamped with build.time. Restoring another machine's incremental state is a known way to confuse Gradle's up-to-date checks. The command also ran the full build lifecycle, so every cached run executed the Testcontainers suite -- a Docker-dependent, side-effecting task cached as if it were pure. Gradle's test task is already inferred by the plugin and already runs in the pipeline's test step, so assemble drops duplicate work rather than coverage.
jdwillmsen
force-pushed
the
fix/usersrole-build-cache
branch
from
August 2, 2026 05:58
2615c70 to
05c9aae
Compare
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.
Closes JDWLABS-282's sibling, JDWLABS-280.
What was wrong
usersrole:buildhadinputsunset, so it inherited["default", "^default"]. That is why the JDWLABS-276 guard never flagged it — the guard checks that declared inputs are dependency-aware, and cannot see inputs that are absent from the hash entirely.The JDK was not in the hash.
build.gradle.ktssetsourceCompatibility/targetCompatibility = 21but declared notoolchain, so Gradle compiled with whatever JVM launched it. CI pins temurin 21; a workstation may be on 23 or 25. Same sources, same Nx hash, different bytecode — across machines that is a wrong artifact, not a cache miss.The outputs were the whole
build/directory, which carries machine-specific state:That last file was also stamped
build.version=1.0.2whileVERSIONreads1.0.4— stale output sitting inside a cached directory.A Docker-dependent task was cached as pure.
gradlew buildrunscheck→test, which starts real Postgres containers via Testcontainers.What changed
toolchain { languageVersion = JavaLanguageVersion.of(21) }— makes the build deterministic rather than merely making Nx notice the difference. Preferred over an Nx-sideruntimeinput for that reason.outputs→["{projectRoot}/build/libs"]— the actual artifact, nothing else.gradlew build→gradlew assemble— skips the test lifecycle.Why dropping
testfrombuildis not a coverage loss@nx/gradlealready infers atesttarget for this project:and CI runs
nx affected -t lint testbeforenx affected -t build. So the Testcontainers suite was running twice per pipeline.assembleremoves the duplicate, not the coverage.Verification
Toolchain, proven not argued — built with a JDK 25 launcher, then read the class-file major version out of the produced jar:
Cache correctness:
Restore contains only the artifact — deleted
build/entirely, then re-ran:Notes
chorerather thanfixon purpose. Afixwould trigger a usersrole release, and JDWLABS-275 has releases stalling unmerged right now — this is a build-config change with no runtime effect (CI already compiled on 21), so it should not push a release into a known-broken delivery path.One claim on the ticket no longer holds:
bootJar's inferred output now readsusersrole-1.0.4.jar, matchingVERSION, not the1.0.1recorded there. The host-local Gradle model snapshot has since refreshed. The underlying fragility is unchanged — those paths are still Windows-shaped ({projectRoot}\build\libs\…) and drift per machine — but the stale-filename defect is not currently present, so nothing was changed for it.Gradle dependency resolution is still unpinned (Spring Boot BOM versions resolve from Maven Central at build time), so identical hashes can still resolve different jars. Fixing that needs Gradle dependency locking and is deliberately not bundled here.