A comprehensive bash script for managing Android keystores, automating the signing configuration, and handling GitHub secrets.
The Android Keystore Manager automates the process of creating, storing, and configuring Android app signing keys. It can:
- Generate debug (ORIGINAL) and release (UPLOAD) keystores with custom information
- Securely store keystore configurations and passwords
- Update fastlane and Gradle build configurations automatically
- Manage GitHub secrets for CI/CD pipelines
- Provide a clear view of all managed secrets
This tool is especially useful for development teams that need to maintain consistent signing configurations across multiple environments and CI/CD pipelines.
-
Make it executable:
chmod +x keystore-manager.sh
-
Ensure you have the required dependencies:
- Java Development Kit (JDK) for keytool
- GitHub CLI (gh) for GitHub secrets management
- Dual Keystore Management: Creates and maintains both debug (ORIGINAL) and release (UPLOAD) keystores
- Automated Configuration: Updates Gradle and fastlane configurations automatically
- GitHub Secrets Integration: Easily manage secrets for CI/CD pipelines
- Secret Visualization: View all secrets in a nicely formatted table
- Secure Storage: Keystores are stored in a separate 'keystores' directory
- Flexible Certificate Information: Support for custom company and organization details
./keystore-manager.sh generateThis command:
- Creates the
keystoresdirectory if it doesn't exist - Generates both ORIGINAL (debug) and UPLOAD (release) keystores
- Updates
secrets.envwith base64-encoded keystores - Updates fastlane config in
fastlane-config/android_config.rb - Updates Gradle config in
cmp-android/build.gradle.kts
./keystore-manager.sh viewDisplays all secrets from secrets.env in a nicely formatted table. For multiline values (like
base64-encoded keystores), it shows [MULTILINE VALUE] instead of the actual content.
./keystore-manager.sh add --repo=username/repoAdds all secrets from secrets.env to the specified GitHub repository (excludes certificate
information and local configuration values).
You can target a specific GitHub environment:
./keystore-manager.sh add --repo=username/repo --env=production./keystore-manager.sh list --repo=username/repoLists all secrets currently stored in the specified GitHub repository.
./keystore-manager.sh delete --repo=username/repo --name=SECRET_NAMEDeletes a specific secret from the GitHub repository.
./keystore-manager.sh delete-all --repo=username/repoDeletes all secrets defined in secrets.env from the GitHub repository (excluding certificate
information and local configuration values).
To also delete excluded secrets:
./keystore-manager.sh delete-all --repo=username/repo --include-excludedThe secrets.env file contains all the keystore and secret information. Example structure:
# GitHub Secrets Environment File
# Format: KEY=VALUE (use quotes for values with spaces)
# Use <<EOF and EOF for multiline values
# ORIGINAL Keystore credentials (Debug/Development)
ORIGINAL_KEYSTORE_FILE_PASSWORD=original_keystore_password
ORIGINAL_KEYSTORE_ALIAS=original_key
ORIGINAL_KEYSTORE_ALIAS_PASSWORD=original_key_password
# UPLOAD Keystore credentials (Release/Production)
UPLOAD_KEYSTORE_FILE_PASSWORD=upload_keystore_password
UPLOAD_KEYSTORE_ALIAS=upload_key
UPLOAD_KEYSTORE_ALIAS_PASSWORD=upload_key_password
# Local keystore generation settings (not sent to GitHub)
ORIGINAL_KEYSTORE_NAME=original_keystore.keystore
UPLOAD_KEYSTORE_NAME=upload_keystore.keystore
VALIDITY=25
KEYALG=RSA
KEYSIZE=2048
OVERWRITE=false
# Certificate information (Distinguished Name)
# IMPORTANT: Use quotes for values with spaces
COMPANY_NAME="Devikon Inc."
DEPARTMENT="Mobile Development"
ORGANIZATION="Devikon Inc."
CITY="Kolkata"
STATE="West Bengal"
COUNTRY=IN
# Base64 encoded keystores (added by the script)
ORIGINAL_KEYSTORE_FILE<<EOF
MIICXAIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+...
EOF
UPLOAD_KEYSTORE_FILE<<EOF
MIICXQIBAAKBgQDASAEDHGOPsVNlHCYi6ofmoEOdG+7xDRa...
EOF
# Other secrets
API_KEY=your_api_key_here
The script automatically updates several configuration files:
-
fastlane-config/android_config.rb:
module FastlaneConfig module AndroidConfig STORE_CONFIG = { default_store_file: "upload.keystore", default_store_password: "upload_keystore_password", default_key_alias: "upload_key", default_key_password: "upload_key_password" } # ... other configurations ... end end
-
cmp-android/build.gradle.kts:
android { signingConfigs { create("release") { storeFile = file(System.getenv("KEYSTORE_PATH") ?: "../keystores/upload.keystore") storePassword = System.getenv("KEYSTORE_PASSWORD") ?: "upload_keystore_password" keyAlias = System.getenv("KEYSTORE_ALIAS") ?: "upload_key" keyPassword = System.getenv("KEYSTORE_ALIAS_PASSWORD") ?: "upload_key_password" enableV1Signing = true enableV2Signing = true } } }
After running the script, you should have the following directory structure:
project/
├── keystore-manager.sh # The main script
├── secrets.env # Environment file with secrets
├── keystores/ # Directory containing keystore files
│ ├── original.keystore # Debug/development keystore
│ └── upload.keystore # Release/production keystore
├── fastlane-config/ # Fastlane configuration directory
│ └── android_config.rb # Fastlane configuration for Android
└── cmp-android/
└── build.gradle.kts # Gradle build file with signing config
-
Error: keytool command not found
- Make sure you have JDK installed and keytool is in your PATH
- Install JDK if needed:
sudo apt install default-jdk
-
GitHub CLI (gh) is not installed
- Install GitHub CLI: https://cli.github.com/manual/installation
- Log in with:
gh auth login
-
Error with multiline values
- Ensure all multiline blocks in
secrets.envare properly closed withEOF - Check for proper formatting:
KEY<<EOFandEOFshould be on separate lines
- Ensure all multiline blocks in
-
Configuration files not updated
- Check if the files exist at the expected paths
- Verify file permissions:
chmod +rw path/to/file
To verify that a keystore was generated correctly:
keytool -list -v -keystore keystores/original.keystore -storepass your_passwordQ: What's the difference between ORIGINAL and UPLOAD keystores?
A: ORIGINAL (debug) keystores are for development/testing, while UPLOAD (release) keystores are for
production builds that get published to the Play Store.
Q: Will my certificate information be uploaded to GitHub?
A: No. Certificate information (COMPANY_NAME, DEPARTMENT, etc.) is excluded from GitHub secrets by
default.
Q: How do I change the keystore passwords?
A: Edit the values in secrets.env and run ./keystore-manager.sh generate with the OVERWRITE
option set to true.
Q: Can I use this script in CI/CD pipelines?
A: Yes, the script is designed to work in both interactive and automated environments. For CI/CD,
you would typically use the add command to upload secrets.
Q: How are my keystore files secured?
A: Keystore files are stored in the keystores directory and never directly uploaded to GitHub.
Only base64-encoded versions are added as GitHub secrets.
Q: Do I need to manually update Gradle and fastlane configurations?
A: No, the script automatically updates both configurations when you run the generate command.