Skip to content

Store stability output of Android variants to their own folder#150

Open
matejdro wants to merge 2 commits into
skydoves:mainfrom
matejdro:android-variants-separate-folder
Open

Store stability output of Android variants to their own folder#150
matejdro wants to merge 2 commits into
skydoves:mainfrom
matejdro:android-variants-separate-folder

Conversation

@matejdro

@matejdro matejdro commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

🎯 Goal

A continuation of the #101

Up until now, every Kotlin task dropped its stability output into build/stability. This was problematic with multi-variant runs:

  1. If debug and release source sets are different, stability output might be different, but they would overwrite each other
  2. If debug and release compile was ran in parallel, two tasks might write to this file simultaneously, corrupting it

PR fixes this by changing plugin to store stability output into build/stability/$VARIANT (such as /build/stability/debug)

Summary by CodeRabbit

  • Refactor
    • Improved organization of stability analysis output directories for Kotlin tasks, with clearer folder naming conventions and better variant-scoped structure for stability analysis data.

@matejdro matejdro requested a review from skydoves as a code owner April 13, 2026 12:35
@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

StabilityAnalyzerGradlePlugin refactors stability output directory naming using a new getKotlinTaskStabilityFolderName() helper that derives per-task folder names by stripping Kotlin task name prefixes/suffixes. Gradle task configuration, compiler plugin wiring, and Android variant tasks now use this helper and variant-scoped paths instead of fixed folder patterns.

Changes

Per-Task Stability Directory Naming

Layer / File(s) Summary
Helper Function
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt
New getKotlinTaskStabilityFolderName(taskName) derives stability folder names by removing compile prefix and Kotlin suffix, lowercasing the remainder, and returning "stability" or "stability/<subfolder>".
Task Output Configuration
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt
KotlinCompile task stabilityDir is now derived per-task using the new helper instead of the prior "stability/$name" pattern.
Compiler Plugin Wiring
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt
Compiler plugin OPTION_STABILITY_OUTPUT_DIR configuration uses the helper function to compute per-compilation output directories.
Android Variant Paths
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt
Android dump and check tasks read/write stability-info from stability/<variantLowerCase>/stability-info.json instead of compile-task-specific paths.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

Stability Validation

Suggested reviewers

  • skydoves

Poem

🐰 A helper hops through task names with glee,
Stripping compile and Kotlin to see,
Per-variant paths now bright and clean,
The finest folder structure ever seen! 🌱

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers the Goal section well with context and motivation, but lacks Implementation details, code examples, and instructions for formatting/API dump verification as specified in the template. Add Implementation details section explaining the technical approach, provide code examples showing the folder structure changes, and include the spotlessApply and apiDump command verification steps.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: moving stability output to variant-specific folders instead of a shared location.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt`:
- Around line 291-295: The IDE tool window still only reads the flat path
"build/stability/stability-info.json" so after StabilityAnalyzerGradlePlugin.kt
started writing variant outputs into "stability/<variant>/stability-info.json"
the collector stops seeing Android results; update ComposableStabilityCollector
(class/method that scans build/stability) to either (a) enumerate the
build/stability directory and read stability-info.json from every subdirectory
(e.g., stability/<variant>/stability-info.json) or (b) fall back to the legacy
root file if present—locate the scan logic around the method that currently
constructs the path "build/stability/stability-info.json" and change it to walk
child directories or attempt both locations so both legacy and variant outputs
are discovered.
- Around line 129-135: The KotlinCompile task filters currently hardcode false
in the calls to isKotlinTaskApplicable, causing test compile tasks to be treated
as non-writing when stabilityValidation.includeTests is enabled; change those
calls so they pass the actual includeTests flag (the same boolean used for
stabilityValidation) instead of false in the
target.tasks.withType(KotlinCompile::class.java).named {
isKotlinTaskApplicable(it, false) } blocks (both the block that sets
outputs.dir(stabilityDir).optional(true) and the similar block at the later
location), so isKotlinTaskApplicable receives the real includeTests value and
test compile tasks correctly declare their build/stability outputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c30514d1-dc51-40cc-8ad8-c5204ecc728a

📥 Commits

Reviewing files that changed from the base of the PR and between 87fcf75 and 8611989.

📒 Files selected for processing (1)
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

Comment on lines +291 to +295
val stabilityFolderName = if (variant.isBlank()) {
"stability"
} else {
"stability/$variant"
}

@coderabbitai coderabbitai Bot Apr 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Update the IDEA collector before moving Android outputs.

compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/toolwindow/ComposableStabilityCollector.kt lines 60-65 still only scan build/stability/stability-info.json. After this change, Android builds emit build/stability/<variant>/stability-info.json, so the tool window will stop discovering fresh Android results unless it also walks the variant subdirectories or you keep a compatibility file in the root folder.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt`
around lines 291 - 295, The IDE tool window still only reads the flat path
"build/stability/stability-info.json" so after StabilityAnalyzerGradlePlugin.kt
started writing variant outputs into "stability/<variant>/stability-info.json"
the collector stops seeing Android results; update ComposableStabilityCollector
(class/method that scans build/stability) to either (a) enumerate the
build/stability directory and read stability-info.json from every subdirectory
(e.g., stability/<variant>/stability-info.json) or (b) fall back to the legacy
root file if present—locate the scan logic around the method that currently
constructs the path "build/stability/stability-info.json" and change it to walk
child directories or attempt both locations so both legacy and variant outputs
are discovered.

