Skip to content

[Crashlytics] Fix release build cache invalidation#8185

Open
jrodiz wants to merge 7 commits into
firebase:mainfrom
jrodiz:feature/jrc--6770.Fix.release.build.cache.invalidation
Open

[Crashlytics] Fix release build cache invalidation#8185
jrodiz wants to merge 7 commits into
firebase:mainfrom
jrodiz:feature/jrc--6770.Fix.release.build.cache.invalidation

Conversation

@jrodiz
Copy link
Copy Markdown
Contributor

@jrodiz jrodiz commented May 20, 2026

Crashlytics: content-driven mapping file id for release builds

Fixes #6770

injectCrashlyticsMappingFileIdRelease minted a new UUID on every release build, cascading through mergeResourcesprocessResources → R8 → packaging and breaking the build cache for every minified release.

This PR makes the task content-driven: Gradle's input snapshotting decides when to re-execute. While the variant's user sources are unchanged, the task stays UP-TO-DATE and the entire downstream cascade stays cached. When sources change — which is when R8 would produce a different mapping.txt — the task re-runs and a fresh id is minted.

How

  • useBlankMappingFileId promoted from @Internal to @Input (mode flip natively invalidates).
  • New @InputFiles obfuscatableSources populated from project.fileTree("src") with java/** + kotlin/** patterns (tests excluded). Path-based, no task deps — sidesteps the cycle that variant.sources.*.all would close through AGP's resource graph.
  • Custom upToDateWhen and on-disk reuse path removed.

Verification

  • :spotlessApply, :validatePlugins, :test, :functionalTest — green (19/19).
  • End-to-end manual repro: github.com/jrodiz/repro6770 (AGP 9.1.0-alpha05, Gradle 9.2.1, compileSdk 36, real Firebase project). Pre-fix plugin 3.0.3 flips mappingFileId.txt every rebuild and re-runs the full cascade. With this branch (mavenLocal()), the id stays byte-identical across rebuilds, every downstream task is UP-TO-DATE, second assembleRelease finishes in ~1s. Editing a Kotlin source mints a new id; clean and mappingFileUploadEnabled toggle behave as expected.

Verification from manual test plan above

  • End-to-end manual verification in a real Android app: github.com/jrodiz/repro6770 (AGP 9.1.0-alpha05, Gradle 9.2.1, compileSdk 36, isMinifyEnabled = true, real Firebase project).
    • Reproduced the bug against the published pre-fix plugin com.google.firebase.crashlytics:3.0.3mappingFileId.txt flipped every rebuild (f5f0310…6514b43…), and :app:processReleaseResources, :app:uploadCrashlyticsMappingFileRelease, :app:packageRelease, :app:assembleRelease all re-executed with no source changes.
    • Verified the fix by publishing this branch to mavenLocal() as 3.0.7 and bumping the app's plugin version — mappingFileId.txt stays byte-identical across rebuilds, :app:injectCrashlyticsMappingFileIdRelease reports UP-TO-DATE, and the full downstream cascade (:mergeReleaseResources, :processReleaseResources, :minifyReleaseWithR8, :packageRelease, :assembleRelease) is UP-TO-DATE. Second assembleRelease finishes in ~1s.
    • Verified clean still mints a fresh id, and toggling mappingFileUploadEnabled = false re-runs the task and writes the blank id 0…0.
    • Full transcript with task outcomes and id values in TEST_RESULTS_ISSUE_6770.md.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

@jrodiz jrodiz requested a review from mrober May 20, 2026 00:56
jrodiz added 4 commits May 19, 2026 19:16
Reuse the on-disk id when present and valid for the current mode; only
generate a new UUID on first build, after clean, or when the mode flips.
This lets the task report UP_TO_DATE and stops invalidating downstream
release tasks (mergeResources, R8, packaging, bundling) on every build.

Fixes firebase#6770
Covers: UP_TO_DATE on second run, resource preserved across rebuilds,
re-run after clean, mode-toggle invalidates the task.
Wrap the existingMappingFileId() KDoc to satisfy ktfmt.
@jrodiz jrodiz force-pushed the feature/jrc--6770.Fix.release.build.cache.invalidation branch from b02cde8 to 7e1c779 Compare May 20, 2026 01:16
jrodiz added 3 commits May 26, 2026 20:54
Source-file fingerprint drives UP_TO_DATE; id regenerates on edits.
Adds source-invalidation test; rewires firebase#6770 tests to buildGradleRunner.
.withProjectDir(projectDir)
.withArguments(":injectCrashlyticsMappingFileIdRelease", "--configuration-cache")
.build()
buildGradleRunner(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OOS93 Applied your suggestion

if (!useBlank) {
this.obfuscatableSources.from(
project.fileTree("src").matching { patterns ->
patterns.include(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tried something like this before, it looked more like this.sourceFiles.from(variant.sources.java?.all, variant.sources.kotlin?.all)

But this created circular dependencies when apps used things like compose. If source depends on resources, and resources depend on sources, we have a circular dependency. Can you confirm if this has the same problem?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran both implementations against the real repro6770 app

App configuration variant.sources.java?.all + kotlin?.all Current fileTree("src")
Compose only builds, no cycle builds, no cycle
Compose + viewBinding Circular dependency — BUILD FAILED builds; inject SUCCESS → UP-TO-DATE; id stable

Compose alone is not a trigger, the trigger is view/data binding. So the circular dependency will only be present if I use variant.sources.java?.all + kotlin?.all + view/data binding.

The project.fileTree("src") form builds cleanly on the same app (task UP-TO-DATE on rebuild, stable id) because it's a plain FileTree: no builtBy/task-dependency edges for Gradle to order against, and it only scans src/**. Never build/generated/** where the binding-generated sources live.

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.

injectCrashlyticsMappingFileIdRelease causes every release build to be invalidated

3 participants