feat(release): add keystore setup + Play Store deployment guide
- Generated PKCS12 keystore (production-release.jks, alias: upload) - Updated GitHub secrets: KEYSTORE_BASE64/PASSWORD/KEY_ALIAS/KEY_PASSWORD - Added keystores/ to .gitignore - Added distribution/PLAY_CONSOLE_SETUP.md: step-by-step guide to create GCP service account, link to Play Console, first manual upload, and configure PLAY_STORE_SERVICE_ACCOUNT_JSON secret - Installed android-playstore-setup skills (sub-skills) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
294
.agents/skills/android-keystore-generation/SKILL.md
Normal file
294
.agents/skills/android-keystore-generation/SKILL.md
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
name: android-keystore-generation
|
||||
description: Generate production and local development keystores for Android release signing
|
||||
category: android
|
||||
version: 1.0.0
|
||||
inputs:
|
||||
- project_path: Path to Android project
|
||||
- organization: Organization name for certificate
|
||||
- country_code: 2-letter country code (optional)
|
||||
outputs:
|
||||
- keystores/production-release.jks
|
||||
- keystores/local-dev-release.jks
|
||||
- keystores/KEYSTORE_INFO.txt
|
||||
verify: "ls keystores/*.jks && cat keystores/KEYSTORE_INFO.txt"
|
||||
---
|
||||
|
||||
# Android Keystore Generation
|
||||
|
||||
Generates dual keystores for Android release signing: production (CI/CD only) and local development.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- JDK installed (`keytool` command available)
|
||||
- Write access to project directory
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| project_path | Yes | . | Android project root |
|
||||
| organization | Yes | - | Organization name for certificate DN |
|
||||
| country_code | No | US | 2-letter country code |
|
||||
|
||||
## Process
|
||||
|
||||
### Organization Name
|
||||
|
||||
**ALWAYS prompt the user, but provide the auto-detected value as default.**
|
||||
|
||||
**Step 1: Detect organization from package name**
|
||||
```bash
|
||||
# Extract package name from build.gradle.kts
|
||||
PACKAGE=$(grep "applicationId" app/build.gradle.kts | sed 's/.*"\(.*\)".*/\1/')
|
||||
echo "Package: $PACKAGE"
|
||||
|
||||
# Extract second segment: com.{ORG}.app → ORG
|
||||
ORG=$(echo $PACKAGE | cut -d. -f2)
|
||||
echo "Detected organization: $ORG"
|
||||
```
|
||||
|
||||
**Step 2: MANDATORY - Ask the user for confirmation**
|
||||
|
||||
⛔ **DO NOT SKIP THIS PROMPT**
|
||||
|
||||
Ask the user:
|
||||
> "The detected organization name is **{ORG}**.
|
||||
> Press Enter to use this, or type a different name:"
|
||||
|
||||
Wait for user response. Use their input if provided, otherwise use the detected default.
|
||||
|
||||
**Step 3: Store the confirmed organization name**
|
||||
```bash
|
||||
ORGANIZATION="{confirmed_org_name}"
|
||||
echo "Using organization: $ORGANIZATION"
|
||||
```
|
||||
|
||||
**Why this matters:** The organization appears in the certificate's Distinguished Name.
|
||||
While it doesn't affect app functionality, users may want to customize it.
|
||||
|
||||
### Password Generation Options
|
||||
|
||||
**Choose one option for keystore passwords:**
|
||||
|
||||
#### Option 1 (Recommended): User-Provided Password
|
||||
|
||||
Ask the user to provide a password for the production keystore:
|
||||
|
||||
> "Please enter a password for the production keystore (minimum 12 characters, mix of letters, numbers, and symbols):"
|
||||
|
||||
**Security benefits:**
|
||||
- Password never visible to the agent during the conversation
|
||||
- User has complete control over password strength and storage
|
||||
- Reduces risk of password exposure in logs or conversation history
|
||||
|
||||
**Instructions for user:**
|
||||
```bash
|
||||
# User will run keytool command manually with their chosen password
|
||||
# Example:
|
||||
keytool -genkeypair -v \
|
||||
-keystore keystores/production-release.jks \
|
||||
-storetype PKCS12 \
|
||||
-alias upload \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-validity 10000 \
|
||||
-storepass "YOUR_PASSWORD_HERE" \
|
||||
-keypass "YOUR_PASSWORD_HERE" \
|
||||
-dname "CN=Android Release, OU=Android, O={ORGANIZATION}, C=US"
|
||||
```
|
||||
|
||||
#### Option 2: Generated Password
|
||||
|
||||
If the user prefers a generated password, use the automated generation steps below.
|
||||
|
||||
**Note:** The agent will have access to this password during the session. The password will be stored in temporary files and KEYSTORE_INFO.txt.
|
||||
|
||||
> "Would you like me to generate a secure password? (The agent will see this password during generation)"
|
||||
|
||||
If yes, proceed with the automated generation steps.
|
||||
|
||||
### Step 1: Create Keystores Directory
|
||||
|
||||
```bash
|
||||
mkdir -p keystores
|
||||
```
|
||||
|
||||
### Step 2: Generate Production Keystore
|
||||
|
||||
**SECURITY:** This keystore is for CI/CD only. Never use locally.
|
||||
|
||||
**⚠️ IMPORTANT: Run each command in a SEPARATE bash call. Do NOT combine commands.**
|
||||
|
||||
**Step 2a: Generate password and save to file**
|
||||
```bash
|
||||
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/prod_password.txt
|
||||
```
|
||||
|
||||
**Step 2b: Read and display the password**
|
||||
```bash
|
||||
cat /tmp/prod_password.txt
|
||||
```
|
||||
📝 **Copy this password now** - you'll need it for the keytool command and KEYSTORE_INFO.txt
|
||||
|
||||
**Step 2c: Generate the keystore**
|
||||
|
||||
Replace `{PASSWORD}` with the password from Step 2b, and `{ORGANIZATION}` with the confirmed organization name:
|
||||
|
||||
```bash
|
||||
keytool -genkeypair -v \
|
||||
-keystore keystores/production-release.jks \
|
||||
-storetype PKCS12 \
|
||||
-alias upload \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-validity 10000 \
|
||||
-storepass "{PASSWORD}" \
|
||||
-keypass "{PASSWORD}" \
|
||||
-dname "CN=Android Release, OU=Android, O={ORGANIZATION}, C=US"
|
||||
```
|
||||
|
||||
**If keytool prompts for confirmation**, type `yes` and press Enter.
|
||||
|
||||
**Step 2d: Verify keystore was created**
|
||||
```bash
|
||||
ls -la keystores/production-release.jks
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
-rw------- 1 user staff 2557 Dec 11 10:30 keystores/production-release.jks
|
||||
```
|
||||
|
||||
### Step 3: Generate Local Development Keystore
|
||||
|
||||
**⚠️ IMPORTANT: Run each command in a SEPARATE bash call. Do NOT combine commands.**
|
||||
|
||||
**Step 3a: Generate password and save to file**
|
||||
```bash
|
||||
openssl rand -base64 24 | tr -d '/+=' | head -c 24 > /tmp/local_password.txt
|
||||
```
|
||||
|
||||
**Step 3b: Read and display the password**
|
||||
```bash
|
||||
cat /tmp/local_password.txt
|
||||
```
|
||||
📝 **Copy this password now** - you'll need it for the keytool command and KEYSTORE_INFO.txt
|
||||
|
||||
**Step 3c: Generate the keystore**
|
||||
|
||||
Replace `{PASSWORD}` with the password from Step 3b:
|
||||
|
||||
```bash
|
||||
keytool -genkeypair -v \
|
||||
-keystore keystores/local-dev-release.jks \
|
||||
-storetype PKCS12 \
|
||||
-alias local-dev \
|
||||
-keyalg RSA \
|
||||
-keysize 2048 \
|
||||
-validity 10000 \
|
||||
-storepass "{PASSWORD}" \
|
||||
-keypass "{PASSWORD}" \
|
||||
-dname "CN=Local Development, OU=Development, O=Local, C=US"
|
||||
```
|
||||
|
||||
**If keytool prompts for confirmation**, type `yes` and press Enter.
|
||||
|
||||
**Step 3d: Verify keystore was created**
|
||||
```bash
|
||||
ls -la keystores/local-dev-release.jks
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
```
|
||||
-rw------- 1 user staff 2557 Dec 11 10:30 keystores/local-dev-release.jks
|
||||
```
|
||||
|
||||
### Step 4: Create Credentials File
|
||||
|
||||
```bash
|
||||
cat > keystores/KEYSTORE_INFO.txt << EOF
|
||||
Production Keystore
|
||||
===================
|
||||
File: production-release.jks
|
||||
Alias: upload
|
||||
Store Password: $PROD_PASSWORD
|
||||
Key Password: $PROD_PASSWORD (same as store - PKCS12 requirement)
|
||||
|
||||
⚠️ SECURITY: CI/CD ONLY - Never use on developer machines
|
||||
|
||||
GitHub Secrets:
|
||||
SIGNING_KEY_STORE_BASE64: $(base64 -w 0 keystores/production-release.jks 2>/dev/null || base64 -i keystores/production-release.jks)
|
||||
SIGNING_KEY_ALIAS: upload
|
||||
SIGNING_STORE_PASSWORD: $PROD_PASSWORD
|
||||
SIGNING_KEY_PASSWORD: $PROD_PASSWORD
|
||||
|
||||
---
|
||||
|
||||
Local Development Keystore
|
||||
==========================
|
||||
File: local-dev-release.jks
|
||||
Alias: local-dev
|
||||
Store Password: $LOCAL_PASSWORD
|
||||
Key Password: $LOCAL_PASSWORD
|
||||
|
||||
Add to ~/.gradle/gradle.properties:
|
||||
SIGNING_KEY_STORE_PATH=$(pwd)/keystores/local-dev-release.jks
|
||||
SIGNING_KEY_ALIAS=local-dev
|
||||
SIGNING_STORE_PASSWORD=$LOCAL_PASSWORD
|
||||
SIGNING_KEY_PASSWORD=$LOCAL_PASSWORD
|
||||
EOF
|
||||
```
|
||||
|
||||
### Step 5: Update .gitignore
|
||||
|
||||
```bash
|
||||
# Add to .gitignore if not present
|
||||
grep -q "keystores/" .gitignore 2>/dev/null || echo "keystores/" >> .gitignore
|
||||
grep -q "*.jks" .gitignore 2>/dev/null || echo "*.jks" >> .gitignore
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
**MANDATORY:** Run these commands:
|
||||
|
||||
```bash
|
||||
# Verify keystores exist
|
||||
ls -la keystores/*.jks
|
||||
|
||||
# Verify credentials documented
|
||||
cat keystores/KEYSTORE_INFO.txt
|
||||
|
||||
# Verify gitignored
|
||||
grep "keystores" .gitignore
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
- Two .jks files in keystores/
|
||||
- KEYSTORE_INFO.txt with passwords
|
||||
- keystores/ in .gitignore
|
||||
|
||||
## Outputs
|
||||
|
||||
| Output | Location | Description |
|
||||
|--------|----------|-------------|
|
||||
| Production keystore | keystores/production-release.jks | For CI/CD only |
|
||||
| Local keystore | keystores/local-dev-release.jks | For local testing |
|
||||
| Credentials | keystores/KEYSTORE_INFO.txt | Passwords and setup info |
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "keytool: command not found"
|
||||
**Cause:** JDK not installed or not in PATH
|
||||
**Fix:** Install JDK 17: `brew install openjdk@17` (macOS) or `apt install openjdk-17-jdk` (Linux)
|
||||
|
||||
### "openssl: command not found"
|
||||
**Cause:** OpenSSL not installed
|
||||
**Fix:** Use alternative password generation: `head -c 24 /dev/urandom | base64`
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
- [ ] `keystores/production-release.jks` exists
|
||||
- [ ] `keystores/local-dev-release.jks` exists
|
||||
- [ ] `keystores/KEYSTORE_INFO.txt` exists with passwords
|
||||
- [ ] `keystores/` is in `.gitignore`
|
||||
Reference in New Issue
Block a user