Skip to content

Commit 05e8cfd

Browse files
authored
Improve broken docs and CI (#174)
1 parent 838bb12 commit 05e8cfd

11 files changed

Lines changed: 210 additions & 33 deletions

File tree

.github/workflows/android.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ jobs:
1212
runs-on: macos-latest
1313
steps:
1414
- name: Check out code
15-
uses: actions/checkout@v5.0.0
15+
uses: actions/checkout@v6
1616
- name: Set up JDK
17-
uses: actions/setup-java@v5.0.0
17+
uses: actions/setup-java@v5
1818
with:
1919
distribution: 'zulu'
2020
java-version: 21
@@ -32,9 +32,9 @@ jobs:
3232
runs-on: macos-latest
3333
steps:
3434
- name: Check out code
35-
uses: actions/checkout@v5.0.0
35+
uses: actions/checkout@v6
3636
- name: Set up JDK
37-
uses: actions/setup-java@v5.0.0
37+
uses: actions/setup-java@v5
3838
with:
3939
distribution: 'zulu'
4040
java-version: 21
@@ -48,16 +48,16 @@ jobs:
4848
name: Build and Tests
4949
runs-on: macos-latest
5050
steps:
51-
- uses: actions/checkout@v5.0.0
51+
- uses: actions/checkout@v6
5252

5353
- name: set up JDK
54-
uses: actions/setup-java@v5.0.0
54+
uses: actions/setup-java@v5
5555
with:
5656
distribution: 'zulu'
5757
java-version: 21
5858

5959
- name: Cache Gradle and wrapper
60-
uses: actions/cache@v4
60+
uses: actions/cache@v5
6161
with:
6262
path: |
6363
~/.gradle/caches

.github/workflows/module-tests.yml

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- name: Checkout code
19-
uses: actions/checkout@v4
19+
uses: actions/checkout@v6
2020

2121
- name: Set up JDK 21
22-
uses: actions/setup-java@v4
22+
uses: actions/setup-java@v5
2323
with:
2424
distribution: 'zulu'
2525
java-version: 21
@@ -36,7 +36,7 @@ jobs:
3636

3737
- name: Upload test results
3838
if: failure()
39-
uses: actions/upload-artifact@v4
39+
uses: actions/upload-artifact@v7
4040
with:
4141
name: compiler-test-results
4242
path: stability-compiler/build/reports/tests/
@@ -46,10 +46,10 @@ jobs:
4646
runs-on: ubuntu-latest
4747
steps:
4848
- name: Checkout code
49-
uses: actions/checkout@v4
49+
uses: actions/checkout@v6
5050

5151
- name: Set up JDK 21
52-
uses: actions/setup-java@v4
52+
uses: actions/setup-java@v5
5353
with:
5454
distribution: 'zulu'
5555
java-version: 21
@@ -66,7 +66,7 @@ jobs:
6666

6767
- name: Upload test results
6868
if: failure()
69-
uses: actions/upload-artifact@v4
69+
uses: actions/upload-artifact@v7
7070
with:
7171
name: compiler-tests-results
7272
path: compiler-tests/build/reports/tests/
@@ -76,10 +76,10 @@ jobs:
7676
runs-on: ubuntu-latest
7777
steps:
7878
- name: Checkout code
79-
uses: actions/checkout@v4
79+
uses: actions/checkout@v6
8080

8181
- name: Set up JDK 21
82-
uses: actions/setup-java@v4
82+
uses: actions/setup-java@v5
8383
with:
8484
distribution: 'zulu'
8585
java-version: 21
@@ -96,7 +96,7 @@ jobs:
9696

9797
- name: Upload test results
9898
if: failure()
99-
uses: actions/upload-artifact@v4
99+
uses: actions/upload-artifact@v7
100100
with:
101101
name: runtime-test-results
102102
path: stability-runtime/build/reports/tests/
@@ -106,10 +106,10 @@ jobs:
106106
runs-on: ubuntu-latest
107107
steps:
108108
- name: Checkout code
109-
uses: actions/checkout@v4
109+
uses: actions/checkout@v6
110110

111111
- name: Set up JDK 21
112-
uses: actions/setup-java@v4
112+
uses: actions/setup-java@v5
113113
with:
114114
distribution: 'zulu'
115115
java-version: 21
@@ -126,7 +126,7 @@ jobs:
126126

127127
- name: Upload test results
128128
if: failure()
129-
uses: actions/upload-artifact@v4
129+
uses: actions/upload-artifact@v7
130130
with:
131131
name: gradle-plugin-test-results
132132
path: stability-gradle/build/reports/tests/
@@ -136,10 +136,10 @@ jobs:
136136
runs-on: ubuntu-latest
137137
steps:
138138
- name: Checkout code
139-
uses: actions/checkout@v4
139+
uses: actions/checkout@v6
140140

141141
- name: Set up JDK 21
142-
uses: actions/setup-java@v4
142+
uses: actions/setup-java@v5
143143
with:
144144
distribution: 'zulu'
145145
java-version: 21
@@ -161,7 +161,7 @@ jobs:
161161

162162
- name: Upload verification results
163163
if: always()
164-
uses: actions/upload-artifact@v4
164+
uses: actions/upload-artifact@v7
165165
with:
166166
name: plugin-verification-results
167167
path: compose-stability-analyzer-idea/build/reports/pluginVerifier/
@@ -171,10 +171,10 @@ jobs:
171171
runs-on: ubuntu-latest
172172
steps:
173173
- name: Checkout code
174-
uses: actions/checkout@v4
174+
uses: actions/checkout@v6
175175

176176
- name: Set up JDK 21
177-
uses: actions/setup-java@v4
177+
uses: actions/setup-java@v5
178178
with:
179179
distribution: 'zulu'
180180
java-version: 21
@@ -191,7 +191,7 @@ jobs:
191191

192192
- name: Upload test results
193193
if: failure()
194-
uses: actions/upload-artifact@v4
194+
uses: actions/upload-artifact@v7
195195
with:
196196
name: lint-test-results
197197
path: stability-lint/build/reports/tests/

.github/workflows/publish.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ jobs:
1111
runs-on: macos-latest
1212
steps:
1313
- name: Check out code
14-
uses: actions/checkout@v4.1.7
14+
uses: actions/checkout@v6
1515

16-
- name: Set up JDK 17
17-
uses: actions/setup-java@v4.2.2
16+
- name: Set up JDK 21
17+
uses: actions/setup-java@v5
1818
with:
1919
distribution: 'zulu'
20-
java-version: 17
20+
java-version: 21
2121

2222
- name: Grant Permission to Execute Gradle
2323
run: chmod +x gradlew

CONTRIBUTING.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Contributing to Compose Stability Analyzer
2+
3+
Thank you for your interest in contributing! This project welcomes bug reports, feature requests, documentation improvements, and pull requests.
4+
5+
## Reporting Issues
6+
7+
- Use the [issue templates](https://github.com/skydoves/compose-stability-analyzer/issues/new/choose) for bug reports and feature requests.
8+
- For IDE plugin freezes or performance problems, please attach the Android Studio thread dumps (**Help → Collect Logs and Diagnostic Data**) — they make issues diagnosable in one pass.
9+
- For Gradle plugin issues, include your Kotlin/AGP versions and the relevant `composeStabilityAnalyzer { ... }` configuration.
10+
11+
## Development Setup
12+
13+
The project requires **JDK 17+** and builds with the bundled Gradle wrapper. Kotlin and AGP versions are pinned in `gradle/libs.versions.toml`.
14+
15+
```bash
16+
# Build everything and publish to Maven Local (required before building the sample app)
17+
./gradlew spotlessApply publishToMavenLocal :app:assembleDebug -x test -PRELEASE_SIGNING_ENABLED=false
18+
```
19+
20+
The IntelliJ/Android Studio plugin is a separate IntelliJ Platform build:
21+
22+
```bash
23+
./gradlew :stability-runtime:publishToMavenLocal -x test -PRELEASE_SIGNING_ENABLED=false
24+
cd compose-stability-analyzer-idea && ../gradlew buildPlugin
25+
```
26+
27+
The sample app resolves the Gradle plugin from Maven Local (it shadows Maven Central for the same version), so always publish locally before building `:app`.
28+
29+
## Module Overview
30+
31+
| Module | What it is |
32+
|--------|-----------|
33+
| `stability-compiler` | K2 compiler plugin (FIR checks + IR instrumentation) |
34+
| `stability-runtime` | KMP runtime emitting recomposition logs |
35+
| `stability-gradle` | Gradle plugin (`composeStabilityAnalyzer {}` DSL, `stabilityDump`/`stabilityCheck`) |
36+
| `stability-lint` | Android Lint rules, packaged into the runtime AAR |
37+
| `compose-stability-analyzer-idea` | IntelliJ/Android Studio plugin (separate build) |
38+
| `compiler-tests` | Kotlin compiler test framework suite |
39+
| `app`, `app-model` | Sample app for end-to-end verification |
40+
41+
## Before Opening a Pull Request
42+
43+
1. **Format**: run `./gradlew spotlessApply` — CI fails on formatting violations, and Spotless also applies the license header to new files.
44+
2. **API dumps**: if you changed any public API in a published module, run `./gradlew apiDump` and commit the updated `api/*.api` files.
45+
3. **Tests**: run the suites relevant to your change:
46+
- `./gradlew :stability-runtime:jvmTest :stability-gradle:test :stability-compiler:test :compiler-tests:test`
47+
- IDE plugin: `cd compose-stability-analyzer-idea && ../gradlew test`
48+
4. **Compiler golden data**: if your change affects generated IR, regenerate snapshots with `./gradlew :compiler-tests:test -Pupdate.test.data` (the regenerating run reports failures while writing; the next run passes). Never hand-edit `.txt` snapshots or the generated JUnit classes under `compiler-tests/src/test/java/`.
49+
50+
## Things to Know Before Changing…
51+
52+
- **The logcat output format** (`stability-runtime` loggers): it is a wire protocol parsed by the IDE plugin's `LogcatParser`. Header lines may only gain *trailing* optional tokens; changing or reordering existing tokens breaks older IDE/runtime combinations.
53+
- **Stability inference** (`stability-compiler` / IDE plugin): the compiler's and the IDE's inference must stay in sync, including the known-stable type lists.
54+
- **AGP types in `stability-gradle`**: AGP is `compileOnly` and its types may only be referenced inside `AndroidStabilityTaskRegistrar`, so projects without AGP keep working.
55+
- **Versions**: `gradle.properties` `VERSION_NAME`, the `VERSION` constant in `StabilityAnalyzerGradlePlugin`, `gradle/libs.versions.toml`, and the IDE plugin's version/runtime dependency must move together.
56+
57+
## Code Reviews
58+
59+
All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) for more information on using pull requests.
60+
61+
## License
62+
63+
By contributing, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE).

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ This is incredibly useful for:
281281
282282
### Including in your project
283283

