Android build sync
Skill esaldgut/ai-native-engineering-workspace/global-skills/claude-code-workflow/android-build-sync
AI-native engineering workspace — 42 Claude Code agent skills, platform-base workflow docs, and a freshness system that re-verifies each pattern against vendor docs.
npx -y skills add esaldgut/ai-native-engineering-workspace --skill android-build-syncAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
One-command Android build-consistency gate, environment-agnostic — format (ktlint via org.jlleitschuh.gradle.ktlint), static analysis (detekt via io.gitlab.arturbosch.detekt, with the Compose ruleset), platform lint (./gradlew lintDebug), then compile/package (./gradlew assembleDebug), with pre-flight checks for JDK (17/21) and the Gradle wrapper. Enforces ./gradlew over system gradle, a detekt baseline for retrofitting, and parallel+caching gradle.properties. Use to set up or run a pre-commit/pre-push/CI build gate for a Kotlin/Compose project. No hardcoded machine paths.
SKILL.md
8.4 KB, as published. Nobody here has run it
android-build-sync — one-command Kotlin/Compose build gate
A single consistency gate that runs the consensus 2025-26 pipeline — ktlint → detekt → lintDebug →
assembleDebug — so "it compiles, it's formatted, it passes static analysis, it links" is one command,
not four remembered ones. Environment-agnostic by design: it uses the project-local ./gradlew
wrapper and relative paths only — no absolute machine paths, no external-drive paths, nothing
machine-specific. It works identically on any developer's machine and in CI.
When to invoke
- Setting up or running a build-consistency gate for a Kotlin/Compose project (pre-commit, pre-push, or CI).
- Before opening a PR, to catch formatting/lint/compile breakage locally.
- You see CI running
gradle(system-installed) instead of./gradlew, or formatting failures that pass locally but fail in CI — fix per the rules below.
Announce on invoke: "Using android-build-sync to run the ktlint → detekt → lintDebug →
assembleDebug gate via the project's ./gradlew wrapper."
The pipeline
| Stage | Task | Catches |
|---|---|---|
| 1. Format | ./gradlew ktlintFormat (or ktlintCheck in CI) | style / JetBrains Kotlin formatting |
| 2. Static analysis | ./gradlew detekt | complexity, smells, Compose recomposition pitfalls |
| 3. Platform lint | ./gradlew lintDebug | manifest issues, deprecated APIs, missing translations, Compose lints |
| 4. Compile + package | ./gradlew assembleDebug | "does the whole thing actually compile and link?" |
The rules (load-bearing)
./gradlew, never systemgradle. The wrapper pins the exact Gradle version — the reproducible-build invariant. Running systemgradleproduces non-reproducible builds; CI must always use./gradlew.- JDK 17 minimum for AGP 8.x (21 supported on AGP 8.7+). Building with JDK 11 fails with
Unsupported class file major version 61. The pre-flight checks the active JDK before doing work. lintDebug, notlint, as the gate. Plainlintruns every variant (release + debug + all flavors) and is slow on multi-flavor apps;lintDebugis the pragmatic single-variant gate.- Use a detekt baseline to retrofit onto an existing codebase.
./gradlew detektBaselinewritesdetekt-baseline.xml; subsequent runs fail only on new violations, so you adopt detekt without fixing every legacy finding at once. - Pin the ktlint version. ktlint releases disagree on formatting (import ordering changed in 0.50); an unpinned version causes "passes locally, fails in CI" loops. Pin it in the plugin config.
- Enable parallel builds and caching. Put
org.gradle.parallel=trueandorg.gradle.caching=trueingradle.properties— these dominate CI time for medium apps. - Add the Compose detekt ruleset.
io.nlopez.compose.rules:detektcatches recomposition bugs (lambda allocation in composition, missingkey()initems()) that plain detekt and lint miss.
Canonical example
# scripts/build-sync.sh — environment-agnostic; run from the repo root.
# Uses the project-local ./gradlew only. No absolute or machine-specific paths.
set -euo pipefail
echo "==> Pre-flight: JDK (expect 17 or 21)"
java -version 2>&1 | grep -E 'version "(17|21)' || { echo "JDK 17/21 required"; exit 1; }
echo "==> Pre-flight: Gradle wrapper present + executable"
test -x ./gradlew || { echo "./gradlew missing — run 'gradle wrapper'"; exit 1; }
echo "==> 1/4 ktlint format"; ./gradlew ktlintFormat
echo "==> 2/4 detekt"; ./gradlew detekt
echo "==> 3/4 Android Lint"; ./gradlew lintDebug
echo "==> 4/4 assembleDebug"; ./gradlew assembleDebug
echo "==> All gates passed."
// build.gradle.kts (root) — plugin + detekt baseline wiring (versions are illustrative; pin yours)
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.1" apply false
id("io.gitlab.arturbosch.detekt") version "1.23.7" apply false
}
subprojects {
apply(plugin = "org.jlleitschuh.gradle.ktlint")
apply(plugin = "io.gitlab.arturbosch.detekt")
extensions.configure<io.gitlab.arturbosch.detekt.extensions.DetektExtension> {
baseline = file("$rootDir/detekt-baseline.xml") // relative to repo root — portable
config.setFrom("$rootDir/detekt.yml")
}
}
# gradle.properties — reproducible, fast, portable
org.gradle.parallel=true
org.gradle.caching=true
# Do NOT hardcode org.gradle.java.home to a machine path; let JAVA_HOME / toolchains resolve it.
Portability note: never set
org.gradle.java.hometo an absolute path in a committedgradle.properties— it breaks every other machine and CI. Use Gradle JVM toolchains or the ambientJAVA_HOMEinstead. Likewise keep all script paths relative to the repo root.
Decision aids
- Hook split: run the fast stages (
ktlintFormat+detekt) in apre-commithook for tight feedback, and the slow stages (lintDebug+assembleDebug) inpre-pushor CI. detekt documents the pre-commit pattern directly. - ktlint vs Spotless? Plain
ktlintis simplest; Spotless wraps ktlint and adds non-Kotlin formatting (XML, Markdown) at the cost of more config. Default to ktlint; adopt Spotless only if you need multi-language formatting. - Single aggregate task? You can define a
./gradlew syncCheckthatdependsOnall four — cleaner to invoke but harder to bootstrap on an existing project. The shell script above is the portable starting point.
Related skills
global-skills/android/android-testing-patterns/SKILL.md— the test stage that runs after this gate (this skill stops atassembleDebug; tests are a separate./gradlew teststep).global-skills/claude-code-workflow/capture-lessons-cascade-android/SKILL.md— captures R8 / Compose / Hilt-scoping lessons surfaced by builds gated here.
Sources
- Android Lint · Enable app optimization with R8 / shrink code · AGP releases & JDK compatibility
- detekt · detekt git pre-commit hook · ktlint Gradle plugin · compose-rules (detekt)
Last verified: 2026-06-03 against developer.android.com (Android Lint variant tasks; AGP 8.x JDK 17
minimum), detekt 1.23.x, ktlint-gradle 12.x.
Re-check after: next Claude Code minor release, or by 2026-09-03. Decay risk: medium (Gradle
plugin versions move; the pipeline shape is stable).
Found a drift? Run /skill-pattern-freshness-audit claude-code-workflow.