https://github.com/fornewid/proguard-shield
A Gradle plugin that detects unintentional changes to Android's merged ProGuard/R8 rules
https://github.com/fornewid/proguard-shield
android gradle-plugin proguard proguard-shield r8
Last synced: about 2 months ago
JSON representation
A Gradle plugin that detects unintentional changes to Android's merged ProGuard/R8 rules
- Host: GitHub
- URL: https://github.com/fornewid/proguard-shield
- Owner: fornewid
- License: apache-2.0
- Created: 2026-04-16T16:33:55.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-05-28T14:39:26.000Z (about 2 months ago)
- Last Synced: 2026-05-28T16:21:39.067Z (about 2 months ago)
- Topics: android, gradle-plugin, proguard, proguard-shield, r8
- Language: Kotlin
- Size: 136 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# :shield: ProGuard Shield
[](https://central.sonatype.com/artifact/io.github.fornewid.proguard-shield/proguard-shield)
[](https://plugins.gradle.org/plugin/io.github.fornewid.proguard-shield)
[](https://github.com/fornewid/proguard-shield/actions/workflows/build.yml)
[](LICENSE)
> :warning: This project is in an experimental stage. APIs and behavior may change without notice.
A Gradle plugin that detects unintentional changes to Android's merged ProGuard/R8 rules.
## Why?
R8/ProGuard rules come from your app, AAR consumer rules, AGP defaults, and AAPT2.
Adding a dependency or upgrading AGP can silently inject new rules — disabling
obfuscation, broadening keeps, weakening optimizer behavior — without touching
your project's own `proguard-rules.pro`.
**ProGuard Shield** saves a baseline of the fully-merged rule set and fails the
build when something changes. Optional regex patterns also fail the build when
a known-dangerous rule appears.
## Quick Start
### Step 1: Apply the plugin
Add the plugin to your **application** module's `build.gradle.kts` (library modules are not supported — they don't run R8):
```kotlin
// app/build.gradle.kts
plugins {
id("com.android.application")
id("io.github.fornewid.proguard-shield") version ""
}
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
}
proguardShield {
configuration("release")
}
```
### Step 2: Generate a baseline
```bash
./gradlew proguardShieldBaseline
```
Creates baseline files under `proguardShield/`:
```
proguardShield/
├── releaseRules.txt # accurate path — what R8 emits via -printconfiguration
└── releaseFastRules.txt # fast path — same content, captured without running R8
```
Commit both files to version control.
### Step 3: Detect changes
```bash
./gradlew check
```
The `check` lifecycle runs the **fast** path (no R8 invocation, seconds instead
of minutes). If the merged rule set differs from the baseline, the build fails
with a diff:
```diff
ProGuard/R8 rules changed in :app for release.
+ -keep class com.example.NewlyAdded { *; }
If this is intentional, re-baseline using ./gradlew :app:proguardShieldFastBaselineRelease
Or use ./gradlew proguardShieldBaseline to re-baseline in entire project.
```
The recommended re-baseline command is the aggregate `./gradlew proguardShieldBaseline`
— it regenerates **both** baseline files together so the fast and accurate paths
stay in sync.
## Two implementations
Internally `proguard-shield` ships two extraction strategies. Both produce
bit-identical baselines when parity holds, and both run the same forbidden-pattern
check. They differ in how they reach the rules:
| Task family | What runs | Speed | AGP coupling |
|---|---|---|---|
| `proguardShield{Variant}` | full R8 with `-printconfiguration` | slow (minutes on real apps) | public AGP API only |
| `proguardShieldFast{Variant}` | reads R8 inputs directly via reflection | fast (seconds) | uses AGP internal class (`ProguardConfigurableTask`) |
Daily `check` runs only the fast path. After every AGP upgrade, run the parity
verification task to confirm the fast path's reflection contract still holds:
```bash
./gradlew :app:proguardShieldVerifyParity
```
This regenerates both baselines and byte-compares them. Divergence means the
fast path can no longer be trusted on this AGP version — fall back to the
accurate `proguardShield` task and please file an issue.
## Forbidden patterns
Empty by default. Declare regex patterns that fail the build whenever a
matching rule appears in the merged input — useful for projects that want
to ban overly broad keeps, disabled obfuscation, and similar foot-guns.
```kotlin
proguardShield {
configuration("release") {
forbiddenPatterns = listOf(
"-keep\\s+class\\s+\\*\\*", // overly broad keeps
"-dontobfuscate", // obfuscation disabled
"-dontshrink", // shrinking disabled
)
}
}
```
Each pattern is matched against the directive head of every rule unit
(continuation lines of multi-line directives like `-keepattributes A,\nB,\nC`
are joined; bodies of `-keep ... { ... }` blocks are excluded). Forbidden
detection runs **before** drift detection — re-baselining cannot silence
a forbidden match.
## Configuration
```kotlin
proguardShield {
baselineDir.set("custom-dir") // default: "proguardShield"
configuration("release") {
forbiddenPatterns = listOf(
"-dontobfuscate",
)
}
}
```
| Option | Default | Description |
|---|---|---|
| `baselineDir` | `"proguardShield"` | Directory (relative to the module) where baseline files are written. |
| `forbiddenPatterns` | `[]` | Regex patterns that fail the build whenever a matching rule appears. |
## Requirements
- Android Gradle Plugin 8.0.0+
- Gradle 8.0+
- `com.android.application` modules only — library modules don't run R8
## AI Agent Guide
If you use an AI coding assistant (Claude Code, GitHub Copilot, Gemini, Cursor, etc.),
reference the [setup guide](docs/setup-guide.md.txt) for accurate installation
instructions and common pitfalls.
## Acknowledgements
Sibling project to [manifest-shield](https://github.com/fornewid/manifest-shield)
and [highlander](https://github.com/fornewid/highlander); shares their build,
release, and integration-test infrastructure. Releasing details live in
[`RELEASING.md`](RELEASING.md).
## License
[Apache License 2.0](LICENSE)