284-
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%compose-stability-runtime%22)
284+
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22compose-stability-runtime%22)
285285

286286
First, add the plugin to the `[plugins]` section of your `libs.versions.toml` file:
287287

docs/gradle-plugin/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ The Compose Stability Analyzer Gradle plugin enables runtime recomposition traci
66

77
## Installation
88

9-
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%compose-stability-runtime%22)
9+
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22compose-stability-runtime%22)
1010

1111
### Step 1: Add to Version Catalog
1212

docs/gradle-plugin/trace-all.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Trace-All Mode
2+
3+
By default, runtime recomposition data flows only for composables you annotate with [`@TraceRecomposition`](trace-recomposition.md) one by one. **Trace-all mode** instruments every restartable composable in the module automatically — as if each one carried the annotation — so the [Live Heatmap](../ide-plugin/recomposition-heatmap.md), [Reality Check](../ide-plugin/reality-check.md), and [Stability Doctor](../ide-plugin/stability-doctor.md) receive module-wide runtime data without manual annotations.
4+
5+
## Setup
6+
7+
```kotlin
8+
composeStabilityAnalyzer {
9+
traceAll {
10+
enabled.set(true) // default: false (opt-in)
11+
threshold.set(2) // default: 2
12+
variants.set(listOf("debug")) // default: ["debug"]
13+
}
14+
}
15+
```
16+
17+
## How It Behaves
18+
19+
- **Debug-oriented by default**: only compilations whose variant name equals or ends with one of the `variants` tokens are instrumented — `debug` matches `debug`, `stagingDebug`, and `fullDebug`, while release builds stay untouched. Test compilations are never instrumented. For KMP/JVM targets without variants, the runtime `ComposeStabilityAnalyzer.setEnabled(...)` gate is the production safety net.
20+
- **Explicit annotations win**: a composable with `@TraceRecomposition` keeps its own `tag`, `threshold`, and `traceStates` settings.
21+
- **Quiet by default**: auto-traced composables only start logging from their 2nd recomposition (`threshold = 2`), so the initial composition of a screen produces no logcat flood. A composable that never *re*-composes emits nothing — which is exactly the population that needs no attention. Raise the threshold if very active composables (e.g. animations) get noisy.
22+
- **Skips what shouldn't be traced**: `@Preview` composables, `@IgnoreStabilityReport`, inline/readonly/non-restartable composables, and property getters are excluded automatically.
23+
- **Cheap when disabled**: with `ComposeStabilityAnalyzer.setEnabled(false)`, the residual cost per composition is a map lookup plus early-returned calls.
24+
25+
## Fully Qualified Names in Logs
26+
27+
Trace-all-capable runtimes (0.10.0+) append two trailing tokens to every log header:
28+
29+
```
30+
D/Recomposition: [Recomposition #2] UserProfile (1.20ms) (fq: com.example.profile.UserProfile) (auto)
31+
```
32+
33+
- `(fq: ...)` — the fully qualified composable name, emitted for annotated and auto-traced composables alike. The IDE uses it to attribute runtime data precisely, so two composables that share a simple name across packages no longer share an inlay.
34+
- `(auto)` — marks events from trace-all instrumentation (absent for explicitly annotated composables).
35+
36+
Both tokens are **trailing and optional**, so older log parsers simply ignore them — old IDE + new runtime and new IDE + old runtime combinations keep working.
37+
38+
## Options
39+
40+
| Option | Default | Description |
41+
|--------|---------|-------------|
42+
| `enabled` | `false` | Opt-in master switch for auto-instrumentation. |
43+
| `threshold` | `2` | Recomposition count at which auto-traced composables start logging. |
44+
| `variants` | `["debug"]` | Android variant/build-type name tokens (case-insensitive equals/endsWith match) that receive instrumentation. |
45+
46+
!!! note "Logging still requires the runtime gate"
47+
48+
Trace-all only decides *what gets instrumented at compile time*. No logs appear unless you also call `ComposeStabilityAnalyzer.setEnabled(BuildConfig.DEBUG)` in your `Application` class, exactly as with `@TraceRecomposition`.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Stability Doctor
2+
3+
The Stability Doctor answers the question every other feature leads up to: **what should I fix first, and what will I gain?** It scans your project, scores every composable by combining the static stability verdict, the downstream [cascade](recomposition-cascade.md) blast radius, and the measured runtime waste from the [Reality Check](reality-check.md), then presents a ranked list of prescriptions with one-click fixes.
4+
5+
![doctor](https://github.com/skydoves/compose-stability-analyzer/raw/main/art/doctor.png)
6+
7+
## Scores: ESTIMATED vs. MEASURED
8+
9+
Each prescription carries a score (0–100) with one of two badges:
10+
11+
| Badge | Inputs | When |
12+
|-------|--------|------|
13+
| **ESTIMATED** | Unstable/unknown parameter count, skippability, cascade blast radius | Always available — no device needed. Capped below measured scores. |
14+
| **MEASURED** | Observed wasted recompositions × average duration, silent-waste grades, blast radius | When a [heatmap](recomposition-heatmap.md) session has collected enough observations. |
15+
16+
Two ranking behaviors follow from this design:
17+
18+
- A composable with **confirmed, measured waste always outranks** a speculative static finding — estimated scores are capped below the measured range.
19+
- A composable the compiler flags as unstable but that **skips fine at runtime sinks toward the bottom**, because the measurement proved the warning to be a false alarm.
20+
21+
## Prescriptions
22+
23+
Expanding a prescription shows its problem parameters, each with:
24+
25+
- The **static reason** (e.g. "Has 2 mutable (var) properties"), from in-IDE analysis.
26+
- The **Reality Check grade** (silent waste / false alarm / justified), when live data exists.
27+
- The **value provenance** at each call site (a `val`/`var` property, a parameter, a function call), reusing the [Blame](recomposition-blame.md) analysis.
28+
29+
## One-Click Fixes
30+
31+
Under each cause, the Doctor offers fixes you can apply by double-clicking:
32+
33+
| Fix | Applies to | Safety |
34+
|-----|-----------|--------|
35+
| **Change `var``val`** | The parameter's class, when it lives in your project | Searches for write usages first; refuses to apply if any assignment exists. |
36+
| **Annotate with `@Immutable` / `@Stable`** | Project classes without an existing stability annotation | The confirmation dialog reminds you this is a *promise* to the compiler, not a verification. `@Stable` is offered instead of `@Immutable` when the class keeps `var` properties. |
37+
| **Add to stability configuration file** | Library types you cannot modify | Skipped for platform/known-stable types and patterns already covered. |
38+
| **Wrap argument in `remember(keys) { ... }`** | Call-site arguments of *silent waste* parameters | Offered only when safety rules prove the transformation valid (see below). |
39+
40+
!!! note "Remember-hoisting safety rules"
41+
42+
The `remember` fix is the cure for an `equals`-equal value that arrives as a new instance on every recomposition. The Doctor offers it only when **all** of the following hold: the argument is evaluated directly in composition (not inside a lambda), it is a genuine computation (not a bare reference, constant, or lambda), it contains no composable calls, and every input resolves to a caller parameter or an earlier local `val`. The remember keys are derived from those inputs automatically, and a preview dialog shows the exact replacement before anything changes. Purity cannot be proven statically — verify the expression is side-effect free before confirming.
43+
44+
## How to Use
45+
46+
1. Open **View → Tool Windows → Compose Stability Analyzer → Doctor** tab (or **Code menu → Run Stability Doctor**) and hit refresh. This works immediately with `ESTIMATED` scores — no device required.
47+
2. For measured scores, enable [trace-all](../gradle-plugin/trace-all.md) (or annotate composables with `@TraceRecomposition`), start the [Recomposition Heatmap](recomposition-heatmap.md), and interact with your app. Rows upgrade to `MEASURED` and re-rank automatically while the session runs.
48+
3. Double-click a row to jump to the composable; double-click a fix to apply it. After a fix is applied, the affected prescription is re-analyzed.
49+
50+
## Configuration
51+
52+
**Settings → Tools → Compose Stability Analyzer → Stability Doctor** provides:
53+
54+
| Setting | Default | Description |
55+
|---------|---------|-------------|
56+
| Enable Stability Doctor | on | Master switch for the Doctor tab and auto-refresh. |
57+
| Max cascade candidates | 15 | How many top-scored composables get the (expensive) downstream blast-radius analysis. |
58+
| Auto-refresh interval | 10s | Refresh cadence while a heatmap session is running. |
59+
| Minimum score | 5 | Prescriptions scoring below this are hidden. |
60+
| Include test sources | off | Whether composables in test source roots are scanned. |
61+
62+
!!! tip "Same-named composables"
63+
64+
Runtime data is matched by fully qualified name when the runtime reports it (version 0.10.0+). If two composables share a simple name and the runtime data cannot be attributed precisely (older runtimes), the prescription stays `ESTIMATED` with a note rather than guessing.

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Open **Android Studio** > **Settings** > **Plugins** > **Marketplace** > search
4343

4444
### Gradle Plugin
4545

46-
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%compose-stability-runtime%22)
46+
[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/compose-stability-runtime.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22compose-stability-runtime%22)
4747

4848
Add the plugin to `libs.versions.toml`:
4949

0 commit comments

Comments
 (0)