# APK Signing Implementation - Complete Summary **Status**: โœ… Implementation Complete | ๐ŸŸก Secrets Configuration Pending **Last Updated**: 2024 --- ## ๐Ÿ“‹ What Was Implemented ### 1. GitHub Actions Workflow Updates **File**: `.github/workflows/product-release.yml` **New Steps Added**: 1. **๐Ÿ” Setup APK Signing** (before build) - Detects app (worker/client) and environment (dev/stage/prod) - Decodes keystore from GitHub Secrets - Sets CodeMagic-compatible environment variables - Configures `CI=true` for build.gradle.kts detection - Gracefully handles missing secrets with warnings 2. **โœ… Verify APK Signature** (after build) - Verifies APK is properly signed using `jarsigner` - Displays certificate details - Shows signer information - Provides helpful warnings if unsigned **How It Works**: ```yaml Setup Signing: - Reads: ${{ secrets.WORKER_KEYSTORE_DEV_BASE64 }} - Decodes to: /tmp/keystores/release.jks - Sets env: CI=true, CM_KEYSTORE_PATH_STAFF=/tmp/keystores/release.jks Build APK: - Runs: make mobile-staff-build PLATFORM=apk MODE=release - build.gradle.kts detects CI=true - Uses environment variables for signing Verify Signature: - Checks with: jarsigner -verify app-release.apk - Displays certificate info ``` ### 2. Documentation Created **Files Created**: | File | Purpose | Lines | |------|---------|-------| | [docs/RELEASE/APK_SIGNING_SETUP.md](../../docs/RELEASE/APK_SIGNING_SETUP.md) | Complete setup guide | 300+ | | [docs/RELEASE/GITHUB_SECRETS_CHECKLIST.md](../../docs/RELEASE/GITHUB_SECRETS_CHECKLIST.md) | Quick reference checklist | 120+ | | [.github/scripts/setup-github-secrets.sh](../../.github/scripts/setup-github-secrets.sh) | Helper script | 200+ | ### 3. Scripts Created **File**: `.github/scripts/setup-github-secrets.sh` **Purpose**: Interactive helper to: - Show which secrets are needed - Generate base64 from existing keystores - Display keytool information - Provide copy-paste commands **Usage**: ```bash ./.github/scripts/setup-github-secrets.sh ``` --- ## ๐Ÿ”‘ GitHub Secrets Required **Total: 24 Secrets** (6 keystores ร— 4 properties each) ### Secret Naming Pattern: ``` {APP}_KEYSTORE_{ENV}_BASE64 {APP}_KEYSTORE_PASSWORD_{ENV} {APP}_KEY_ALIAS_{ENV} {APP}_KEY_PASSWORD_{ENV} ``` Where: - `{APP}` = `WORKER` or `CLIENT` - `{ENV}` = `DEV`, `STAGING`, or `PROD` ### Full List: **Worker Mobile (12 secrets)**: - `WORKER_KEYSTORE_DEV_BASE64`, `WORKER_KEYSTORE_PASSWORD_DEV`, `WORKER_KEY_ALIAS_DEV`, `WORKER_KEY_PASSWORD_DEV` - `WORKER_KEYSTORE_STAGING_BASE64`, `WORKER_KEYSTORE_PASSWORD_STAGING`, `WORKER_KEY_ALIAS_STAGING`, `WORKER_KEY_PASSWORD_STAGING` - `WORKER_KEYSTORE_PROD_BASE64`, `WORKER_KEYSTORE_PASSWORD_PROD`, `WORKER_KEY_ALIAS_PROD`, `WORKER_KEY_PASSWORD_PROD` **Client Mobile (12 secrets)**: - `CLIENT_KEYSTORE_DEV_BASE64`, `CLIENT_KEYSTORE_PASSWORD_DEV`, `CLIENT_KEY_ALIAS_DEV`, `CLIENT_KEY_PASSWORD_DEV` - `CLIENT_KEYSTORE_STAGING_BASE64`, `CLIENT_KEYSTORE_PASSWORD_STAGING`, `CLIENT_KEY_ALIAS_STAGING`, `CLIENT_KEY_PASSWORD_STAGING` - `CLIENT_KEYSTORE_PROD_BASE64`, `CLIENT_KEYSTORE_PASSWORD_PROD`, `CLIENT_KEY_ALIAS_PROD`, `CLIENT_KEY_PASSWORD_PROD` --- ## ๐Ÿš€ How to Configure ### Step 1: Prepare Dev Keystores Dev keystores are already in the repository: - Worker: `apps/mobile/apps/staff/android/app/krow_with_us_staff_dev.jks` - Client: `apps/mobile/apps/client/android/app/krow_with_us_client_dev.jks` Generate base64: ```bash # Worker Dev base64 -i apps/mobile/apps/staff/android/app/krow_with_us_staff_dev.jks # Client Dev base64 -i apps/mobile/apps/client/android/app/krow_with_us_client_dev.jks ``` ### Step 2: Retrieve Staging/Prod Keystores **Option A**: From CodeMagic 1. Go to CodeMagic โ†’ Team Settings โ†’ Code signing identities 2. Download keystores: `krow_staff_staging.jks`, `krow_staff_prod.jks`, etc. 3. Generate base64 for each **Option B**: From Secure Storage 1. Retrieve from your organization's key management system 2. Generate base64 for each ### Step 3: Add to GitHub 1. Go to: **Repository โ†’ Settings โ†’ Secrets and variables โ†’ Actions** 2. Click: **New repository secret** 3. Add all 24 secrets (use checklist: [GITHUB_SECRETS_CHECKLIST.md](../../docs/RELEASE/GITHUB_SECRETS_CHECKLIST.md)) ### Step 4: Test the Workflow ```bash # Test with dev environment first # Go to: Actions โ†’ Product Release โ†’ Run workflow # Select: # - App: worker-mobile-app # - Environment: dev # - Version type: patch # - Create GitHub Release: true ``` **Expected Output**: ``` ๐Ÿ” Setting up Android signing for worker-mobile-app in dev environment... โœ… Keystore decoded successfully ๐Ÿ“ฆ Keystore size: 3.2K โœ… Signing environment configured for STAFF (dev environment) ๐Ÿ”‘ Using key alias: krow_staff_dev ๐Ÿ“ฑ Building Staff (Worker) APK... โœ… APK built successfully ๐Ÿ” Verifying APK signature... โœ… APK is properly signed! ๐Ÿ“œ Certificate Details: [shows cert info] ``` --- ## ๐Ÿ”’ Security Considerations ### โœ… Safe Practices 1. **Dev keystores in repo**: Acceptable for development - Committed: `krow_with_us_staff_dev.jks`, `krow_with_us_client_dev.jks` - Password: `krowwithus` (public knowledge) 2. **Staging/Prod keystores**: ONLY in GitHub Secrets - Never commit to repository - Encrypted at rest by GitHub - Only accessible in workflow runs 3. **Keystore cleanup**: Workflow stores in `${{ runner.temp }}` - Automatically deleted after job completes - Not persisted in artifacts or logs ### โš ๏ธ Important Notes 1. **Same keystores as CodeMagic**: Use identical keystores to ensure app updates work 2. **Signature consistency**: Apps signed with different keystores cannot update each other 3. **Key rotation**: Document process for rotating production keys 4. **Backup keystores**: Keep secure backups - lost keystores = can't update app --- ## ๐Ÿงช Testing Checklist Before using in production: - [ ] Configure all 24 GitHub Secrets - [ ] Run workflow with `dev` environment - [ ] Download APK artifact - [ ] Verify signature: `jarsigner -verify -verbose app.apk` - [ ] Install APK on Android device - [ ] Launch app and verify functionality - [ ] Compare signature fingerprints with CodeMagic builds - [ ] Test `stage` environment - [ ] Test `prod` environment (after full validation) --- ## ๐Ÿ“Š Architecture Overview ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ GitHub Actions Workflow: product-release.yml โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ 1. Validate & Create Release โ”‚ โ”‚ โ””โ”€> Extract version from pubspec.yaml โ”‚ โ”‚ โ””โ”€> Create Git tag โ”‚ โ”‚ โ””โ”€> Create GitHub Release โ”‚ โ”‚ โ”‚ โ”‚ 2. Build Mobile Artifacts โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> Setup Node.js + Firebase CLI โ”‚ โ”‚ โ”œโ”€> Setup Java 17 โ”‚ โ”‚ โ”œโ”€> Setup Flutter 3.24.5 โ”‚ โ”‚ โ”œโ”€> Install Melos โ”‚ โ”‚ โ”œโ”€> Install Dependencies โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> ๐Ÿ” Setup APK Signing (NEW) โ”‚ โ”‚ โ”‚ โ”œโ”€> Detect app (worker/client) โ”‚ โ”‚ โ”‚ โ”œโ”€> Detect environment (dev/stage/prod) โ”‚ โ”‚ โ”‚ โ”œโ”€> Read GitHub Secret: โ”‚ โ”‚ โ”‚ โ”‚ {APP}_KEYSTORE_{ENV}_BASE64 โ”‚ โ”‚ โ”‚ โ”œโ”€> Decode base64 โ†’ .jks file โ”‚ โ”‚ โ”‚ โ”œโ”€> Set environment variables: โ”‚ โ”‚ โ”‚ โ”‚ - CI=true โ”‚ โ”‚ โ”‚ โ”‚ - CM_KEYSTORE_PATH_STAFF=/tmp/keystore.jks โ”‚ โ”‚ โ”‚ โ”‚ - CM_KEYSTORE_PASSWORD_STAFF=*** โ”‚ โ”‚ โ”‚ โ”‚ - CM_KEY_ALIAS_STAFF=krow_staff_dev โ”‚ โ”‚ โ”‚ โ”‚ - CM_KEY_PASSWORD_STAFF=*** โ”‚ โ”‚ โ”‚ โ””โ”€> โœ… Ready for signed build โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> ๐Ÿ—๏ธ Build APK โ”‚ โ”‚ โ”‚ โ””โ”€> make mobile-staff-build PLATFORM=apk โ”‚ โ”‚ โ”‚ โ””โ”€> Flutter build detects CI=true โ”‚ โ”‚ โ”‚ โ””โ”€> build.gradle.kts uses env vars โ”‚ โ”‚ โ”‚ โ””โ”€> Signs APK with keystore โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> โœ… Verify APK Signature (NEW) โ”‚ โ”‚ โ”‚ โ”œโ”€> jarsigner -verify app-release.apk โ”‚ โ”‚ โ”‚ โ”œโ”€> Show certificate details โ”‚ โ”‚ โ”‚ โ””โ”€> Confirm signing successful โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€> ๐Ÿ“ค Upload APK as Artifact โ”‚ โ”‚ โ”‚ โ””โ”€> 30-day retention in GitHub Actions โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€> ๐Ÿ“ฆ Attach APK to GitHub Release โ”‚ โ”‚ โ””โ”€> krow-withus-worker-mobile-dev-v0.1.0.apk โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Build Configuration: build.gradle.kts โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ โ”‚ โ”‚ signingConfigs { โ”‚ โ”‚ create("release") { โ”‚ โ”‚ if (System.getenv()["CI"] == "true") { โ”‚ โ”‚ // โœ… GitHub Actions / CodeMagic โ”‚ โ”‚ storeFile = file( โ”‚ โ”‚ System.getenv()["CM_KEYSTORE_PATH_STAFF"] โ”‚ โ”‚ ) โ”‚ โ”‚ storePassword = โ”‚ โ”‚ System.getenv()["CM_KEYSTORE_PASSWORD_*"] โ”‚ โ”‚ keyAlias = โ”‚ โ”‚ System.getenv()["CM_KEY_ALIAS_*"] โ”‚ โ”‚ keyPassword = โ”‚ โ”‚ System.getenv()["CM_KEY_PASSWORD_*"] โ”‚ โ”‚ } else { โ”‚ โ”‚ // Local Development โ”‚ โ”‚ use key.properties file โ”‚ โ”‚ } โ”‚ โ”‚ } โ”‚ โ”‚ } โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` --- ## ๐Ÿ“– Documentation Index 1. **[APK_SIGNING_SETUP.md](../../docs/RELEASE/APK_SIGNING_SETUP.md)** - Complete setup guide with all details - Security best practices - Troubleshooting guide - Keystore management commands 2. **[GITHUB_SECRETS_CHECKLIST.md](../../docs/RELEASE/GITHUB_SECRETS_CHECKLIST.md)** - Quick reference for all 24 secrets - Copy-paste checklist - Dev environment values 3. **[setup-mobile-github-secrets.sh](../../.github/scripts/setup-mobile-github-secrets.sh)** - Interactive helper script - Shows existing keystores - Generates base64 commands - Displays keytool info 4. **[product-release.yml](../../.github/workflows/product-release.yml)** - Updated workflow with signing - Lines 198-294: Setup APK Signing - Lines 330-364: Verify APK Signature --- ## โœ… Next Steps ### Immediate (Required for Signed APKs) 1. **Configure GitHub Secrets** (30 minutes) - Start with dev environment (test first) - Use helper script: `.github/scripts/setup-mobile-github-secrets.sh` - Follow checklist: `docs/RELEASE/GITHUB_SECRETS_CHECKLIST.md` 2. **Test Dev Signing** (15 minutes) - Run workflow with dev environment - Download APK and verify signature - Install on device and test 3. **Configure Staging/Prod** (30 minutes) - Retrieve keystores from CodeMagic/secure storage - Add to GitHub Secrets - Test each environment ### Future Enhancements (Optional) - [ ] Add AAB (Android App Bundle) support for Play Store - [ ] Implement iOS signing for IPA files - [ ] Add automated Play Store upload - [ ] Set up GitHub Environments with protection rules - [ ] Add Slack notifications for releases --- ## ๐Ÿ†˜ Support If you encounter issues: 1. Check workflow logs for signing step output 2. Verify GitHub Secrets are configured correctly 3. Run helper script: `.github/scripts/setup-mobile-github-secrets.sh` 4. Review: `docs/RELEASE/APK_SIGNING_SETUP.md` โ†’ Troubleshooting section **Common Issues**: - "Keystore not found" โ†’ Secret not configured or wrong name - "Wrong password" โ†’ Secret value doesn't match actual keystore - APK unsigned โ†’ CI=true not set or build.gradle.kts issue - App won't install over existing โ†’ Different keystore used --- **Implementation Date**: 2024 **Implemented By**: GitHub Copilot (Claude Sonnet 4.5) **Status**: โœ… Code Complete | ๐ŸŸก Awaiting Secrets Configuration