@mmerterden/multi-agent-pipeline 20.1.0 → 20.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/docs/facts.json +6 -6
- package/manifest.json +55 -34
- package/package.json +1 -1
- package/pipeline/multi-agent-refs/features/usage-reporting.md +7 -3
- package/pipeline/schemas/prefs.schema.json +4 -0
- package/pipeline/scripts/usage-register.mjs +2 -0
- package/pipeline/skills/.skill-manifest.json +36 -20
- package/pipeline/skills/.skills-index.json +75 -9
- package/pipeline/skills/shared/README.md +13 -7
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +71 -0
- package/pipeline/skills/shared/external/android-architecture/references/patterns.md +142 -0
- package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +314 -0
- package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +432 -0
- package/pipeline/skills/shared/external/android-datastore/SKILL.md +236 -0
- package/pipeline/skills/shared/external/android-datastore/references/patterns.md +297 -0
- package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +249 -0
- package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +270 -0
- package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +62 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +255 -0
- package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +257 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +86 -602
- package/pipeline/skills/shared/external/android-performance/references/patterns.md +659 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +117 -430
- package/pipeline/skills/shared/external/android-security/references/patterns.md +690 -0
- package/pipeline/skills/shared/external/{android_ui_verification → android-ui-verification}/SKILL.md +1 -1
- package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +35 -733
- package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +299 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +255 -0
- package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +167 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +39 -174
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +178 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +48 -0
- package/pipeline/skills/shared/external/compose-components/references/patterns.md +200 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +66 -3
- package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +191 -0
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +107 -397
- package/pipeline/skills/shared/external/compose-testing/references/patterns.md +631 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +121 -449
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +715 -0
- package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +143 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +27 -102
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +42 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +94 -383
- package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +640 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +101 -440
- package/pipeline/skills/shared/external/room-database/references/patterns.md +614 -0
- package/pipeline/skills/shared/external/storekit/SKILL.md +69 -343
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +371 -0
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +25 -101
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +107 -0
- package/pipeline/skills/skills-index.md +8 -2
|
@@ -8,10 +8,19 @@ description: "Configure Android builds with Gradle Kotlin DSL, version catalogs
|
|
|
8
8
|
Modern Gradle build configuration for Android projects using Kotlin DSL,
|
|
9
9
|
version catalogs, and convention plugins. Targets AGP 8.x+ and Gradle 8.x+.
|
|
10
10
|
|
|
11
|
+
Full, copy-ready configuration for every section below lives in
|
|
12
|
+
[references/patterns.md](references/patterns.md). Load that file when you need a
|
|
13
|
+
complete example to adapt (catalog, convention plugin, module build file,
|
|
14
|
+
ProGuard rules, or `gradle.properties`). Each heading there mirrors a section
|
|
15
|
+
here.
|
|
16
|
+
|
|
11
17
|
## Contents
|
|
12
18
|
|
|
13
19
|
- [Version Catalog](#version-catalog)
|
|
14
20
|
- [Convention Plugins](#convention-plugins)
|
|
21
|
+
- [Convention Plugin Composition](#convention-plugin-composition)
|
|
22
|
+
- [Typed BuildConfig Injection](#typed-buildconfig-injection)
|
|
23
|
+
- [Typed Module Paths and Flavors](#typed-module-paths-and-flavors)
|
|
15
24
|
- [App Module Configuration](#app-module-configuration)
|
|
16
25
|
- [Library Module Configuration](#library-module-configuration)
|
|
17
26
|
- [Compose Configuration](#compose-configuration)
|
|
@@ -19,490 +28,148 @@ version catalogs, and convention plugins. Targets AGP 8.x+ and Gradle 8.x+.
|
|
|
19
28
|
- [Signing Config](#signing-config)
|
|
20
29
|
- [R8 and ProGuard](#r8-and-proguard)
|
|
21
30
|
- [Build Performance](#build-performance)
|
|
31
|
+
- [Fast-Feedback Tasks](#fast-feedback-tasks)
|
|
22
32
|
- [Do's and Don'ts](#dos-and-donts)
|
|
23
33
|
- [Troubleshooting](#troubleshooting)
|
|
24
34
|
- [Review Checklist](#review-checklist)
|
|
25
35
|
|
|
26
36
|
## Version Catalog
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
compose-bom = "2024.12.01"
|
|
36
|
-
hilt = "2.53.1"
|
|
37
|
-
room = "2.6.1"
|
|
38
|
-
retrofit = "2.11.0"
|
|
39
|
-
okhttp = "4.12.0"
|
|
40
|
-
coroutines = "1.9.0"
|
|
41
|
-
lifecycle = "2.8.7"
|
|
42
|
-
navigation = "2.8.5"
|
|
43
|
-
serialization = "1.7.3"
|
|
44
|
-
coil = "2.7.0"
|
|
45
|
-
turbine = "1.2.0"
|
|
46
|
-
roborazzi = "1.32.2"
|
|
47
|
-
|
|
48
|
-
[libraries]
|
|
49
|
-
# Compose
|
|
50
|
-
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
|
51
|
-
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
52
|
-
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
|
53
|
-
compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
|
54
|
-
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
|
55
|
-
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
|
|
56
|
-
|
|
57
|
-
# Lifecycle
|
|
58
|
-
lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
|
59
|
-
lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
|
60
|
-
|
|
61
|
-
# Navigation
|
|
62
|
-
navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation" }
|
|
63
|
-
|
|
64
|
-
# Hilt
|
|
65
|
-
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
|
|
66
|
-
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
|
|
67
|
-
hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version = "1.2.0" }
|
|
68
|
-
|
|
69
|
-
# Room
|
|
70
|
-
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
|
71
|
-
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
|
72
|
-
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
|
73
|
-
room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room" }
|
|
74
|
-
|
|
75
|
-
# Network
|
|
76
|
-
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
|
|
77
|
-
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
|
78
|
-
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
|
|
79
|
-
serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
|
|
80
|
-
retrofit-serialization = { group = "com.squareup.retrofit2", name = "converter-kotlinx-serialization", version = "2.11.0" }
|
|
81
|
-
|
|
82
|
-
# Coroutines
|
|
83
|
-
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
|
|
84
|
-
coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
|
85
|
-
coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
|
86
|
-
|
|
87
|
-
# Image loading
|
|
88
|
-
coil-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" }
|
|
89
|
-
|
|
90
|
-
# Testing
|
|
91
|
-
junit = { group = "junit", name = "junit", version = "4.13.2" }
|
|
92
|
-
turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" }
|
|
93
|
-
truth = { group = "com.google.truth", name = "truth", version = "1.4.4" }
|
|
94
|
-
compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
|
95
|
-
compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
|
96
|
-
roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" }
|
|
97
|
-
roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose", version.ref = "roborazzi" }
|
|
98
|
-
|
|
99
|
-
[bundles]
|
|
100
|
-
compose = ["compose-ui", "compose-ui-tooling-preview", "compose-material3", "compose-runtime"]
|
|
101
|
-
compose-debug = ["compose-ui-tooling", "compose-ui-test-manifest"]
|
|
102
|
-
lifecycle = ["lifecycle-runtime-compose", "lifecycle-viewmodel-compose"]
|
|
103
|
-
room = ["room-runtime", "room-ktx"]
|
|
104
|
-
network = ["retrofit", "okhttp", "okhttp-logging", "serialization-json", "retrofit-serialization"]
|
|
105
|
-
|
|
106
|
-
[plugins]
|
|
107
|
-
android-application = { id = "com.android.application", version.ref = "agp" }
|
|
108
|
-
android-library = { id = "com.android.library", version.ref = "agp" }
|
|
109
|
-
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
|
110
|
-
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
|
111
|
-
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
112
|
-
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
|
113
|
-
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
|
114
|
-
room = { id = "androidx.room", version.ref = "room" }
|
|
115
|
-
```
|
|
38
|
+
Declare every version, library, and plugin in `gradle/libs.versions.toml` under
|
|
39
|
+
`[versions]`, `[libraries]`, `[bundles]`, and `[plugins]`. Reference them with
|
|
40
|
+
`libs.xxx`, `libs.bundles.xxx`, and `alias(libs.plugins.xxx)`; never hardcode a
|
|
41
|
+
version in a `build.gradle.kts`.
|
|
42
|
+
|
|
43
|
+
Full catalog (Compose BOM, Hilt, Room, Retrofit, coroutines, testing, bundles,
|
|
44
|
+
plugins): [references/patterns.md#version-catalog](references/patterns.md#version-catalog).
|
|
116
45
|
|
|
117
46
|
## Convention Plugins
|
|
118
47
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
import org.gradle.api.Project
|
|
173
|
-
import org.gradle.kotlin.dsl.configure
|
|
174
|
-
|
|
175
|
-
class AndroidLibraryConventionPlugin : Plugin<Project> {
|
|
176
|
-
override fun apply(target: Project) {
|
|
177
|
-
with(target) {
|
|
178
|
-
with(pluginManager) {
|
|
179
|
-
apply("com.android.library")
|
|
180
|
-
apply("org.jetbrains.kotlin.android")
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
extensions.configure<LibraryExtension> {
|
|
184
|
-
compileSdk = 35
|
|
185
|
-
defaultConfig {
|
|
186
|
-
minSdk = 26
|
|
187
|
-
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
188
|
-
}
|
|
189
|
-
compileOptions {
|
|
190
|
-
sourceCompatibility = JavaVersion.VERSION_17
|
|
191
|
-
targetCompatibility = JavaVersion.VERSION_17
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Register in build-logic/convention/build.gradle.kts
|
|
200
|
-
|
|
201
|
-
```kotlin
|
|
202
|
-
gradlePlugin {
|
|
203
|
-
plugins {
|
|
204
|
-
register("androidLibrary") {
|
|
205
|
-
id = "app.android.library"
|
|
206
|
-
implementationClass = "AndroidLibraryConventionPlugin"
|
|
207
|
-
}
|
|
208
|
-
register("androidCompose") {
|
|
209
|
-
id = "app.android.compose"
|
|
210
|
-
implementationClass = "AndroidComposeConventionPlugin"
|
|
211
|
-
}
|
|
212
|
-
register("androidHilt") {
|
|
213
|
-
id = "app.android.hilt"
|
|
214
|
-
implementationClass = "AndroidHiltConventionPlugin"
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Usage in Feature Module
|
|
221
|
-
|
|
222
|
-
```kotlin
|
|
223
|
-
// feature/home/build.gradle.kts
|
|
224
|
-
plugins {
|
|
225
|
-
id("app.android.library")
|
|
226
|
-
id("app.android.compose")
|
|
227
|
-
id("app.android.hilt")
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
dependencies {
|
|
231
|
-
implementation(project(":core:model"))
|
|
232
|
-
implementation(project(":core:data"))
|
|
233
|
-
implementation(project(":core:ui"))
|
|
234
|
-
}
|
|
235
|
-
```
|
|
48
|
+
Share Android/Compose/Hilt configuration through a `build-logic` included build
|
|
49
|
+
instead of copying blocks between modules. Root `settings.gradle.kts` does
|
|
50
|
+
`includeBuild("build-logic")`; the convention module implements
|
|
51
|
+
`Plugin<Project>` classes and registers them with stable ids
|
|
52
|
+
(`app.android.library`, `app.android.compose`, `app.android.hilt`) that feature
|
|
53
|
+
modules then apply.
|
|
54
|
+
|
|
55
|
+
Full wiring (settings files, `Plugin<Project>` class, `gradlePlugin` registration,
|
|
56
|
+
feature-module usage):
|
|
57
|
+
[references/patterns.md#convention-plugins](references/patterns.md#convention-plugins).
|
|
58
|
+
|
|
59
|
+
## Convention Plugin Composition
|
|
60
|
+
|
|
61
|
+
Compose plugins into a ladder: a higher-level plugin applies the lower ones. A
|
|
62
|
+
single `feature-impl` plugin applies library + hilt + compose + test and wires
|
|
63
|
+
the standard dependency set, so a feature module's `plugins {}` block is one
|
|
64
|
+
line. Put shared toolchain and compiler flags (`jvmTarget`, strict explicit-api,
|
|
65
|
+
`compileSdk`/`minSdk`, Java 17) in one `configureKotlinAndroid()` extension that
|
|
66
|
+
every Android convention plugin calls, so no value is duplicated across plugins.
|
|
67
|
+
|
|
68
|
+
Alias your own convention plugins in the version catalog
|
|
69
|
+
(`app-android-feature = { id = "app.android.feature", version = "unspecified" }`)
|
|
70
|
+
so modules apply `alias(libs.plugins.app.android.feature)` rather than a raw id
|
|
71
|
+
string.
|
|
72
|
+
|
|
73
|
+
For build-FAILING quality-gate tasks (module-layer checks, coverage floor,
|
|
74
|
+
Compose stability gate), see the `android-build-quality-gates` skill; those
|
|
75
|
+
tasks are not reproduced here.
|
|
76
|
+
|
|
77
|
+
Full ladder plugin, `configureKotlinAndroid()`, and catalog aliases:
|
|
78
|
+
[references/patterns.md#convention-plugin-composition](references/patterns.md#convention-plugin-composition).
|
|
79
|
+
|
|
80
|
+
## Typed BuildConfig Injection
|
|
81
|
+
|
|
82
|
+
Replace scattered `buildConfigField("String", "KEY", "\"...\"")` strings with a
|
|
83
|
+
typed key `enum` (each key carries its Java type), per-module `contributor`
|
|
84
|
+
objects, and a central `resolver` that filters contributions by module scope and
|
|
85
|
+
de-dupes by key. The convention plugin materializes the resolved entries once per
|
|
86
|
+
flavor, so the type is always right and no module hand-writes a `buildConfigField`.
|
|
87
|
+
|
|
88
|
+
Full key enum, contributor interface, and resolver:
|
|
89
|
+
[references/patterns.md#typed-buildconfig-injection](references/patterns.md#typed-buildconfig-injection).
|
|
90
|
+
|
|
91
|
+
## Typed Module Paths and Flavors
|
|
92
|
+
|
|
93
|
+
Model module paths and flavors as `sealed`/`enum` types so builds reference
|
|
94
|
+
`Module.Core.Domain.toProject()` instead of the stringly-typed
|
|
95
|
+
`project(":core:domain")`, and `Flavor.PROD.baseUrl` instead of a repeated URL
|
|
96
|
+
literal. A typo becomes a compile error, and refactoring a path updates one
|
|
97
|
+
declaration.
|
|
98
|
+
|
|
99
|
+
Full sealed `Module` hierarchy, `toProject()`, and `Flavor` enum:
|
|
100
|
+
[references/patterns.md#typed-module-paths-and-flavors](references/patterns.md#typed-module-paths-and-flavors).
|
|
236
101
|
|
|
237
102
|
## App Module Configuration
|
|
238
103
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
alias(libs.plugins.ksp)
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
android {
|
|
250
|
-
namespace = "com.example.app"
|
|
251
|
-
compileSdk = 35
|
|
252
|
-
|
|
253
|
-
defaultConfig {
|
|
254
|
-
applicationId = "com.example.app"
|
|
255
|
-
minSdk = 26
|
|
256
|
-
targetSdk = 35
|
|
257
|
-
versionCode = 1
|
|
258
|
-
versionName = "1.0.0"
|
|
259
|
-
testInstrumentationRunner = "com.example.app.HiltTestRunner"
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
buildTypes {
|
|
263
|
-
release {
|
|
264
|
-
isMinifyEnabled = true
|
|
265
|
-
isShrinkResources = true
|
|
266
|
-
proguardFiles(
|
|
267
|
-
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
268
|
-
"proguard-rules.pro",
|
|
269
|
-
)
|
|
270
|
-
}
|
|
271
|
-
debug {
|
|
272
|
-
isMinifyEnabled = false
|
|
273
|
-
applicationIdSuffix = ".debug"
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
compileOptions {
|
|
278
|
-
sourceCompatibility = JavaVersion.VERSION_17
|
|
279
|
-
targetCompatibility = JavaVersion.VERSION_17
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
kotlinOptions {
|
|
283
|
-
jvmTarget = "17"
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
dependencies {
|
|
288
|
-
implementation(platform(libs.compose.bom))
|
|
289
|
-
implementation(libs.bundles.compose)
|
|
290
|
-
implementation(libs.bundles.lifecycle)
|
|
291
|
-
implementation(libs.navigation.compose)
|
|
292
|
-
implementation(libs.hilt.android)
|
|
293
|
-
ksp(libs.hilt.compiler)
|
|
294
|
-
implementation(libs.hilt.navigation.compose)
|
|
295
|
-
|
|
296
|
-
debugImplementation(libs.bundles.compose.debug)
|
|
297
|
-
|
|
298
|
-
testImplementation(libs.junit)
|
|
299
|
-
testImplementation(libs.truth)
|
|
300
|
-
testImplementation(libs.coroutines.test)
|
|
301
|
-
testImplementation(libs.turbine)
|
|
302
|
-
|
|
303
|
-
androidTestImplementation(platform(libs.compose.bom))
|
|
304
|
-
androidTestImplementation(libs.compose.ui.test)
|
|
305
|
-
}
|
|
306
|
-
```
|
|
104
|
+
Apply plugins via `alias(libs.plugins.xxx)`, set `namespace`/`compileSdk`/
|
|
105
|
+
`defaultConfig`, enable `isMinifyEnabled` + `isShrinkResources` on `release`,
|
|
106
|
+
target Java 17, and pull dependencies from the catalog (`platform(libs.compose.bom)`,
|
|
107
|
+
bundles, `ksp(libs.hilt.compiler)`).
|
|
108
|
+
|
|
109
|
+
Full `app/build.gradle.kts`:
|
|
110
|
+
[references/patterns.md#app-module-configuration](references/patterns.md#app-module-configuration).
|
|
307
111
|
|
|
308
112
|
## Library Module Configuration
|
|
309
113
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
alias(libs.plugins.kotlin.android)
|
|
315
|
-
alias(libs.plugins.hilt)
|
|
316
|
-
alias(libs.plugins.ksp)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
android {
|
|
320
|
-
namespace = "com.example.core.data"
|
|
321
|
-
compileSdk = 35
|
|
322
|
-
|
|
323
|
-
defaultConfig {
|
|
324
|
-
minSdk = 26
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
dependencies {
|
|
329
|
-
implementation(project(":core:model"))
|
|
330
|
-
implementation(project(":core:network"))
|
|
331
|
-
implementation(project(":core:database"))
|
|
332
|
-
implementation(libs.hilt.android)
|
|
333
|
-
ksp(libs.hilt.compiler)
|
|
334
|
-
implementation(libs.coroutines.core)
|
|
335
|
-
|
|
336
|
-
testImplementation(libs.junit)
|
|
337
|
-
testImplementation(libs.truth)
|
|
338
|
-
testImplementation(libs.coroutines.test)
|
|
339
|
-
}
|
|
340
|
-
```
|
|
114
|
+
A library module mirrors the app module but applies `libs.plugins.android.library`,
|
|
115
|
+
declares only its `namespace`/`minSdk`, and depends on sibling `project(...)`
|
|
116
|
+
modules. Full `core/data/build.gradle.kts`:
|
|
117
|
+
[references/patterns.md#library-module-configuration](references/patterns.md#library-module-configuration).
|
|
341
118
|
|
|
342
119
|
## Compose Configuration
|
|
343
120
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
// With Kotlin 2.0+, use the Compose Compiler plugin:
|
|
353
|
-
plugins {
|
|
354
|
-
alias(libs.plugins.compose.compiler)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Optional: Compose compiler metrics for performance debugging
|
|
358
|
-
// In gradle.properties or via command line:
|
|
359
|
-
// composeCompiler.reportsDestination=build/compose-reports
|
|
360
|
-
// composeCompiler.metricsDestination=build/compose-metrics
|
|
361
|
-
```
|
|
121
|
+
Enable `buildFeatures { compose = true }` and apply the Compose Compiler plugin
|
|
122
|
+
(`alias(libs.plugins.compose.compiler)`) on Kotlin 2.0+. Compiler metrics/reports
|
|
123
|
+
are opt-in via `composeCompiler.*Destination`.
|
|
124
|
+
|
|
125
|
+
Full snippet: [references/patterns.md#compose-configuration](references/patterns.md#compose-configuration).
|
|
362
126
|
|
|
363
127
|
## Build Variants and Flavors
|
|
364
128
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
|
372
|
-
}
|
|
373
|
-
debug {
|
|
374
|
-
isMinifyEnabled = false
|
|
375
|
-
applicationIdSuffix = ".debug"
|
|
376
|
-
isDebuggable = true
|
|
377
|
-
}
|
|
378
|
-
create("staging") {
|
|
379
|
-
initWith(getByName("release"))
|
|
380
|
-
applicationIdSuffix = ".staging"
|
|
381
|
-
isMinifyEnabled = true
|
|
382
|
-
matchingFallbacks += "release"
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
flavorDimensions += "environment"
|
|
387
|
-
productFlavors {
|
|
388
|
-
create("dev") {
|
|
389
|
-
dimension = "environment"
|
|
390
|
-
applicationIdSuffix = ".dev"
|
|
391
|
-
buildConfigField("String", "BASE_URL", "\"https://dev-api.example.com/\"")
|
|
392
|
-
}
|
|
393
|
-
create("prod") {
|
|
394
|
-
dimension = "environment"
|
|
395
|
-
buildConfigField("String", "BASE_URL", "\"https://api.example.com/\"")
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
```
|
|
129
|
+
Extend `buildTypes` (e.g. a `staging` type via `initWith(getByName("release"))`)
|
|
130
|
+
and declare `productFlavors` under a `flavorDimensions` axis, using
|
|
131
|
+
`buildConfigField` for per-environment values like the base URL.
|
|
132
|
+
|
|
133
|
+
Full variants + flavors block:
|
|
134
|
+
[references/patterns.md#build-variants-and-flavors](references/patterns.md#build-variants-and-flavors).
|
|
400
135
|
|
|
401
136
|
## Signing Config
|
|
402
137
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
storeFile = file(System.getenv("KEYSTORE_PATH") ?: "release.keystore")
|
|
408
|
-
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
|
|
409
|
-
keyAlias = System.getenv("KEY_ALIAS") ?: ""
|
|
410
|
-
keyPassword = System.getenv("KEY_PASSWORD") ?: ""
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
buildTypes {
|
|
415
|
-
release {
|
|
416
|
-
signingConfig = signingConfigs.getByName("release")
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
Never commit keystore passwords. Use environment variables or `local.properties` (gitignored).
|
|
138
|
+
Read keystore path and credentials from environment variables (or gitignored
|
|
139
|
+
`local.properties`), never from committed source, and attach the config to the
|
|
140
|
+
`release` build type. Full `signingConfigs` block:
|
|
141
|
+
[references/patterns.md#signing-config](references/patterns.md#signing-config).
|
|
423
142
|
|
|
424
143
|
## R8 and ProGuard
|
|
425
144
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
# Kotlin serialization
|
|
430
|
-
-keepattributes *Annotation*, InnerClasses
|
|
431
|
-
-dontnote kotlinx.serialization.AnnotationsKt
|
|
432
|
-
|
|
433
|
-
-keepclassmembers class kotlinx.serialization.json.** {
|
|
434
|
-
*** Companion;
|
|
435
|
-
}
|
|
436
|
-
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
|
437
|
-
kotlinx.serialization.KSerializer serializer(...);
|
|
438
|
-
}
|
|
439
|
-
-keep,includedescriptorclasses class com.example.app.data.dto.**$$serializer { *; }
|
|
440
|
-
-keepclassmembers class com.example.app.data.dto.** {
|
|
441
|
-
*** Companion;
|
|
442
|
-
}
|
|
443
|
-
-keepclasseswithmembers class com.example.app.data.dto.** {
|
|
444
|
-
kotlinx.serialization.KSerializer serializer(...);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
# Retrofit
|
|
448
|
-
-keepattributes Signature
|
|
449
|
-
-keepattributes Exceptions
|
|
450
|
-
-keepclassmembers,allowshrinking,allowobfuscation interface * {
|
|
451
|
-
@retrofit2.http.* <methods>;
|
|
452
|
-
}
|
|
453
|
-
-dontwarn javax.annotation.**
|
|
454
|
-
-dontwarn kotlin.Unit
|
|
455
|
-
-dontwarn retrofit2.KotlinExtensions
|
|
456
|
-
-dontwarn retrofit2.KotlinExtensions$*
|
|
457
|
-
|
|
458
|
-
# OkHttp
|
|
459
|
-
-dontwarn okhttp3.internal.platform.**
|
|
460
|
-
-dontwarn org.conscrypt.**
|
|
461
|
-
-dontwarn org.bouncycastle.**
|
|
462
|
-
-dontwarn org.openjsse.**
|
|
463
|
-
|
|
464
|
-
# Hilt
|
|
465
|
-
-keep class dagger.hilt.** { *; }
|
|
466
|
-
```
|
|
145
|
+
Keep rules are needed for reflection-driven libraries: kotlinx serialization
|
|
146
|
+
(`$$serializer` classes and `Companion`), Retrofit (`@retrofit2.http.*` methods,
|
|
147
|
+
signatures), OkHttp `-dontwarn`s, and Hilt. Never use a global `-dontwarn *`.
|
|
467
148
|
|
|
468
|
-
|
|
149
|
+
Full `proguard-rules.pro`:
|
|
150
|
+
[references/patterns.md#r8-and-proguard](references/patterns.md#r8-and-proguard).
|
|
469
151
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
```properties
|
|
473
|
-
# Parallel execution
|
|
474
|
-
org.gradle.parallel=true
|
|
475
|
-
|
|
476
|
-
# Configuration cache
|
|
477
|
-
org.gradle.configuration-cache=true
|
|
478
|
-
|
|
479
|
-
# Build cache
|
|
480
|
-
org.gradle.caching=true
|
|
481
|
-
|
|
482
|
-
# JVM memory
|
|
483
|
-
org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC -XX:MaxMetaspaceSize=1g
|
|
152
|
+
## Build Performance
|
|
484
153
|
|
|
485
|
-
|
|
486
|
-
|
|
154
|
+
Enable parallel execution, configuration cache, build cache, sized JVM/daemon
|
|
155
|
+
memory, and `android.nonTransitiveRClass=true` in `gradle.properties`; disable
|
|
156
|
+
unused `buildfeatures`. Optionally lock dependencies with `dependencyLocking`.
|
|
487
157
|
|
|
488
|
-
|
|
489
|
-
|
|
158
|
+
Full `gradle.properties` and locking snippet:
|
|
159
|
+
[references/patterns.md#build-performance](references/patterns.md#build-performance).
|
|
490
160
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
android.defaults.buildfeatures.aidl=false
|
|
494
|
-
android.defaults.buildfeatures.renderscript=false
|
|
495
|
-
```
|
|
161
|
+
Keep the build cache (`org.gradle.caching=true`) in this perf-flag set alongside
|
|
162
|
+
parallel execution and the configuration cache.
|
|
496
163
|
|
|
497
|
-
|
|
164
|
+
## Fast-Feedback Tasks
|
|
498
165
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
166
|
+
Install git hooks through a Gradle task wired as a `preBuild` dependency, so a
|
|
167
|
+
fresh clone gets the hooks on its first build with no manual step. Resolve the
|
|
168
|
+
git directory correctly when `.git` is a worktree pointer file (contents
|
|
169
|
+
`gitdir: <path>`) rather than a directory, or the hooks land in the wrong place.
|
|
503
170
|
|
|
504
|
-
|
|
505
|
-
|
|
171
|
+
Full `resolveGitDir` helper, `installGitHooks` task, and `preBuild` wiring:
|
|
172
|
+
[references/patterns.md#fast-feedback-tasks](references/patterns.md#fast-feedback-tasks).
|
|
506
173
|
|
|
507
174
|
## Do's and Don'ts
|
|
508
175
|
|
|
@@ -550,3 +217,8 @@ dependencyLocking {
|
|
|
550
217
|
- [ ] `android.nonTransitiveRClass=true` enabled
|
|
551
218
|
- [ ] Bundles used for related dependency groups
|
|
552
219
|
- [ ] No `kapt` usage in new modules
|
|
220
|
+
- [ ] `BuildConfig` values injected via typed key/contributor/resolver, not scattered `buildConfigField` strings
|
|
221
|
+
- [ ] Module paths and flavors referenced through typed `Module`/`Flavor`, not raw `project(":...")` strings
|
|
222
|
+
- [ ] Convention plugins composed as a ladder with shared config in one `configureKotlinAndroid()` extension
|
|
223
|
+
- [ ] Own convention plugins aliased in the version catalog (`libs.plugins.*`)
|
|
224
|
+
- [ ] Git-hooks install task wired as a `preBuild` dependency, worktree `.git` file handled
|