Skip to content
Open
141 changes: 128 additions & 13 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,42 @@ name: Flutter CI Builds
on:
workflow_dispatch:
push:
branches: [ main ]
branches: [ main, ci-setup ]
tags:
- 'v*'
- 'release-*'
pull_request:
branches: [ main ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

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
runs-on: ubuntu-latest
name: Build Android APK/AAB
runs-on: ubuntu-22.04
env:
CI: true
steps:
Expand All @@ -29,31 +51,81 @@ 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: Accept Android licenses
run: yes | sdkmanager --licenses

- 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

- 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 > android/key.properties <<'EOF'
storeFile=../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 }}

- name: Upload APKs
uses: actions/upload-artifact@v4
with:
name: android-apk
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:
name: android-aab
path: |
build/app/outputs/**/*.aab

ios:
name: Build iOS App (no codesign)
runs-on: macos-latest
runs-on: macos-14
env:
CI: true
steps:
Expand All @@ -66,16 +138,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 }}
Expand All @@ -89,4 +168,40 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: ios-app
path: build/ios/iphoneos/Runner.app.zip
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
16 changes: 16 additions & 0 deletions .github/workflows/preflight.yml
Original file line number Diff line number Diff line change
@@ -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 }}"
28 changes: 25 additions & 3 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}

// 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(java.io.FileInputStream(keystorePropertiesFile))
}

android {
namespace = "com.example.flutter_app"
compileSdk = flutter.compileSdkVersion
Expand All @@ -30,11 +37,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")
}
}
}
}
Expand Down
Loading