✅ Addressed in commit b61d18a

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.

Hm that's a good point, it would break IDEA plugin. @skydoves how should we handle this? Would we add a variant property to the idea plugin?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

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.

What I think we could do is, have Intellij plugin scan the stability folder + all the subfolders for the stability-info.json. That way it detects the json file regardless of the variant.

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.

I have added check-all-folders, however it is untested. I'm not sure how can I build the IntelliJ plugin, it does not appear to be documented. Or did I miss it?

@matejdro matejdro force-pushed the android-variants-separate-folder branch from 8611989 to b61d18a Compare April 13, 2026 12:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/toolwindow/ComposableStabilityCollector.kt`:
- Around line 125-126: In ComposableStabilityCollector.kt, the catch (e:
Exception) that swallows parse failures for stability-info.json should log the
failing folder path and exception instead of ignoring it; update the catch to
call the module/class logger (or Logger.getInstance(...).error) including the
folder or file path being parsed and e (stacktrace/message) so users can
distinguish "no output" from "failed to read/generated" when stability-info.json
parsing fails.
- Around line 63-64: The collector currently flattens variantFolders +
rootStabilityFolder into allStabilityFolders and loses variant identity, causing
duplicate composables to be indistinguishable and StabilityStats to overcount;
update ComposableStabilityCollector to either (A) carry variant metadata with
each folder entry (e.g., change the collection element to a data class or pair
like (variantName, folder) and propagate that through the map/reduce that builds
StabilityStats and the UI model) or (B) deduplicate exact composable entries
before computing the summary (compare by canonical path/signature and collapse
identical entries) and apply the same change to the other aggregation sites
around the blocks that build the stats and UI model so counts reflect unique
composables per variant or globally as intended.
- Around line 61-64: The loop currently builds allStabilityFolders from
rootStabilityFolder ("build/stability") only, so non-Android test outputs
written to "build/stabilityTest/stability-info.json" are missed; update the
collector to also consider "build/stabilityTest" (e.g., create a
rootStabilityTest = File(contentRoot.path, "build/stabilityTest") and include
its directory plus its variant children into allStabilityFolders), and only
add/scan that folder when the includeTests flag/property is enabled; keep
references to rootStabilityFolder, rootStabilityTest, variantFolders,
allStabilityFolders, and the existing folder loop so the new folder is treated
the same as the original.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2b3523a1-aec5-4bfc-aa92-01021269f4ba

📥 Commits

Reviewing files that changed from the base of the PR and between b61d18a and 5541142.

📒 Files selected for processing (2)
  • compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/toolwindow/ComposableStabilityCollector.kt
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

Comment on lines +63 to +64
val allStabilityFolders = variantFolders + rootStabilityFolder
for (folder in allStabilityFolders) {

@coderabbitai coderabbitai Bot Apr 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve variant identity or dedupe before building stats.

The collector now merges debug, release, *UnitTest, etc. into one flat list but throws away the folder/variant name. Shared composables will show up as indistinguishable duplicates, and StabilityStats will overcount them. Please either carry the variant through the model/UI or collapse exact duplicates before computing the summary.

Also applies to: 110-123, 133-136

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@compose-stability-analyzer-idea/src/main/kotlin/com/skydoves/compose/stability/idea/toolwindow/ComposableStabilityCollector.kt`
around lines 63 - 64, The collector currently flattens variantFolders +
rootStabilityFolder into allStabilityFolders and loses variant identity, causing
duplicate composables to be indistinguishable and StabilityStats to overcount;
update ComposableStabilityCollector to either (A) carry variant metadata with
each folder entry (e.g., change the collection element to a data class or pair
like (variantName, folder) and propagate that through the map/reduce that builds
StabilityStats and the UI model) or (B) deduplicate exact composable entries
before computing the summary (compare by canonical path/signature and collapse
identical entries) and apply the same change to the other aggregation sites
around the blocks that build the stats and UI model so counts reflect unique
composables per variant or globally as intended.

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.

