From 8b1441c4d0a0dd17855e9098ab09a7d76fea1e59 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 21:57:22 +0530 Subject: [PATCH 1/9] CI: build AAB and auto-release on tags --- .github/workflows/build.yml | 54 +++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2cb57f..d9b61ad 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,12 +10,16 @@ on: pull_request: branches: [ main ] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + permissions: contents: read jobs: android: - name: Build Android APK + name: Build Android APK/AAB runs-on: ubuntu-latest env: CI: true @@ -44,6 +48,9 @@ jobs: - name: Build APK (split per ABI) run: flutter build apk --release --split-per-abi --build-number ${{ github.run_number }} + - name: Build App Bundle (AAB) + run: flutter build appbundle --release --build-number ${{ github.run_number }} + - name: Upload APKs uses: actions/upload-artifact@v4 with: @@ -51,6 +58,13 @@ jobs: path: | build/app/outputs/**/*.apk + - name: Upload AAB + uses: actions/upload-artifact@v4 + with: + name: android-aab + path: | + build/app/outputs/**/*.aab + ios: name: Build iOS App (no codesign) runs-on: macos-latest @@ -89,4 +103,40 @@ jobs: uses: actions/upload-artifact@v4 with: name: ios-app - path: build/ios/iphoneos/Runner.app.zip \ No newline at end of file + path: build/ios/iphoneos/Runner.app.zip + + release: + name: Publish GitHub Release + runs-on: ubuntu-latest + needs: [android, ios] + if: startsWith(github.ref, 'refs/tags/') + permissions: + contents: write + steps: + - name: Download Android APKs + uses: actions/download-artifact@v4 + with: + name: android-apk + path: dist + + - name: Download Android AAB + uses: actions/download-artifact@v4 + with: + name: android-aab + path: dist + + - name: Download iOS App + uses: actions/download-artifact@v4 + with: + name: ios-app + path: dist + + - name: Publish Release + uses: softprops/action-gh-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + files: | + dist/**/*.apk + dist/**/*.aab + dist/**/*.zip \ No newline at end of file From 99c341f00556e6e5b635a5794ff6eac025394889 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:08:42 +0530 Subject: [PATCH 2/9] CI: install Android SDK packages and CocoaPods, add pod install --- .github/workflows/build.yml | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d9b61ad..a5d644f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,14 +33,23 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + cmdline-tools-version: latest + packages: | + platforms;android-34 + build-tools;34.0.0 + platform-tools + - name: Setup Flutter uses: subosito/flutter-action@v2 with: channel: 'stable' cache: true - - name: Flutter version - run: flutter --version + - name: Doctor (sanity) + run: flutter doctor -v - name: Install dependencies run: flutter pub get @@ -80,16 +89,23 @@ jobs: channel: 'stable' cache: true - - name: Flutter version - run: flutter --version + - name: Xcode version + run: xcodebuild -version - - name: Install dependencies - run: flutter pub get + - name: Install CocoaPods (gem) + run: sudo gem install cocoapods --no-document - - name: Ensure CocoaPods + - name: Pod install run: | - brew --version - brew install cocoapods || true + cd ios + pod repo update + pod install + + - name: Doctor (sanity) + run: flutter doctor -v + + - name: Install dependencies + run: flutter pub get - name: Build iOS (no codesign) run: flutter build ios --release --no-codesign --build-number ${{ github.run_number }} From 19b05b7d18055205bb166b0af43e7bf38bb838f4 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:21:53 +0530 Subject: [PATCH 3/9] CI: add universal APK and optional Android signing via secrets; Gradle: release signing with key.properties --- .github/workflows/build.yml | 29 +++++++++++++++++++++++++++++ android/app/build.gradle.kts | 30 +++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5d644f..eb12945 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,9 @@ jobs: build-tools;34.0.0 platform-tools + - name: Accept Android licenses + run: yes | sdkmanager --licenses + - name: Setup Flutter uses: subosito/flutter-action@v2 with: @@ -54,9 +57,29 @@ jobs: - name: Install dependencies run: flutter pub get + - name: Prepare Android keystore (optional) + if: ${{ secrets.ANDROID_KEYSTORE_BASE64 != '' }} + env: + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + shell: bash + run: | + echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/keystore.jks + cat > key.properties <<'EOF' + storeFile=android/app/keystore.jks + storePassword=${ANDROID_KEYSTORE_PASSWORD} + keyAlias=${ANDROID_KEY_ALIAS} + keyPassword=${ANDROID_KEY_PASSWORD} + EOF + - name: Build APK (split per ABI) run: flutter build apk --release --split-per-abi --build-number ${{ github.run_number }} + - name: Build APK (universal) + run: flutter build apk --release --build-number ${{ github.run_number }} + - name: Build App Bundle (AAB) run: flutter build appbundle --release --build-number ${{ github.run_number }} @@ -67,6 +90,12 @@ jobs: path: | build/app/outputs/**/*.apk + - name: Upload Universal APK + uses: actions/upload-artifact@v4 + with: + name: android-apk-universal + path: build/app/outputs/flutter-apk/app-release.apk + - name: Upload AAB uses: actions/upload-artifact@v4 with: diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 4281d5c..5c2112e 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -5,6 +5,15 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } +import java.util.Properties +import java.io.FileInputStream + +val keystoreProperties = Properties() +val keystorePropertiesFile = rootProject.file("key.properties") +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(FileInputStream(keystorePropertiesFile)) +} + android { namespace = "com.example.flutter_app" compileSdk = flutter.compileSdkVersion @@ -30,11 +39,26 @@ android { versionName = flutter.versionName } + signingConfigs { + create("release") { + val storeFileProp = keystoreProperties.getProperty("storeFile") + if (storeFileProp != null) { + storeFile = file(storeFileProp) + storePassword = keystoreProperties.getProperty("storePassword") + keyAlias = keystoreProperties.getProperty("keyAlias") + keyPassword = keystoreProperties.getProperty("keyPassword") + } + } + } + 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") + // Use release signing if keystore is configured, otherwise fall back to debug + signingConfig = if (keystoreProperties.getProperty("storeFile") != null) { + signingConfigs.getByName("release") + } else { + signingConfigs.getByName("debug") + } } } } From feb4e88a1c32b14ff8020a5baa57f7e0e5f559ea Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:23:55 +0530 Subject: [PATCH 4/9] Gradle: remove imports, use FQNs to keep plugins block first --- android/app/build.gradle.kts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 5c2112e..9712efa 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -5,13 +5,11 @@ plugins { id("dev.flutter.flutter-gradle-plugin") } -import java.util.Properties -import java.io.FileInputStream - -val keystoreProperties = Properties() +// Remove imports to keep plugins block first; use fully-qualified names +val keystoreProperties = java.util.Properties() val keystorePropertiesFile = rootProject.file("key.properties") if (keystorePropertiesFile.exists()) { - keystoreProperties.load(FileInputStream(keystorePropertiesFile)) + keystoreProperties.load(java.io.FileInputStream(keystorePropertiesFile)) } android { From 3cd1d13215a0c2547d2d3e7adb1758fddf5627b0 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:25:53 +0530 Subject: [PATCH 5/9] CI: run on ci-setup pushes, pin ubuntu-22.04/macos-14, fix keystore path --- .github/workflows/build.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb12945..94886eb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ name: Flutter CI Builds on: workflow_dispatch: push: - branches: [ main ] + branches: [ main, ci-setup ] tags: - 'v*' - 'release-*' @@ -20,7 +20,7 @@ permissions: jobs: android: name: Build Android APK/AAB - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 env: CI: true steps: @@ -50,6 +50,8 @@ jobs: with: channel: 'stable' cache: true + - name: Flutter version + run: flutter --version - name: Doctor (sanity) run: flutter doctor -v @@ -67,8 +69,8 @@ jobs: shell: bash run: | echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/app/keystore.jks - cat > key.properties <<'EOF' - storeFile=android/app/keystore.jks + cat > android/key.properties <<'EOF' + storeFile=../app/keystore.jks storePassword=${ANDROID_KEYSTORE_PASSWORD} keyAlias=${ANDROID_KEY_ALIAS} keyPassword=${ANDROID_KEY_PASSWORD} @@ -105,7 +107,7 @@ jobs: ios: name: Build iOS App (no codesign) - runs-on: macos-latest + runs-on: macos-14 env: CI: true steps: From ba536a70397df1266d861b2d9f2533309f1c45ef Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:29:37 +0530 Subject: [PATCH 6/9] CI: add preflight smoke job to validate Actions execution --- .github/workflows/build.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94886eb..a20b762 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,24 @@ permissions: contents: read jobs: + preflight: + name: Preflight Checks + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Show Runner Info + run: | + uname -a + df -h + echo "Branch: ${{ github.ref }}" + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + cache: true + - name: Flutter Doctor + run: flutter doctor -v android: name: Build Android APK/AAB runs-on: ubuntu-22.04 From 87da352d4fbbb4a163503e1dce4fb1b958bb47a0 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:33:14 +0530 Subject: [PATCH 7/9] CI: add preflight-only workflow without external actions --- .github/workflows/preflight.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/preflight.yml diff --git a/.github/workflows/preflight.yml b/.github/workflows/preflight.yml new file mode 100644 index 0000000..a9a8514 --- /dev/null +++ b/.github/workflows/preflight.yml @@ -0,0 +1,16 @@ +name: Preflight (no external actions) + +on: + push: + branches: + - ci-setup + +jobs: + preflight-only: + name: Preflight Only + runs-on: ubuntu-22.04 + steps: + - name: Say Hello + run: | + echo "Runner: $(uname -a)" + echo "Branch: ${{ github.ref }}" \ No newline at end of file From ca0d97fe46a6242f6841139922b44e96007c205e Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:35:09 +0530 Subject: [PATCH 8/9] CI: install Android SDK packages and CocoaPods, add pod install From 7779657b08aaceb7e2fcffee1372216e32ccc266 Mon Sep 17 00:00:00 2001 From: local-user Date: Mon, 20 Oct 2025 22:41:10 +0530 Subject: [PATCH 9/9] CI: retrigger after enabling Actions