chore(ci): close the gaps left by the Develocity onboarding - #6541
Conversation
Follow-up to #6531. Three things the onboarding left on the table: - build-logic is a plugin-included build with its own settings, so it does not inherit the root's Develocity config. It applies the shared build-cache script, but that script looks up the `develocity` extension, which is null there — so the whole remote block was skipped and the convention plugins compiled against the local cache only. The pre-#6531 HttpBuildCache config handled this explicitly (it had an isLogic branch); the port dropped it. Verified with `./gradlew help --info`: ':build-logic' now reports a remote Develocity cache alongside the local one. - Restore `capture { fileFingerprints }`, CI-only. Fingerprints are what let Develocity's build comparison explain a cache miss down to the changed input; not worth the scan payload on local builds. - Exclude caches/build-cache-1 from the Actions cache. It duplicates the Develocity remote cache, and was being tarballed on every one of the ~14 jobs behind the shared gradle-setup action. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe pull request adds Develocity build-scan configuration, enables CI-only file-fingerprint capture, and excludes the local Gradle build cache from GitHub Actions cache storage. ChangesDevelocity and Gradle caching
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
community.develocity.cloud is a public OSS instance and the README badge
links its scan list. Only IP addresses were obfuscated, so once a
contributor provisions an access key — which is what makes local builds
publish at all, via publishing.onlyIf { it.isAuthenticated } — every local
build would publish their OS username and machine hostname.
CI runners are left as-is: they are disposable and their identity is
already public in the Actions log, which is the more useful signal when
reading a CI scan.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Develocity settings plugin is applied in two settings files — the root
build and the build-logic included build — because a settings `plugins {}`
block cannot be sourced from an `apply(from = ...)` script. The version
literal is therefore unavoidably duplicated, and two copies of a version
string drift silently.
Adds a third drift guard alongside the existing check-changes filter and
test-shard guards, asserting both files declare the same version. Verified
both directions: passes on the current tree, and fails with a diagnostic
when the build-logic version is perturbed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Obfuscation verified against a real Build ScanThe PR body notes that the username/hostname obfuscation could not be verified locally, because nothing publishes without an access key. That's now resolved — I provisioned a key ( Scan: https://community.develocity.cloud/s/ydiyls2oxv2qo Queried back through the Develocity API ( {
"user": "local-dev",
"host": "local-machine",
"localHost": "local-machine",
"ip": null
}The real OS username and machine hostname do not appear in the published scan. Note that Develocity plugin version drift guardAdded in c26a44e, following the reviewer note about the duplicated Verified both directions: This does not remove the duplication — it can't be removed, since a settings 🤖 Generated with Claude Code |
Extends the workstation redaction to CI, so no machine identity is
published to the public OSS instance from either context.
This is a hedge rather than a fix for a live leak: every runner today is
GitHub-hosted, so the raw hostname is an ephemeral Azure VM name that
leaks nothing and correlates nothing, since VMs are never reused. But a
self-hosted runner's hostname would be real infrastructure, and whoever
adds one will not be thinking about build scans.
Values are constants rather than anything descriptive. The scan already
records operatingSystem ("Linux ... (amd64)") and numberOfCpuCores, and
common-custom-user-data already adds CI workflow/job/step/run values, so
an "informative" hostname would only duplicate them.
The `if` must stay outside the obfuscation lambdas: referencing isCI from
inside one captures the enclosing settings script object, which the
configuration cache cannot serialize. Caught by the baseline as "cannot
serialize Gradle script object references"; the constraint is now
commented so it does not get refactored back.
Verified against published scans in both modes — local: local-dev /
local-machine, CI=true: ci / ci-runner — with the configuration cache
storing cleanly in each.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CI obfuscation verified on a real GitHub Actions buildFollow-up to the earlier verification comment, which covered local builds only. Scan: https://community.develocity.cloud/s/3ugjngaluoeug {
"username": "ci",
"publicHostname": "ci-runner",
"localHostname": "ci-runner",
"operatingSystem": "Linux 6.17.0-1020-azure (amd64)",
"numberOfCpuCores": 4
}Two things worth noting from this:
Configuration cacheThe first attempt at this commit collapsed the branch into No problems reported. The constraint is commented in both settings files so the branch does not get refactored back inside the lambdas. 🤖 Generated with Claude Code |
`common-custom-user-data-gradle-plugin` publishes the raw output of
`git status --porcelain` as the `Git status` custom value, and it offers
no opt-out — `CaptureGitMetadataAction` runs unconditionally, and the
`obfuscation {}` block only covers username, hostname, IP addresses and
external process names.
That means every untracked, non-ignored path is visible on the public
community instance. `:desktopApp:test` leaves `hs_err_pid*.log` dumps
behind when the JVM crashes in libgobject, so three of them were riding
along in every local scan.
Ignoring them keeps the value scoped to real working-tree changes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up to #6531. Onboarding to the OSS Community Develocity instance landed the plumbing, but three things were left on the table: the
build-logicincluded build never actually got the remote cache, Build Scans lost the one capture setting that makes cache misses diagnosable, and CI now ships a local build cache that duplicates the remote one. This PR closes those gaps so the sponsorship is doing the work it can do.🛠️ Improvements
build-logicnow shares the remote build cache.build-logicis a plugin-included build with its ownsettings.gradle.kts, so it does not inherit the root build's Develocity configuration. It applies the sharedgradle/build-cache.settings.gradle, but that script looks upextensions.findByName("develocity")— which isnullthere — so the entireremote {}block was skipped and the convention plugins compiled against the local cache only. The pre-Onboard to the OSS Community Develocity Instance #6531HttpBuildCacheconfig handled this case explicitly (it had anisLogicbranch and its own log line); the Develocity port dropped it. Applying the Develocity settings plugin inbuild-logic/settings.gradle.ktsrestores parity.Verified with
./gradlew help --info:No additional Build Scan is published — the included build mirrors the root's
publishing.onlyIf { it.isAuthenticated }guard.Restore
capture { fileFingerprints }on CI. Onboard to the OSS Community Develocity Instance #6531 dropped the explicit setting in favour of the plugin default. File fingerprints are what let Develocity's build-comparison view explain a cache miss down to the individual changed input, which is the main reason to reach for a scan in the first place. Re-enabled on CI only (fileFingerprints = isCI), so local builds don't pay the scan payload for a tool that gets used when debugging CI.Stop shipping the local build cache in the Actions cache.
gradle-home-cache-includes: cachessweeps incaches/build-cache-1, which since Onboard to the OSS Community Develocity Instance #6531 duplicates the remote cache atcommunity.develocity.cloud. Every one of the ~14 jobs that call the sharedgradle-setupaction paid tarball upload/download for entries the remote already serves, against a 10 GB repo-wide Actions cache quota that evicts under pressure. Addedgradle-home-cache-excludes: caches/build-cache-1; dependency and artifact-transform caches — the expensive part — still ship.This one is a trade, not a strict win: it swaps one bulk tarball restore for per-entry HTTP against Develocity. The effect is now measurable in the Build Scan cache-performance view, and the comment in the action says to revert if remote latency costs more than the restore it replaced.
Obfuscate username and hostname in Build Scans, from both CI and workstations.
community.develocity.cloudis a public OSS instance and the README badge links its scan list, but only IP addresses were being obfuscated.The workstation case is the live one: provisioning a local access key is what makes local builds publish at all (
publishing.onlyIf { it.isAuthenticated }), so the moment a contributor runs./gradlew provisionDevelocityAccessKey, every local build would publish their OS username and machine hostname. Nowlocal-dev/local-machine.The CI case is a hedge, not a fix for a live leak. Every runner today is GitHub-hosted, so the raw hostname is an ephemeral Azure VM name that leaks nothing and correlates nothing (VMs are never reused). But a self-hosted runner's hostname would be real infrastructure, and whoever adds one will not be thinking about build scans. Now
ci/ci-runner.Values are deliberately constants rather than anything descriptive: the scan already records
operatingSystem("Linux 7.0.0-28-generic (amd64)"— including architecture) andnumberOfCpuCores, andcommon-custom-user-dataalready addsCI workflow/CI job/CI step/CI runvalues, so an "informative" hostname would only duplicate them.Reviewer note on structure: the
ifmust stay outside the obfuscation lambdas. ReferencingisCIfrom inside one captures the enclosing settings script object, which the configuration cache cannot serialize (cannot serialize Gradle script object references) — this repo runs withorg.gradle.configuration-cache=true. The first attempt at this collapsed the branch intousername { if (isCI) ... }and failed the baseline; the constraint is now commented in both files so it doesn't get refactored back.Applied in both settings files. In
build-logicit is only reachable via a standalone-p build-logicrun (when included, the root build publishes the scan), but the exposure would be identical, so the gap is closed there too.Gitignore JVM crash dumps so they stay out of public scans.
common-custom-user-datapublishes the raw output ofgit status --porcelainas theGit statuscustom value, and it offers no opt-out:CaptureGitMetadataActionruns unconditionally, and theobfuscation {}block used above covers only username, hostname, IP addresses and external process names — not custom values. So on a public instance every untracked, non-ignored path in the working tree is visible.The
:desktopApp:testcrash described at the bottom of this PR leaveshs_err_pid*.logdumps behind, and three of them were riding along in every local scan. Ignoring them — plusreplay_pid*.logand.attach_pid*, same family — scopes the value back to real working-tree changes; a fresh scan now reportsGit status: " M .gitignore"alone. Paths only, never file contents, but worth keeping honest.Notes for reviewers
configureTestOptions()wiresorg.gradle.test-retrywithmaxRetries=2,maxFailures=10,failOnPassedAfterRetry=falseacross the Android app/library and KMP library convention plugins. Develocity's within-build detection works with the standalone plugin as-is, and cross-build detection needs no client config at all — so the flaky data is already flowing to the Tests dashboard and no code change is warranted. Migrating to the built-indevelocity.testRetrywould be tidy but is not required, and would risk the screenshot-test opt-out inAndroidScreenshotConventionPlugin; left alone deliberately.failOnPassedAfterRetrystaysfalse. Develocity's guide suggeststrueto keep a flake as a gate failure, but recording flakes without breaking CI is the more useful posture now that the Tests dashboard actually surfaces them./api/builds/{id}/gradle-attributes: a local build reportslocal-dev/local-machine, aCI=truebuild reportsci/ci-runner, and the configuration cache stores cleanly in each. Initial local-only verification is in this comment.com.gradle.develocityversion literal is duplicated across the two settings files, and is now guarded by a CI drift check (third guard incheck-changes). It cannot be shared — a settings plugin must be applied in the settings file itself, so aplugins {}block cannot move into anapply(from = …)script. Folding the rest of thedevelocity { }body into the shared Groovygradle/build-cache.settings.gradlewas considered and rejected:publishing.onlyIf { it.isAuthenticated }does not port literally to Groovy (boolean getter →it.authenticated), and getting it wrong would silently disable the publish guard this PR just tightened. Not worth trading compile-time checking for ~12 lines on that particular block.setup-gradle'sadd-job-summary-as-pr-commentwas considered and skipped: with ~14 jobs behind the shared action, a broad failure would post ~14 comments. The existingadd-job-summary: alwaysalready carries the scan links.Testing Performed
./gradlew help --infobefore and after, confirming which builds resolve the remote cache (output quoted above)../gradlew spotlessApply spotlessCheck detekt assembleDebug test allTests -x :desktopApp:test --continue— BUILD SUCCESSFUL, no failing tasks, spotless produced no reformatting. Re-run after the obfuscation commit: still green.One task was excluded from the local baseline and needs CI to cover it.
:desktopApp:testaborts on my Linux workstation withexit value 134(SIGABRT) — a native SIGSEGV inlibgobject-2.0.so/g_object_unrefunder the JetBrains JRE, almost certainlyLinuxNotificationSenderTesttouching GLib/GTK on a host with a real desktop session. I confirmed it is pre-existing and unrelated to this change by stashing the diff and running:desktopApp:test --rerun-taskson the unmodified tree: identical crash, identical signature, and anhs_errlog from before this work began. CI covers the task in theshard-apptest shard, and that shard passed on this PR — so the gap is closed by CI, not left open. Flagging it because the full local baseline as documented inCLAUDE.mdis currently unrunnable on a Linux desktop session, which is worth fixing separately.🤖 Generated with Claude Code