That is a good point, but I feel extra UI is out of scope for this PR. What do you think @skydoves?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt`:
- Around line 87-92: The code uses stabilityInputFiles.setFrom(...) twice which
causes the second call to overwrite the first; change the second invocation to
stabilityInputFiles.from(target.layout.buildDirectory.file("stability/test/stability-info.json"))
so the test file is appended instead of replacing the initial
stability/stability-info.json entry, ensuring stabilityInputFiles contains both
files.
- Around line 175-187: The stabilityInputFiles property is being overwritten by
repeated setFrom() calls so only the last file (AndroidTest) is kept; update the
code that sets stabilityInputFiles (references: stabilityInputFiles and
variant.name in StabilityAnalyzerGradlePlugin.kt) to aggregate all three files
instead of replacing them—use a single setFrom(...) with a collection of files
or call from(...) / addAll(...) for each file (e.g., gather the three
target.layout.buildDirectory.file(...) results into a list and pass that to
stabilityInputFiles.setFrom or call stabilityInputFiles.from(...) for each) so
main variant, UnitTest and AndroidTest files are all included.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9d43cb7f-227f-42bc-bdd4-4819296aee53

📥 Commits

Reviewing files that changed from the base of the PR and between 5541142 and 56b51f0.

📒 Files selected for processing (1)
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

@matejdro

Copy link
Copy Markdown
Contributor Author

@skydoves any inputs here?

@matejdro

Copy link
Copy Markdown
Contributor Author

Hm, it looks like this might be superseded by the #154?

@skydoves

skydoves commented May 3, 2026

Copy link
Copy Markdown
Owner

Hi @matejdro, thanks for the PR! I worked on a broader fix for this as part of #153, which also covers implicit dependency failures with Kover/Gradle 9.x, mustRunAfter cleanup, and IDE backward compatibility.

But I really like your variant-based naming (build/stability/debug vs build/stability/compileDebugKotlin) much cleaner. Would you like to rebase this PR on top of the #153 fix and focusing on just the variant naming improvement? That way we can incorporate your approach without duplicating the rest.

One quick note: the setFrom() calls for Android variants overwrite each other (only the last path survives) - should be from() for additive behavior.

@matejdro matejdro force-pushed the android-variants-separate-folder branch from 56b51f0 to bfc2f27 Compare May 8, 2026 06:24
@matejdro

matejdro commented May 8, 2026

Copy link
Copy Markdown
Contributor Author

Updated.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt (1)

107-109: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Non-Android input glob misses the root stability-info.json.

At Line 108 and Line 126, include("*/stability-info.json") won’t match build/stability/stability-info.json (the compileKotlin output folder). That can leave dump/check with no main input on JVM/non-Android projects.

Suggested fix
       stabilityInputFiles.setFrom(
         target.fileTree(target.layout.buildDirectory.dir("stability")) {
-          include("*/stability-info.json")
+          include("**/stability-info.json")
         },
       )
@@
       stabilityInputFiles.from(
         target.fileTree(target.layout.buildDirectory.dir("stability")) {
-          include("*/stability-info.json")
+          include("**/stability-info.json")
         },
       )

Also applies to: 125-127

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt`
around lines 107 - 109, The include glob currently used in
StabilityAnalyzerGradlePlugin (the target.fileTree {
include("*/stability-info.json") } calls) misses files directly under
build/stability (e.g., stability-info.json) for non-Android projects; update
those include calls to match both root and nested files — either use
include("**/stability-info.json") or list both include("stability-info.json",
"*/stability-info.json") — and apply the same change to the second occurrence of
target.fileTree/include in the file so the dump/check tasks always pick up
build/stability/stability-info.json on JVM projects.
♻️ Duplicate comments (1)
stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt (1)

