Files
Krow-workspace/docs/RELEASE/APK_SIGNING_SETUP.md
Achintha Isuru 8b9a58adb1 feat: Add mobile CI/CD secrets setup for APK signing
- Updated Makefile to include new command for setting up mobile CI secrets.
- Enhanced tools.mk with setup-mobile-ci-secrets target.
- Created setup-mobile-github-secrets.sh script for configuring GitHub Secrets for APK signing.
- Added APK signing implementation summary documentation.
- Created detailed APK signing setup guide.
- Added GitHub secrets checklist for easy reference.
2026-03-05 13:55:38 -05:00

8.3 KiB

APK Signing Setup for GitHub Actions

For Worker Mobile & Client Mobile Apps


📋 Overview

This document explains how to set up APK signing for automated builds in GitHub Actions. The same keystore files used in CodeMagic will be used here.


🔑 Understanding App Signing

Why Sign APKs?

  • Required by Google Play: All Android apps must be signed before distribution
  • App Identity: The signature identifies your app across updates
  • Security: Ensures the APK hasn't been tampered with

Keystore Files

Each app and environment combination has its own keystore:

Worker Mobile (Staff App):

  • krow_staff_dev.jks - Development builds
  • krow_staff_staging.jks - Staging builds
  • krow_staff_prod.jks - Production builds

Client Mobile:

  • krow_client_dev.jks - Development builds
  • krow_client_staging.jks - Staging builds
  • krow_client_prod.jks - Production builds

