home / skills / hitoshura25 / claude-devtools / android-signing-config
This skill configures Android release signing using dual-source credentials from environment variables and gradle.properties to streamline CI/CD and local dev.
npx playbooks add skill hitoshura25/claude-devtools --skill android-signing-configReview the files below or copy the command above to add this skill to your agents.
---
name: android-signing-config
description: Configure Android release build signing with dual-source credentials (env vars + gradle.properties)
category: android
version: 1.0.0
inputs:
- project_path: Path to Android project
- keystores_created: Keystores must exist in keystores/ directory
outputs:
- Updated app/build.gradle.kts with signingConfigs
verify: "./gradlew assembleRelease"
---
# Android Signing Configuration
Configures release build signing with dual-source strategy: environment variables (CI/CD) and gradle.properties (local dev).
## Prerequisites
- Keystores exist in `keystores/` directory (run `android-keystore-generation` first)
- Android project with Gradle
- Kotlin DSL (build.gradle.kts)
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| project_path | Yes | . | Android project root |
## Process
### Step 1: Detect and Select Environment Variable Prefix
**Purpose:** Avoid variable name conflicts with other projects by using a project-specific prefix.
**Step 1a: Auto-detect project name**
```bash
# From settings.gradle.kts
PROJECT_NAME=$(grep "rootProject.name" settings.gradle.kts | sed 's/.*"\(.*\)".*/\1/' | tr '[:lower:]' '[:upper:]' | tr '-' '_')
echo "Detected project: $PROJECT_NAME"
```
**Step 1b: Ask user for prefix preference**
> "Choose environment variable prefix for signing configuration:
> 1. **${PROJECT_NAME}** (detected from project) - e.g., `${PROJECT_NAME}_SIGNING_KEY_STORE_PATH`
> 2. **APP** (generic) - e.g., `APP_SIGNING_KEY_STORE_PATH`
> 3. **Custom** - Enter your own prefix
>
> Select option (1/2/3):"
**Step 1c: Store selected prefix**
```bash
# Based on user selection:
# Option 1: PREFIX="$PROJECT_NAME"
# Option 2: PREFIX="APP"
# Option 3: PREFIX="{user_custom_prefix}"
echo "Using prefix: $PREFIX"
echo "Example variable: ${PREFIX}_SIGNING_KEY_STORE_PATH"
```
**GitHub Secrets to create later:**
- `${PREFIX}_SIGNING_KEY_STORE_BASE64`
- `${PREFIX}_SIGNING_KEY_ALIAS`
- `${PREFIX}_SIGNING_STORE_PASSWORD`
- `${PREFIX}_SIGNING_KEY_PASSWORD`
### Step 2: Add Signing Configuration to build.gradle.kts
Update `app/build.gradle.kts` to add signing configuration:
```kotlin
android {
namespace = "com.example.app" // Keep existing
compileSdk = 34 // Keep existing
defaultConfig {
// Keep existing config
}
// ADD THIS SECTION
signingConfigs {
create("release") {
// Use the prefix selected in Step 1 (replace {PREFIX} with actual prefix)
val prefix = "{PREFIX}" // e.g., "APP" or project-specific prefix
// Priority: environment variables (CI/CD) > gradle.properties (local dev)
val keystorePath = System.getenv("${prefix}_SIGNING_KEY_STORE_PATH")
?: project.findProperty("${prefix}_SIGNING_KEY_STORE_PATH")?.toString()
val storePass = System.getenv("${prefix}_SIGNING_STORE_PASSWORD")
?: project.findProperty("${prefix}_SIGNING_STORE_PASSWORD")?.toString()
val alias = System.getenv("${prefix}_SIGNING_KEY_ALIAS")
?: project.findProperty("${prefix}_SIGNING_KEY_ALIAS")?.toString()
val keyPass = System.getenv("${prefix}_SIGNING_KEY_PASSWORD")
?: project.findProperty("${prefix}_SIGNING_KEY_PASSWORD")?.toString()
if (keystorePath != null && storePass != null && alias != null && keyPass != null) {
storeFile = file(keystorePath)
storePassword = storePass
keyAlias = alias
// For PKCS12 keystores, storePassword and keyPassword must be identical
keyPassword = keyPass
}
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
// ProGuard config set by android-proguard-setup skill
}
}
// Validate signing config only when building release variants
tasks.matching {
it.name.matches(Regex(".*[aA]ssemble.*Release.*|.*[bB]undle.*Release.*"))
}.configureEach {
doFirst {
val releaseConfig = android.signingConfigs.getByName("release")
if (releaseConfig.storeFile == null) {
throw GradleException(
"""
Release signing not configured!
For CI/CD: Set environment variables (using prefix: $prefix):
- ${prefix}_SIGNING_KEY_STORE_PATH
- ${prefix}_SIGNING_STORE_PASSWORD
- ${prefix}_SIGNING_KEY_ALIAS
- ${prefix}_SIGNING_KEY_PASSWORD
For local development: Add to ~/.gradle/gradle.properties:
${prefix}_SIGNING_KEY_STORE_PATH=/path/to/local-dev-release.jks
${prefix}_SIGNING_STORE_PASSWORD=your-password
${prefix}_SIGNING_KEY_ALIAS=local-dev
${prefix}_SIGNING_KEY_PASSWORD=your-password
""".trimIndent()
)
}
}
}
}
```
**Detection logic:**
- Check if `signingConfigs` already exists - update if present
- Check if release `buildType` already has signingConfig - preserve other settings
- Don't modify ProGuard settings (handled by separate skill)
### Step 2: Update .gitignore
Ensure sensitive files are gitignored:
```bash
# Add to .gitignore if not present
grep -q "gradle.properties" .gitignore 2>/dev/null || echo -e "\n# Gradle properties with secrets\ngradle.properties" >> .gitignore
```
### Step 3: Configure Local Development
Ask user permission to update `~/.gradle/gradle.properties`:
```bash
# Read credentials from KEYSTORE_INFO.txt
LOCAL_PASSWORD=$(grep "Local.*Store Password:" keystores/KEYSTORE_INFO.txt | cut -d: -f2 | xargs)
PROJECT_PATH=$(pwd)
PREFIX="{PREFIX}" # Use the prefix selected in Step 1
# Add to ~/.gradle/gradle.properties (using selected prefix)
cat >> ~/.gradle/gradle.properties << EOF
# ${PROJECT_PATH} (using prefix: ${PREFIX})
${PREFIX}_SIGNING_KEY_STORE_PATH=${PROJECT_PATH}/keystores/local-dev-release.jks
${PREFIX}_SIGNING_KEY_ALIAS=local-dev
${PREFIX}_SIGNING_STORE_PASSWORD=${LOCAL_PASSWORD}
${PREFIX}_SIGNING_KEY_PASSWORD=${LOCAL_PASSWORD}
EOF
```
**Important:** Always ask permission before modifying user's global gradle.properties!
## Verification
**MANDATORY:** Run this command to verify signing works:
```bash
# Build release APK
./gradlew assembleRelease
# Verify APK exists
ls -lh app/build/outputs/apk/release/app-release.apk
# Verify APK signature (supports APK Signature Scheme v2/v3)
$ANDROID_HOME/build-tools/34.0.0/apksigner verify --verbose app/build/outputs/apk/release/app-release.apk
# Or if apksigner is in PATH:
apksigner verify --verbose app/build/outputs/apk/release/app-release.apk
```
**Expected output:**
- Release build succeeds
- APK file exists
- `apksigner` shows "Verifies" with v2/v3 scheme confirmation
## Outputs
| Output | Location | Description |
|--------|----------|-------------|
| Signing config | app/build.gradle.kts | Dual-source signing configuration |
| Local config | ~/.gradle/gradle.properties | Local dev credentials |
## Troubleshooting
### "Signing config not found"
**Cause:** gradle.properties not configured correctly
**Fix:** Verify ~/.gradle/gradle.properties has correct paths and passwords
### "Cannot find keystore file"
**Cause:** Path in gradle.properties is incorrect
**Fix:** Use absolute path: `/full/path/to/keystores/local-dev-release.jks`
### "apksigner verification fails"
**Cause:** Wrong keystore or passwords
**Fix:** Double-check credentials in KEYSTORE_INFO.txt
## Completion Criteria
- [ ] `signingConfigs.release` exists in app/build.gradle.kts
- [ ] Release buildType uses signingConfig
- [ ] `~/.gradle/gradle.properties` configured (or env vars set for CI)
- [ ] `./gradlew assembleRelease` succeeds
- [ ] `apksigner verify` confirms APK is signed (v2/v3 schemes)
This skill configures Android release signing using a dual-source strategy: CI-friendly environment variables and local development via gradle.properties. It adds a release signingConfig to app/build.gradle.kts, validates release builds, and helps manage a project-specific environment variable prefix to avoid collisions. The goal is a reproducible, secure signing setup for CI and local workflows.
The script auto-detects a project prefix from settings.gradle.kts or asks the user to choose/apply a custom prefix. It injects a signingConfigs.release block into app/build.gradle.kts that reads credentials first from environment variables and falls back to project properties (gradle.properties). It also wires the release buildType to use that signingConfig and adds pre-build validation to fail fast when signing data is missing. Optionally it can append local credentials to ~/.gradle/gradle.properties after explicit user consent.
What environment variables are required for CI?
Set PREFIX_SIGNING_KEY_STORE_PATH, PREFIX_SIGNING_STORE_PASSWORD, PREFIX_SIGNING_KEY_ALIAS, and PREFIX_SIGNING_KEY_PASSWORD (or store a base64 keystore and decode in CI).
Will this overwrite my existing signing config?
The logic updates or adds signingConfigs.release and preserves other buildType settings; it detects existing configs and avoids overwriting unrelated settings.