From b20cf07508f8e7d409b36dd53bfd681286458bbb Mon Sep 17 00:00:00 2001 From: Nabil Mohammed Nalakath Date: Sun, 26 Apr 2026 03:40:35 +0530 Subject: [PATCH 1/5] feat: implement secure android release signing via github secrets --- .github/workflows/main.yml | 12 ++++++++++++ README.md | 18 ++++++++++++++++++ android/app/build.gradle.kts | 22 +++++++++++++++++++--- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c399db7..0b790d9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,7 +47,19 @@ jobs: - name: ✅ Run tests run: flutter test --coverage + - name: 🔐 Decode Keystore + if: "${{ secrets.KEYSTORE_BASE64 != '' }}" + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + run: | + echo $KEYSTORE_BASE64 | base64 --decode > android/app/upload-keystore.jks + - name: 🏗️ Build APKs + env: + KEYSTORE_PATH: "upload-keystore.jks" + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} run: flutter build apk --release --split-per-abi - name: 📦 Prepare artifacts diff --git a/README.md b/README.md index 7ff3cfa..8854b35 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,21 @@ This project uses the following github actions - * https://github.com/marketplace/actions/create-release For a complete guide on implemenatation read the tutorial on [Medium](https://medium.com/better-programming/ci-cd-for-flutter-apps-using-github-actions-b833f8f7aac) + +## 🔐 Secure Release Signing + +To automatically sign your release APK with your own keystore using this GitHub Action, you need to configure your repository secrets. + +1. **Generate your keystore** as you normally would for an Android app. +2. **Encode your keystore to Base64** by running the following command in your terminal: + ```bash + base64 -i path/to/your/keystore.jks > keystore.txt + ``` + *(On some operating systems, you may need to use `base64 -w 0 path/to/keystore.jks`)* +3. **Add GitHub Secrets** to your repository (Settings -> Secrets and variables -> Actions): + - `KEYSTORE_BASE64`: The contents of the `keystore.txt` file you just created. + - `KEYSTORE_PASSWORD`: The password for your keystore. + - `KEY_ALIAS`: Your key alias. + - `KEY_PASSWORD`: The password for your key. + +The GitHub action will automatically detect these secrets and use them to securely sign the release APK. If these secrets are not provided, it will gracefully fall back to the default debug signing keys. diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 7d74b6b..71666a0 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -30,11 +30,27 @@ android { versionName = flutter.versionName } + signingConfigs { + create("release") { + val keystorePath = System.getenv("KEYSTORE_PATH") + if (keystorePath != null && file(keystorePath).exists()) { + storeFile = file(keystorePath) + storePassword = System.getenv("KEYSTORE_PASSWORD") + keyAlias = System.getenv("KEY_ALIAS") + keyPassword = System.getenv("KEY_PASSWORD") + } + } + } + buildTypes { release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.getByName("debug") + val keystorePath = System.getenv("KEYSTORE_PATH") + if (keystorePath != null && file(keystorePath).exists()) { + signingConfig = signingConfigs.getByName("release") + } else { + // Fallback to debug keys if secrets are not provided + signingConfig = signingConfigs.getByName("debug") + } } } } From c795dd9a031dd331af4ba9368c932471d6e4c764 Mon Sep 17 00:00:00 2001 From: Nabil Mohammed Nalakath Date: Sun, 26 Apr 2026 04:12:04 +0530 Subject: [PATCH 2/5] Updated readme --- README.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8854b35..05f4ac1 100644 --- a/README.md +++ b/README.md @@ -22,12 +22,25 @@ For a complete guide on implemenatation read the tutorial on [Medium](https://me To automatically sign your release APK with your own keystore using this GitHub Action, you need to configure your repository secrets. -1. **Generate your keystore** as you normally would for an Android app. -2. **Encode your keystore to Base64** by running the following command in your terminal: +1. **Generate your keystore** + If you don't already have a `.jks` keystore file, you can generate one using the `keytool` utility (which comes bundled with Java or Android Studio). Run this in your terminal: ```bash - base64 -i path/to/your/keystore.jks > keystore.txt + keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload ``` - *(On some operating systems, you may need to use `base64 -w 0 path/to/keystore.jks`)* + *(**Note for Mac users:** If you get a "command not found" error because Java isn't installed system-wide, you can use the version bundled with Android Studio by typing `/Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin/keytool` instead of just `keytool`)* +2. **Encode your keystore to Base64**: + - **Mac:** + ```bash + base64 -b 0 -i "path/to/your/keystore.jks" > keystore.txt + ``` + - **Linux:** + ```bash + base64 -w 0 "path/to/your/keystore.jks" > keystore.txt + ``` + - **Windows (PowerShell):** + ```powershell + [Convert]::ToBase64String([IO.File]::ReadAllBytes("path\to\your\keystore.jks")) | Out-File keystore.txt + ``` 3. **Add GitHub Secrets** to your repository (Settings -> Secrets and variables -> Actions): - `KEYSTORE_BASE64`: The contents of the `keystore.txt` file you just created. - `KEYSTORE_PASSWORD`: The password for your keystore. @@ -35,3 +48,6 @@ To automatically sign your release APK with your own keystore using this GitHub - `KEY_PASSWORD`: The password for your key. The GitHub action will automatically detect these secrets and use them to securely sign the release APK. If these secrets are not provided, it will gracefully fall back to the default debug signing keys. + +> [!WARNING] +> **Never commit your `.jks` keystore file, `keystore.txt`, or any of your passwords directly to your git repository!** Always add them securely via GitHub Secrets and make sure your `.gitignore` is configured to ignore `.jks` and `.txt` key files. From ae5894fa593eb48cf776fad905854b27772afeeb Mon Sep 17 00:00:00 2001 From: Nabil Mohammed Nalakath Date: Sun, 26 Apr 2026 04:14:01 +0530 Subject: [PATCH 3/5] Fixed error in if condition --- .github/workflows/main.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b790d9..b121aa6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,11 +48,14 @@ jobs: run: flutter test --coverage - name: 🔐 Decode Keystore - if: "${{ secrets.KEYSTORE_BASE64 != '' }}" env: KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} run: | - echo $KEYSTORE_BASE64 | base64 --decode > android/app/upload-keystore.jks + if [ -n "$KEYSTORE_BASE64" ]; then + echo $KEYSTORE_BASE64 | base64 --decode > android/app/upload-keystore.jks + else + echo "No KEYSTORE_BASE64 secret provided, skipping secure signing setup." + fi - name: 🏗️ Build APKs env: From 8492e6b26ec99f372bebfed88df08533db83a741 Mon Sep 17 00:00:00 2001 From: Nabil Mohammed Nalakath Date: Sun, 26 Apr 2026 04:19:31 +0530 Subject: [PATCH 4/5] added log signing key used --- android/app/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 71666a0..9ffe411 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -46,9 +46,11 @@ android { release { val keystorePath = System.getenv("KEYSTORE_PATH") if (keystorePath != null && file(keystorePath).exists()) { + println("🚀 SIGNING WITH CUSTOM SECURE RELEASE KEY!") signingConfig = signingConfigs.getByName("release") } else { // Fallback to debug keys if secrets are not provided + println("⚠️ NO SECURE KEY FOUND. SIGNING WITH DEFAULT DEBUG KEY.") signingConfig = signingConfigs.getByName("debug") } } From e0b8d7564309a35ea561ce46c4d10e6861016402 Mon Sep 17 00:00:00 2001 From: Nabil Mohammed Nalakath Date: Sun, 26 Apr 2026 04:26:21 +0530 Subject: [PATCH 5/5] Updated readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 05f4ac1..8de8f9b 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ To automatically sign your release APK with your own keystore using this GitHub ``` 3. **Add GitHub Secrets** to your repository (Settings -> Secrets and variables -> Actions): - `KEYSTORE_BASE64`: The contents of the `keystore.txt` file you just created. - - `KEYSTORE_PASSWORD`: The password for your keystore. - - `KEY_ALIAS`: Your key alias. - - `KEY_PASSWORD`: The password for your key. + - `KEYSTORE_PASSWORD`: The keystore password you typed when running the `keytool` command. + - `KEY_ALIAS`: The alias you used (e.g., `upload` if you copied the command exactly). + - `KEY_PASSWORD`: The key password you typed when running the `keytool` command. The GitHub action will automatically detect these secrets and use them to securely sign the release APK. If these secrets are not provided, it will gracefully fall back to the default debug signing keys.