177-180: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Android dump/check currently ignore test stability files when includeTests=true.

At Line 179 and Line 198, inputs are fixed to stability/$variantNameLowerCase/stability-info.json only. But test compilations (enabled via includeTests) write to variant test folders, so those results are dropped from dump/check inputs.

Suggested fix
       val stabilityDumpTask = target.tasks.register(
         "${variantNameLowerCase}StabilityDump",
         StabilityDumpTask::class.java,
       ) {
         projectName.set(target.name)
-        stabilityInputFiles.setFrom(
-          target.layout.buildDirectory.file(
-            "stability/$variantNameLowerCase/stability-info.json",
-          ),
-        )
+        stabilityInputFiles.setFrom(
+          target.fileTree(target.layout.buildDirectory.dir("stability")) {
+            include("$variantNameLowerCase/stability-info.json")
+            include("${variantNameLowerCase}UnitTest/stability-info.json")
+            include("${variantNameLowerCase}AndroidTest/stability-info.json")
+          },
+        )
@@
       val stabilityCheckTask = target.tasks.register(
         "${variantNameLowerCase}StabilityCheck",
         StabilityCheckTask::class.java,
       ) {
         projectName.set(target.name)
-        stabilityInputFiles.from(
-          target.layout.buildDirectory.file(
-            "stability/$variantNameLowerCase/stability-info.json",
-          ),
-        )
+        stabilityInputFiles.from(
+          target.fileTree(target.layout.buildDirectory.dir("stability")) {
+            include("$variantNameLowerCase/stability-info.json")
+            include("${variantNameLowerCase}UnitTest/stability-info.json")
+            include("${variantNameLowerCase}AndroidTest/stability-info.json")
+          },
+        )

Also applies to: 196-199

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt`:
- Around line 107-109: The include glob currently used in
StabilityAnalyzerGradlePlugin (the target.fileTree {
include("*/stability-info.json") } calls) misses files directly under
build/stability (e.g., stability-info.json) for non-Android projects; update
those include calls to match both root and nested files — either use
include("**/stability-info.json") or list both include("stability-info.json",
"*/stability-info.json") — and apply the same change to the second occurrence of
target.fileTree/include in the file so the dump/check tasks always pick up
build/stability/stability-info.json on JVM projects.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a651b24e-c60b-4041-92ff-9f6740a1d8f6

📥 Commits

Reviewing files that changed from the base of the PR and between 56b51f0 and bfc2f27.

📒 Files selected for processing (1)
  • stability-gradle/src/main/kotlin/com/skydoves/compose/stability/gradle/StabilityAnalyzerGradlePlugin.kt

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