Current State

  • Dev keystores are committed to the repository (in apps/mobile/apps/*/android/app/)
  • ⚠️ Staging/Prod keystores are stored securely in CodeMagic (NOT in repo)

🔐 GitHub Secrets Setup

Step 1: Export Keystores as Base64

For staging and production keystores (stored in CodeMagic), you'll need to:

# On your local machine with access to the keystore files:

# For Worker Mobile - Staging
base64 -i krow_staff_staging.jks -o krow_staff_staging.jks.base64

# For Worker Mobile - Production
base64 -i krow_staff_prod.jks -o krow_staff_prod.jks.base64

# For Client Mobile - Staging
base64 -i krow_client_staging.jks -o krow_client_staging.jks.base64

# For Client Mobile - Production
base64 -i krow_client_prod.jks -o krow_client_prod.jks.base64

Step 2: Create GitHub Secrets

Go to: GitHub Repository → Settings → Secrets and variables → Actions → New repository secret

Create the following secrets:

Worker Mobile (Staff App) Secrets

Secret Name Value Environment
WORKER_KEYSTORE_DEV_BASE64 (base64 of dev keystore) dev
WORKER_KEYSTORE_STAGING_BASE64 (base64 of staging keystore) stage
WORKER_KEYSTORE_PROD_BASE64 (base64 of prod keystore) prod
WORKER_KEYSTORE_PASSWORD_DEV krowwithus dev
WORKER_KEYSTORE_PASSWORD_STAGING (actual staging password) stage
WORKER_KEYSTORE_PASSWORD_PROD (actual prod password) prod
WORKER_KEY_ALIAS_DEV krow_staff_dev dev
WORKER_KEY_ALIAS_STAGING (actual staging alias) stage
WORKER_KEY_ALIAS_PROD (actual prod alias) prod
WORKER_KEY_PASSWORD_DEV krowwithus dev
WORKER_KEY_PASSWORD_STAGING (actual staging key password) stage
WORKER_KEY_PASSWORD_PROD (actual prod key password) prod

Client Mobile Secrets

Secret Name Value Environment
CLIENT_KEYSTORE_DEV_BASE64 (base64 of dev keystore) dev
CLIENT_KEYSTORE_STAGING_BASE64 (base64 of staging keystore) stage
CLIENT_KEYSTORE_PROD_BASE64 (base64 of prod keystore) prod
CLIENT_KEYSTORE_PASSWORD_DEV krowwithus dev
CLIENT_KEYSTORE_PASSWORD_STAGING (actual staging password) stage
CLIENT_KEYSTORE_PASSWORD_PROD (actual prod password) prod
CLIENT_KEY_ALIAS_DEV krow_client_dev dev
CLIENT_KEY_ALIAS_STAGING (actual staging alias) stage
CLIENT_KEY_ALIAS_PROD (actual prod alias) prod
CLIENT_KEY_PASSWORD_DEV krowwithus dev
CLIENT_KEY_PASSWORD_STAGING (actual staging key password) stage
CLIENT_KEY_PASSWORD_PROD (actual prod key password) prod

⚙️ How It Works in GitHub Actions

Build Configuration Detection

The build.gradle.kts files check for CI=true environment variable:

signingConfigs {
    create("release") {
        if (System.getenv()["CI"] == "true") {
            // CI environment (CodeMagic or GitHub Actions)
            storeFile = file(System.getenv()["CM_KEYSTORE_PATH_STAFF"] ?: "")
            storePassword = System.getenv()["CM_KEYSTORE_PASSWORD_STAFF"]
            keyAlias = System.getenv()["CM_KEY_ALIAS_STAFF"]
            keyPassword = System.getenv()["CM_KEY_PASSWORD_STAFF"]
        } else {
            // Local development (uses key.properties)
            keyAlias = keystoreProperties["keyAlias"] as String?
            keyPassword = keystoreProperties["keyPassword"] as String?
            storeFile = keystoreProperties["storeFile"]?.let { file(it) }
            storePassword = keystoreProperties["storePassword"] as String?
        }
    }
}

GitHub Actions Workflow Steps

  1. Decode Keystore: Convert base64 secret back to .jks file
  2. Set Environment Variables: Provide the same env vars CodeMagic uses
  3. Build APK: Flutter build automatically uses the signing config
  4. Verify Signature: Optionally verify the APK is signed correctly

🚀 Usage

For Development Builds

Dev keystores are already in the repo, so GitHub Actions will automatically use them:

# No special setup needed for dev builds
# They use committed keystores: krow_with_us_staff_dev.jks

For Staging/Production Builds

Once GitHub Secrets are configured (Step 2 above), the workflow will:

  1. Detect the environment (dev/stage/prod)
  2. Use the appropriate keystore secret
  3. Decode it before building
  4. Sign the APK automatically

Verification

Check APK Signature

After building, verify the APK is signed:

# Using keytool (part of Java JDK)
keytool -printcert -jarfile app-release.apk

# Expected output should show certificate info with your key alias

Check Build Logs

In GitHub Actions, look for:

✅ Keystore decoded successfully
✅ APK signed with: krow_staff_prod
✅ APK built successfully: /path/to/app-release.apk

🔒 Security Best Practices

DO:

  • Store production keystores ONLY in GitHub Secrets (encrypted)
  • Use different keystores for dev/staging/prod
  • Rotate passwords periodically
  • Limit access to repository secrets (use environment protection rules)
  • Keep keystore files backed up securely offline

DON'T:

  • Never commit staging/production keystores to Git
  • Never share keystore passwords in plain text
  • Never use production keystores for development
  • Never commit .jks files for staging/prod

📝 Keystore Management Commands

Generate New Keystore

keytool -genkey -v \
  -keystore krow_staff_prod.jks \
  -alias krow_staff_prod \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -storetype JKS

View Keystore Info

keytool -list -v -keystore krow_staff_prod.jks

Get SHA-1 and SHA-256 Fingerprints

keytool -list -v -keystore krow_staff_prod.jks -alias krow_staff_prod

These fingerprints are needed for:

  • Firebase project configuration
  • Google Maps API key restrictions
  • Google Play Console app signing

🆘 Troubleshooting

"keystore not found" Error

Problem: GitHub Actions can't find the decoded keystore
Solution: Check the decode step in the workflow creates the file in the correct location

"wrong password" Error

Problem: Keystore password doesn't match
Solution: Verify the GitHub Secret value matches the actual keystore password

APK Not Signed

Problem: APK builds but isn't signed
Solution: Ensure CI=true is set before building

Certificate Mismatch

Problem: "App not installed" when updating
Solution: You're using a different keystore than previous builds. Use the same keystore for all versions.



🔄 Migration from CodeMagic

If you want to use GitHub Actions instead of CodeMagic:

  1. Export all keystores from CodeMagic
  2. Convert to base64 (as shown above)
  3. Add to GitHub Secrets
  4. Test with a dev build first
  5. Verify signatures match previous releases
  6. Deploy staging build for testing
  7. Only then use for production

Important: Make sure the GitHub Actions builds produce the SAME signature as CodeMagic builds, otherwise app updates will fail!