diff --git a/.gitea/workflows/build-apk.yaml b/.gitea/workflows/build-apk.yaml index cb51eb9..c504034 100644 --- a/.gitea/workflows/build-apk.yaml +++ b/.gitea/workflows/build-apk.yaml @@ -14,6 +14,12 @@ on: - master workflow_dispatch: +# Only one build per ref at a time: two QEMU-emulated Gradle builds in parallel +# take the ARM64 runner down (both runs then die at the same instant). +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: # Keep in sync with .github/workflows/build-ios.yaml and pubspec.yaml (environment). FLUTTER_VERSION: '3.47.4' @@ -25,7 +31,7 @@ jobs: build-android: name: Build Release APKs runs-on: ubuntu-latest - timeout-minutes: 180 + timeout-minutes: 60 permissions: contents: write container: @@ -119,8 +125,8 @@ jobs: # the NDK Flutter expects up front and swap in Ubuntu's native arm64 llvm-strip. NDK_VERSION="$(sed -nE 's/.*ndkVersion: String = "([^"]+)".*/\1/p' "${FLUTTER_ROOT:-/sdks/flutter}/packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt")" [ -n "$NDK_VERSION" ] || { echo "::error::Could not determine Flutter's NDK version"; exit 1; } - echo "Installing NDK $NDK_VERSION and Android SDK platforms 34 & 35 ..." - yes | sdkmanager --install "platforms;android-34" "platforms;android-35" "ndk;${NDK_VERSION}" > /dev/null + echo "Installing NDK $NDK_VERSION ..." + yes | sdkmanager --install "ndk;${NDK_VERSION}" > /dev/null NDK_BIN="${ANDROID_HOME:-/opt/android-sdk-linux}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/linux-x86_64/bin" for tool in llvm-strip llvm-objcopy; do mv "$NDK_BIN/$tool" "$NDK_BIN/$tool.x86_64" @@ -163,68 +169,23 @@ jobs: flutter analyze flutter test - - name: Build Release APKs (Split-per-ABI & Universal) - run: | - flutter build apk --release --split-per-abi - mkdir -p release-apks - # Locate universal APK generated by AGP splits - for f in \ - build/app/outputs/flutter-apk/app-universal-release.apk \ - build/app/outputs/flutter-apk/*universal*.apk \ - android/app/build/outputs/apk/release/app-universal-release.apk \ - android/app/build/outputs/apk/release/*universal*.apk \ - build/app/outputs/flutter-apk/app-release.apk \ - android/app/build/outputs/apk/release/app-release.apk; do - if [ -f "$f" ]; then - echo "Found universal APK at $f" - cp "$f" release-apks/oggauer-gemeindetrommler-universal.apk - break - fi - done - # Fallback: if universal APK was not created by split build, build it explicitly - if [ ! -f release-apks/oggauer-gemeindetrommler-universal.apk ]; then - echo "Universal APK not found from split build; running fallback universal build..." - flutter build apk --release - if [ -f build/app/outputs/flutter-apk/app-release.apk ]; then - cp build/app/outputs/flutter-apk/app-release.apk release-apks/oggauer-gemeindetrommler-universal.apk - elif [ -f android/app/build/outputs/apk/release/app-release.apk ]; then - cp android/app/build/outputs/apk/release/app-release.apk release-apks/oggauer-gemeindetrommler-universal.apk - fi - fi + - name: Build Release APKs (Split-per-ABI + Universal) + # android/app/build.gradle re-enables the universal APK for split builds, so + # this one Gradle run yields all four APKs in build/app/outputs/flutter-apk/. + run: flutter build apk --release --split-per-abi - name: Organize Artifacts run: | mkdir -p release-apks - # Copy universal APK if not already in release-apks - if [ ! -f release-apks/oggauer-gemeindetrommler-universal.apk ]; then - for f in \ - build/app/outputs/flutter-apk/app-universal-release.apk \ - build/app/outputs/flutter-apk/*universal*.apk \ - android/app/build/outputs/apk/release/app-universal-release.apk \ - android/app/build/outputs/apk/release/*universal*.apk \ - build/app/outputs/flutter-apk/app-release.apk \ - android/app/build/outputs/apk/release/app-release.apk; do - if [ -f "$f" ]; then - cp "$f" release-apks/oggauer-gemeindetrommler-universal.apk - break - fi - done - fi - # Copy split APKs - for apk in build/app/outputs/flutter-apk/*.apk android/app/build/outputs/apk/release/*.apk; do - [ -f "$apk" ] || continue - case "$(basename "$apk")" in - *arm64-v8a*) cp "$apk" release-apks/oggauer-gemeindetrommler-arm64-v8a.apk ;; - *armeabi-v7a*) cp "$apk" release-apks/oggauer-gemeindetrommler-armeabi-v7a.apk ;; - *x86_64*) cp "$apk" release-apks/oggauer-gemeindetrommler-x86_64.apk ;; - esac - done + APK_DIR=build/app/outputs/flutter-apk + echo "=== Flutter APK outputs ===" + ls -lah "$APK_DIR" + cp "$APK_DIR/app-release.apk" release-apks/oggauer-gemeindetrommler-universal.apk + cp "$APK_DIR/app-arm64-v8a-release.apk" release-apks/oggauer-gemeindetrommler-arm64-v8a.apk + cp "$APK_DIR/app-armeabi-v7a-release.apk" release-apks/oggauer-gemeindetrommler-armeabi-v7a.apk + cp "$APK_DIR/app-x86_64-release.apk" release-apks/oggauer-gemeindetrommler-x86_64.apk echo "=== Generated APK Artifacts ===" ls -lah release-apks/ - if [ "$(ls release-apks/*.apk 2>/dev/null | wc -l)" -ne 4 ]; then - echo "::error::Expected 4 APKs in release-apks/, the build did not produce all artifacts" - exit 1 - fi - name: Upload Universal APK uses: actions/upload-artifact@v3 diff --git a/android/app/build.gradle b/android/app/build.gradle index 30aa6a4..601754b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,26 +34,29 @@ android { 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.debug } } - splits { - abi { - enable true - reset() - include "arm64-v8a", "armeabi-v7a", "x86_64" - universalApk true + // `flutter build apk --split-per-abi` makes the Flutter Gradle plugin enable ABI + // splits for the requested target platforms but switch the universal APK off. + // Turn it back on so a single Gradle run yields the per-ABI APKs *and* the + // universal one (the plugin copies all of them to build/app/outputs/flutter-apk/, + // the universal one as app-release.apk). Only when splitting: for plain builds + // the plugin sets ndk.abiFilters instead, which AGP does not allow together + // with splits. Do not use AGP's `packageReleaseUniversalApk` task for this - + // it derives the universal APK from an App Bundle, and bundles cannot be + // built while ABI splits are enabled. + if (project.findProperty("split-per-abi")?.toString()?.toBoolean()) { + splits { + abi { + universalApk true + } } } } -afterEvaluate { - tasks.named('assembleRelease').configure { - dependsOn 'packageReleaseUniversalApk' - } -} - kotlin { compilerOptions { jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17