android-sdd 1.0.0
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/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: environment-validator
|
|
3
|
+
description: >
|
|
4
|
+
Validating the build environment before running Android builds.
|
|
5
|
+
Load this skill when ensuring required tools are installed, verifying
|
|
6
|
+
SDK versions, checking environment variables, or creating pre-build
|
|
7
|
+
validation tasks that fail fast with clear error messages.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Environment Validator
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
Environment validation ensures the build environment has all required tools, SDKs, and configuration before the build starts. Without validation, builds fail with cryptic errors mid-way through. A good validator fails fast with clear, actionable messages.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- **Fail fast** — validate before any build work begins
|
|
20
|
+
- Provide **clear, actionable error messages** — tell the developer exactly what to fix
|
|
21
|
+
- Validate in `settings.gradle.kts` or a `preBuild` task — earliest possible point
|
|
22
|
+
- Check **required tools** (Java, Android SDK, NDK if needed) and **environment variables**
|
|
23
|
+
- Validation must be **fast** — it runs on every build
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Gradle Wrapper Version Validation
|
|
28
|
+
|
|
29
|
+
```kotlin
|
|
30
|
+
// settings.gradle.kts
|
|
31
|
+
// ✅ Validate Gradle wrapper version
|
|
32
|
+
val minGradleVersion = "8.7"
|
|
33
|
+
val currentVersion = gradle.gradleVersion
|
|
34
|
+
|
|
35
|
+
if (GradleVersion.version(currentVersion) < GradleVersion.version(minGradleVersion)) {
|
|
36
|
+
throw GradleException(
|
|
37
|
+
"""
|
|
38
|
+
❌ Gradle $minGradleVersion or higher is required.
|
|
39
|
+
Current version: $currentVersion
|
|
40
|
+
|
|
41
|
+
Fix: Run ./gradlew wrapper --gradle-version=$minGradleVersion
|
|
42
|
+
""".trimIndent()
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Java Version Validation
|
|
50
|
+
|
|
51
|
+
```kotlin
|
|
52
|
+
// settings.gradle.kts or root build.gradle.kts
|
|
53
|
+
// ✅ Validate Java version
|
|
54
|
+
val minJavaVersion = JavaVersion.VERSION_17
|
|
55
|
+
val currentJavaVersion = JavaVersion.current()
|
|
56
|
+
|
|
57
|
+
if (currentJavaVersion < minJavaVersion) {
|
|
58
|
+
throw GradleException(
|
|
59
|
+
"""
|
|
60
|
+
❌ Java $minJavaVersion or higher is required.
|
|
61
|
+
Current version: $currentJavaVersion
|
|
62
|
+
|
|
63
|
+
Fix: Install JDK 17+ and set JAVA_HOME:
|
|
64
|
+
export JAVA_HOME=${'$'}(/usr/libexec/java_home -v 17)
|
|
65
|
+
""".trimIndent()
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Android SDK Validation
|
|
73
|
+
|
|
74
|
+
```kotlin
|
|
75
|
+
// root build.gradle.kts
|
|
76
|
+
// ✅ Validate Android SDK is installed
|
|
77
|
+
subprojects {
|
|
78
|
+
afterEvaluate {
|
|
79
|
+
val androidExtension = extensions.findByName("android") ?: return@afterEvaluate
|
|
80
|
+
tasks.register("validateAndroidSdk") {
|
|
81
|
+
group = "verification"
|
|
82
|
+
doLast {
|
|
83
|
+
val sdkDir = System.getenv("ANDROID_HOME")
|
|
84
|
+
?: System.getenv("ANDROID_SDK_ROOT")
|
|
85
|
+
?: "${System.getProperty("user.home")}/Library/Android/sdk"
|
|
86
|
+
|
|
87
|
+
if (!File(sdkDir).exists()) {
|
|
88
|
+
throw GradleException(
|
|
89
|
+
"""
|
|
90
|
+
❌ Android SDK not found at: $sdkDir
|
|
91
|
+
|
|
92
|
+
Fix: Install Android Studio or set ANDROID_HOME:
|
|
93
|
+
export ANDROID_HOME=~/Library/Android/sdk
|
|
94
|
+
""".trimIndent()
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
val compileSdkVersion = 35
|
|
99
|
+
val platformDir = File(sdkDir, "platforms/android-$compileSdkVersion")
|
|
100
|
+
if (!platformDir.exists()) {
|
|
101
|
+
throw GradleException(
|
|
102
|
+
"""
|
|
103
|
+
❌ Android SDK platform $compileSdkVersion not installed.
|
|
104
|
+
|
|
105
|
+
Fix: Open Android Studio → SDK Manager → Install Android $compileSdkVersion
|
|
106
|
+
Or run: sdkmanager "platforms;android-$compileSdkVersion"
|
|
107
|
+
""".trimIndent()
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
tasks.named("preBuild") {
|
|
114
|
+
dependsOn("validateAndroidSdk")
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Environment Variables Validation
|
|
123
|
+
|
|
124
|
+
```kotlin
|
|
125
|
+
// build.gradle.kts (app)
|
|
126
|
+
// ✅ Validate required environment variables for release builds
|
|
127
|
+
tasks.register("validateReleaseEnvironment") {
|
|
128
|
+
group = "verification"
|
|
129
|
+
onlyIf { gradle.taskGraph.hasTask(":app:assembleRelease") }
|
|
130
|
+
|
|
131
|
+
doLast {
|
|
132
|
+
val required = mapOf(
|
|
133
|
+
"KEYSTORE_PATH" to "Path to the release keystore file",
|
|
134
|
+
"KEYSTORE_PASSWORD" to "Password for the keystore",
|
|
135
|
+
"KEY_ALIAS" to "Key alias in the keystore",
|
|
136
|
+
"KEY_PASSWORD" to "Password for the key"
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
val missing = required.filter { (key, _) ->
|
|
140
|
+
System.getenv(key).isNullOrBlank()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (missing.isNotEmpty()) {
|
|
144
|
+
throw GradleException(
|
|
145
|
+
buildString {
|
|
146
|
+
appendLine("❌ Missing required environment variables for release build:")
|
|
147
|
+
missing.forEach { (key, description) ->
|
|
148
|
+
appendLine(" - $key: $description")
|
|
149
|
+
}
|
|
150
|
+
appendLine()
|
|
151
|
+
appendLine("Fix: Set these variables in your CI environment or local .env file.")
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
tasks.named("preBuild") {
|
|
159
|
+
dependsOn("validateReleaseEnvironment")
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## google-services.json Validation
|
|
166
|
+
|
|
167
|
+
```kotlin
|
|
168
|
+
// app/build.gradle.kts
|
|
169
|
+
// ✅ Validate google-services.json exists before build
|
|
170
|
+
tasks.register("validateGoogleServices") {
|
|
171
|
+
group = "verification"
|
|
172
|
+
doLast {
|
|
173
|
+
val googleServicesFile = file("google-services.json")
|
|
174
|
+
if (!googleServicesFile.exists()) {
|
|
175
|
+
throw GradleException(
|
|
176
|
+
"""
|
|
177
|
+
❌ google-services.json not found at: ${googleServicesFile.absolutePath}
|
|
178
|
+
|
|
179
|
+
Fix:
|
|
180
|
+
1. Go to Firebase Console → Project Settings → Your App
|
|
181
|
+
2. Download google-services.json
|
|
182
|
+
3. Place it in the app/ directory
|
|
183
|
+
|
|
184
|
+
Note: Never commit this file to public repositories.
|
|
185
|
+
""".trimIndent()
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
tasks.named("preBuild") {
|
|
192
|
+
dependsOn("validateGoogleServices")
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Convention Plugin for Environment Validation
|
|
199
|
+
|
|
200
|
+
```kotlin
|
|
201
|
+
// build-logic/convention/src/main/kotlin/EnvironmentValidationPlugin.kt
|
|
202
|
+
class EnvironmentValidationPlugin : Plugin<Project> {
|
|
203
|
+
override fun apply(target: Project) {
|
|
204
|
+
target.tasks.register("validateEnvironment") {
|
|
205
|
+
group = "verification"
|
|
206
|
+
description = "Validates build environment prerequisites"
|
|
207
|
+
|
|
208
|
+
doLast {
|
|
209
|
+
val errors = mutableListOf<String>()
|
|
210
|
+
|
|
211
|
+
// Java version
|
|
212
|
+
if (JavaVersion.current() < JavaVersion.VERSION_17) {
|
|
213
|
+
errors += "Java 17+ required (current: ${JavaVersion.current()})"
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Gradle version
|
|
217
|
+
if (GradleVersion.version(target.gradle.gradleVersion) <
|
|
218
|
+
GradleVersion.version("8.7")) {
|
|
219
|
+
errors += "Gradle 8.7+ required (current: ${target.gradle.gradleVersion})"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ANDROID_HOME
|
|
223
|
+
val androidHome = System.getenv("ANDROID_HOME")
|
|
224
|
+
?: System.getenv("ANDROID_SDK_ROOT")
|
|
225
|
+
if (androidHome.isNullOrBlank()) {
|
|
226
|
+
errors += "ANDROID_HOME environment variable not set"
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (errors.isNotEmpty()) {
|
|
230
|
+
throw GradleException(
|
|
231
|
+
buildString {
|
|
232
|
+
appendLine("❌ Build environment validation failed:")
|
|
233
|
+
errors.forEach { appendLine(" • $it") }
|
|
234
|
+
appendLine()
|
|
235
|
+
appendLine("Fix each issue above and try again.")
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
println("✅ Build environment validation passed")
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
target.tasks.configureEach {
|
|
245
|
+
if (name == "preBuild") {
|
|
246
|
+
dependsOn("validateEnvironment")
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## local.properties Validation
|
|
256
|
+
|
|
257
|
+
```kotlin
|
|
258
|
+
// root build.gradle.kts
|
|
259
|
+
// ✅ Validate local.properties exists and has required keys
|
|
260
|
+
tasks.register("validateLocalProperties") {
|
|
261
|
+
group = "verification"
|
|
262
|
+
doLast {
|
|
263
|
+
val localProps = file("local.properties")
|
|
264
|
+
if (!localProps.exists()) {
|
|
265
|
+
throw GradleException(
|
|
266
|
+
"""
|
|
267
|
+
❌ local.properties not found.
|
|
268
|
+
|
|
269
|
+
Fix: Create local.properties in the project root with:
|
|
270
|
+
sdk.dir=/path/to/your/android/sdk
|
|
271
|
+
|
|
272
|
+
Android Studio creates this automatically — open the project in AS.
|
|
273
|
+
""".trimIndent()
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
val props = java.util.Properties().apply {
|
|
278
|
+
load(localProps.inputStream())
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (props.getProperty("sdk.dir").isNullOrBlank()) {
|
|
282
|
+
throw GradleException(
|
|
283
|
+
"""
|
|
284
|
+
❌ sdk.dir not set in local.properties.
|
|
285
|
+
|
|
286
|
+
Fix: Add to local.properties:
|
|
287
|
+
sdk.dir=/Users/yourname/Library/Android/sdk
|
|
288
|
+
""".trimIndent()
|
|
289
|
+
)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Anti-Patterns
|
|
298
|
+
|
|
299
|
+
- Validation that runs too late — after expensive tasks have already started
|
|
300
|
+
- Cryptic error messages — "SDK not found" without telling where it looked
|
|
301
|
+
- Validating everything on every build — slow, check only what's needed
|
|
302
|
+
- Not validating in CI — environment assumption mismatches cause mysterious failures
|
|
303
|
+
- Hardcoding paths in validation — use environment variables and standard locations
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Related Skills
|
|
308
|
+
- `build-orchestration` — task ordering and build pipeline
|
|
309
|
+
- `gradle` — Gradle task configuration
|
|
310
|
+
- `build-variant` — validating per-variant requirements
|
|
311
|
+
- `ci-cd` — environment setup in CI pipeline
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-cache
|
|
3
|
+
description: >
|
|
4
|
+
Gradle build cache setup for local and remote caching in Android projects.
|
|
5
|
+
Load this skill when configuring build cache, speeding up CI builds,
|
|
6
|
+
sharing cached outputs across team members, or debugging cache misses.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Build Cache
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Gradle Build Cache stores task outputs (compiled classes, resources, generated code) and reuses them when the same task runs again with identical inputs. Local cache helps individual developers; remote cache shares outputs across the whole team and CI — dramatically reducing build times.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- Local cache is **always worth enabling** — zero configuration cost, significant speedup
|
|
19
|
+
- Remote cache requires a server — Gradle Enterprise, self-hosted, or GitHub Actions cache
|
|
20
|
+
- Cache keys are computed from task **inputs** — any input change = cache miss
|
|
21
|
+
- **Configuration cache** and **build cache** are complementary but different
|
|
22
|
+
- Never cache tasks with **non-deterministic outputs** — timestamps, random values
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Local Cache Setup
|
|
27
|
+
|
|
28
|
+
```properties
|
|
29
|
+
# gradle.properties
|
|
30
|
+
org.gradle.caching=true # ✅ enable local build cache
|
|
31
|
+
org.gradle.parallel=true # parallel execution benefits from cache
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```kotlin
|
|
35
|
+
// settings.gradle.kts — explicit local cache config (optional)
|
|
36
|
+
buildCache {
|
|
37
|
+
local {
|
|
38
|
+
isEnabled = true
|
|
39
|
+
directory = File(rootDir, ".gradle/build-cache")
|
|
40
|
+
removeUnusedEntriesAfterDays = 30
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Remote Cache Setup
|
|
48
|
+
|
|
49
|
+
### Gradle Enterprise / Develocity
|
|
50
|
+
|
|
51
|
+
```kotlin
|
|
52
|
+
// settings.gradle.kts
|
|
53
|
+
plugins {
|
|
54
|
+
id("com.gradle.develocity") version "3.18"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
develocity {
|
|
58
|
+
buildScan {
|
|
59
|
+
termsOfUseUrl = "https://gradle.com/terms-of-service"
|
|
60
|
+
termsOfUseAgree = "yes"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
buildCache {
|
|
65
|
+
local { isEnabled = true }
|
|
66
|
+
remote<HttpBuildCache> {
|
|
67
|
+
url = uri("https://gradle-cache.example.com/cache/")
|
|
68
|
+
credentials {
|
|
69
|
+
username = System.getenv("GRADLE_CACHE_USERNAME")
|
|
70
|
+
password = System.getenv("GRADLE_CACHE_PASSWORD")
|
|
71
|
+
}
|
|
72
|
+
isPush = System.getenv("CI") == "true" // only CI pushes to remote
|
|
73
|
+
isEnabled = true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### GitHub Actions Cache (Free Alternative)
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
# .github/workflows/build.yml
|
|
82
|
+
- name: Setup Gradle
|
|
83
|
+
uses: gradle/actions/setup-gradle@v3
|
|
84
|
+
with:
|
|
85
|
+
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
|
86
|
+
# Automatically caches ~/.gradle and build outputs
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Verifying Cache Effectiveness
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# ✅ Run build twice — second should show FROM-CACHE
|
|
95
|
+
./gradlew assembleDebug
|
|
96
|
+
./gradlew clean
|
|
97
|
+
./gradlew assembleDebug
|
|
98
|
+
# Expected: :app:compileDebugKotlin FROM-CACHE
|
|
99
|
+
|
|
100
|
+
# ✅ Check cache hit rate
|
|
101
|
+
./gradlew assembleDebug --build-cache --info 2>&1 | grep "FROM-CACHE\|UP-TO-DATE\|EXECUTED"
|
|
102
|
+
|
|
103
|
+
# ✅ Build scan for detailed cache analysis
|
|
104
|
+
./gradlew assembleDebug --scan
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Cache Miss Debugging
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# ✅ Find why a task isn't cached
|
|
113
|
+
./gradlew assembleDebug --info 2>&1 | grep -A5 "compileDebugKotlin"
|
|
114
|
+
# Look for: "Input property ... has changed"
|
|
115
|
+
|
|
116
|
+
# ✅ Task input fingerprint
|
|
117
|
+
./gradlew assembleDebug --rerun-tasks # force all tasks to run
|
|
118
|
+
# Then compare with cached run inputs
|
|
119
|
+
|
|
120
|
+
# Common cache miss causes:
|
|
121
|
+
# - Absolute paths in task inputs
|
|
122
|
+
# - Timestamps in generated files
|
|
123
|
+
# - Environment variables not declared as inputs
|
|
124
|
+
# - Non-deterministic code generation
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Making Custom Tasks Cacheable
|
|
130
|
+
|
|
131
|
+
```kotlin
|
|
132
|
+
// ✅ Declare task as cacheable with proper input/output declarations
|
|
133
|
+
@CacheableTask
|
|
134
|
+
abstract class GenerateApiTask : DefaultTask() {
|
|
135
|
+
|
|
136
|
+
@get:InputFiles
|
|
137
|
+
@get:PathSensitive(PathSensitivity.RELATIVE) // ✅ relative paths = cache-portable
|
|
138
|
+
abstract val inputSpecs: ConfigurableFileCollection
|
|
139
|
+
|
|
140
|
+
@get:Input
|
|
141
|
+
abstract val apiVersion: Property<String>
|
|
142
|
+
|
|
143
|
+
@get:OutputDirectory
|
|
144
|
+
abstract val outputDir: DirectoryProperty
|
|
145
|
+
|
|
146
|
+
@TaskAction
|
|
147
|
+
fun generate() {
|
|
148
|
+
val output = outputDir.get().asFile
|
|
149
|
+
output.mkdirs()
|
|
150
|
+
inputSpecs.forEach { spec ->
|
|
151
|
+
// generate code from spec
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ✅ Register
|
|
157
|
+
tasks.register<GenerateApiTask>("generateApi") {
|
|
158
|
+
inputSpecs.from(fileTree("src/api/specs"))
|
|
159
|
+
apiVersion.set("v2")
|
|
160
|
+
outputDir.set(layout.buildDirectory.dir("generated/api"))
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Path Sensitivity
|
|
167
|
+
|
|
168
|
+
```kotlin
|
|
169
|
+
// ✅ Use PathSensitivity to make inputs cache-portable
|
|
170
|
+
@get:InputFiles
|
|
171
|
+
@get:PathSensitive(PathSensitivity.RELATIVE) // only relative path matters
|
|
172
|
+
abstract val sourceFiles: ConfigurableFileCollection
|
|
173
|
+
|
|
174
|
+
@get:InputFiles
|
|
175
|
+
@get:PathSensitive(PathSensitivity.NAME_ONLY) // only filename matters
|
|
176
|
+
abstract val resourceFiles: ConfigurableFileCollection
|
|
177
|
+
|
|
178
|
+
@get:InputFiles
|
|
179
|
+
@get:PathSensitive(PathSensitivity.NONE) // only content matters, not path
|
|
180
|
+
abstract val configFiles: ConfigurableFileCollection
|
|
181
|
+
|
|
182
|
+
// ❌ Absolute path (default) — cache misses on different machines
|
|
183
|
+
@get:InputFiles
|
|
184
|
+
abstract val files: ConfigurableFileCollection // absolute path = machine-specific
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## CI Cache Configuration
|
|
190
|
+
|
|
191
|
+
```yaml
|
|
192
|
+
# .github/workflows/build.yml
|
|
193
|
+
jobs:
|
|
194
|
+
build:
|
|
195
|
+
runs-on: ubuntu-latest
|
|
196
|
+
steps:
|
|
197
|
+
- uses: actions/checkout@v4
|
|
198
|
+
|
|
199
|
+
- uses: actions/setup-java@v4
|
|
200
|
+
with:
|
|
201
|
+
distribution: temurin
|
|
202
|
+
java-version: 17
|
|
203
|
+
|
|
204
|
+
# ✅ Cache Gradle dependencies and build outputs
|
|
205
|
+
- name: Setup Gradle
|
|
206
|
+
uses: gradle/actions/setup-gradle@v3
|
|
207
|
+
with:
|
|
208
|
+
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
|
209
|
+
|
|
210
|
+
- name: Build
|
|
211
|
+
run: ./gradlew assembleDebug
|
|
212
|
+
env:
|
|
213
|
+
CI: true
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Anti-Patterns
|
|
219
|
+
|
|
220
|
+
- Not enabling local cache — free speedup left on the table
|
|
221
|
+
- Absolute paths in task inputs — cache miss on every machine
|
|
222
|
+
- Non-deterministic task outputs — cache entries become stale/incorrect
|
|
223
|
+
- Pushing to remote cache from developer machines — pollutes cache with unverified outputs
|
|
224
|
+
- `@CacheableTask` without `@PathSensitive` on file inputs — over-broad cache invalidation
|
|
225
|
+
- Timestamps or build numbers as task inputs — always a cache miss
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Related Skills
|
|
230
|
+
- `incremental-build` — incremental task execution
|
|
231
|
+
- `gradle` — Gradle build configuration
|
|
232
|
+
- `build-orchestration` — remote cache in multi-project builds
|
|
233
|
+
- `ci-cd` — cache configuration in CI pipeline
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-flavor-strategy
|
|
3
|
+
description: >
|
|
4
|
+
Strategy for designing product flavors and build variant structure.
|
|
5
|
+
Load this skill when deciding how many flavors to create, what each
|
|
6
|
+
flavor dimension should represent, and how to keep variants manageable.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Build Flavor Strategy
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Product flavors define what variant of the app is built — different environments, tiers, or feature sets. A poorly designed flavor structure leads to exponential variant count, complex CI pipelines, and unmaintainable build files. This skill defines how to design flavor structure deliberately.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- Fewer flavors = simpler CI, faster builds, less maintenance
|
|
19
|
+
- Each flavor dimension should represent a **clearly different product concern**
|
|
20
|
+
- Prefer **build types** for environment differences — flavors for product differences
|
|
21
|
+
- Avoid flavors for things that can be done with **feature flags** or **Remote Config**
|
|
22
|
+
- Document the variant matrix — every combination must have a clear purpose
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Common Flavor Patterns
|
|
27
|
+
|
|
28
|
+
### Pattern 1: Environment Only (Most Common)
|
|
29
|
+
|
|
30
|
+
```kotlin
|
|
31
|
+
// ✅ Use when you need different backend environments
|
|
32
|
+
flavorDimensions += "environment"
|
|
33
|
+
|
|
34
|
+
productFlavors {
|
|
35
|
+
create("dev") {
|
|
36
|
+
dimension = "environment"
|
|
37
|
+
applicationIdSuffix = ".dev"
|
|
38
|
+
buildConfigField("String", "API_URL", "\"https://dev.api.example.com/\"")
|
|
39
|
+
}
|
|
40
|
+
create("staging") {
|
|
41
|
+
dimension = "environment"
|
|
42
|
+
applicationIdSuffix = ".staging"
|
|
43
|
+
buildConfigField("String", "API_URL", "\"https://staging.api.example.com/\"")
|
|
44
|
+
}
|
|
45
|
+
create("prod") {
|
|
46
|
+
dimension = "environment"
|
|
47
|
+
buildConfigField("String", "API_URL", "\"https://api.example.com/\"")
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Variants: devDebug, devRelease, stagingDebug, stagingRelease, prodDebug, prodRelease
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pattern 2: Tier Only
|
|
54
|
+
|
|
55
|
+
```kotlin
|
|
56
|
+
// ✅ Use when distributing free vs paid version of same app
|
|
57
|
+
flavorDimensions += "tier"
|
|
58
|
+
|
|
59
|
+
productFlavors {
|
|
60
|
+
create("free") {
|
|
61
|
+
dimension = "tier"
|
|
62
|
+
buildConfigField("Boolean", "IS_PREMIUM", "false")
|
|
63
|
+
}
|
|
64
|
+
create("premium") {
|
|
65
|
+
dimension = "tier"
|
|
66
|
+
buildConfigField("Boolean", "IS_PREMIUM", "true")
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Pattern 3: Two Dimensions (Use Sparingly)
|
|
72
|
+
|
|
73
|
+
```kotlin
|
|
74
|
+
// ✅ Only when both dimensions represent truly independent product axes
|
|
75
|
+
flavorDimensions += listOf("environment", "tier")
|
|
76
|
+
|
|
77
|
+
// Results in: devFreeDebug, devPremiumDebug, prodFreeRelease, prodPremiumRelease...
|
|
78
|
+
// 8 variants — manageable if most are excluded
|
|
79
|
+
|
|
80
|
+
android {
|
|
81
|
+
variantFilter {
|
|
82
|
+
// Only build dev+free and prod+premium — others don't make sense
|
|
83
|
+
val env = flavors.find { it.dimension == "environment" }?.name
|
|
84
|
+
val tier = flavors.find { it.dimension == "tier" }?.name
|
|
85
|
+
if ((env == "dev" && tier == "premium") || (env == "prod" && tier == "free")) {
|
|
86
|
+
ignore = true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Environment via Build Type vs Flavor
|
|
95
|
+
|
|
96
|
+
```kotlin
|
|
97
|
+
// ✅ Environment differences → Build Types (simpler)
|
|
98
|
+
// Use when dev/staging/prod differ only in config (URL, keys)
|
|
99
|
+
buildTypes {
|
|
100
|
+
debug { buildConfigField("String", "API_URL", "\"https://dev.api.com/\"") }
|
|
101
|
+
create("staging") { buildConfigField("String", "API_URL", "\"https://staging.api.com/\"") }
|
|
102
|
+
release { buildConfigField("String", "API_URL", "\"https://api.com/\"") }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ✅ Environment via Flavors
|
|
106
|
+
// Use when environments need different:
|
|
107
|
+
// - Firebase projects (different google-services.json)
|
|
108
|
+
// - App IDs (can install dev and prod side by side)
|
|
109
|
+
// - Dependencies (dev has debug tools, prod doesn't)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Decision Framework
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Do you need separate Firebase projects per environment?
|
|
118
|
+
YES → Use flavors (each flavor can have its own google-services.json)
|
|
119
|
+
NO → Use build types
|
|
120
|
+
|
|
121
|
+
Do you need to install dev and prod side by side?
|
|
122
|
+
YES → Use flavors (different applicationId per flavor)
|
|
123
|
+
NO → Use build types + applicationIdSuffix
|
|
124
|
+
|
|
125
|
+
Do you have multiple product tiers (free/paid)?
|
|
126
|
+
YES → Add a tier flavor dimension
|
|
127
|
+
NO → Use feature flags or Remote Config
|
|
128
|
+
|
|
129
|
+
Would Remote Config solve this without a new flavor?
|
|
130
|
+
YES → Use Remote Config — avoid the flavor
|
|
131
|
+
NO → Proceed with flavor
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Variant Matrix Documentation
|
|
137
|
+
|
|
138
|
+
```markdown
|
|
139
|
+
<!-- Always document the variant matrix in the project -->
|
|
140
|
+
|
|
141
|
+
## Build Variant Matrix
|
|
142
|
+
|
|
143
|
+
| Variant | Purpose | Used By |
|
|
144
|
+
|---------|---------|---------|
|
|
145
|
+
| devDebug | Local development | Developers |
|
|
146
|
+
| devRelease | Dev build for QA | QA team |
|
|
147
|
+
| stagingRelease | Pre-prod testing | QA + stakeholders |
|
|
148
|
+
| prodRelease | Production release | Play Store |
|
|
149
|
+
|
|
150
|
+
### Excluded Variants
|
|
151
|
+
- stagingDebug — unnecessary (staging = pre-prod, always release config)
|
|
152
|
+
- prodDebug — security risk (debuggable prod build)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Anti-Patterns
|
|
158
|
+
|
|
159
|
+
- More than 2 flavor dimensions — variant count explodes (3 dims × 2 values = 8+ variants)
|
|
160
|
+
- Using flavors for feature flags — use Remote Config instead, no rebuild needed
|
|
161
|
+
- No variant filtering — building all combinations wastes CI time
|
|
162
|
+
- Flavors with overlapping purposes with build types — pick one
|
|
163
|
+
- Not documenting the variant matrix — developers don't know which variant to use
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Related Skills
|
|
168
|
+
- `build-variant` — implementing build types and flavors
|
|
169
|
+
- `gradle` — Gradle build configuration
|
|
170
|
+
- `remote-config` — replacing flavor-based feature switching
|
|
171
|
+
- `ci-cd` — building specific variants in CI pipeline
|