From 4b979fa403b78f6859b7998999e131dd7a6e2315 Mon Sep 17 00:00:00 2001 From: Ian Rumac Date: Fri, 10 Apr 2026 19:13:08 +0200 Subject: [PATCH 1/5] Split tests on CI to improve perf --- .github/workflows/build+test.yml | 136 +++++++++++++++++++++++++++---- .github/workflows/on-release.yml | 2 +- .github/workflows/pr-tests.yml | 122 +++++++++++++++++++++++++-- gradle/jacoco-combined.gradle | 8 +- 4 files changed, 239 insertions(+), 29 deletions(-) diff --git a/.github/workflows/build+test.yml b/.github/workflows/build+test.yml index 81a1dc20c..a0fdc0430 100644 --- a/.github/workflows/build+test.yml +++ b/.github/workflows/build+test.yml @@ -40,9 +40,78 @@ jobs: # Run Build & Test the Project - name: Build gradle project - run: ./gradlew build + run: ./gradlew build -x lint -x lintDebug -x lintVitalRelease - - name: Run unit and instrumentation tests with coverage + - name: Build test project + run: ./gradlew :app:assembleAndroidTest -DtestBuildType=debug -x lint -x lintDebug -x lintVitalRelease + + - name: Run tests on Firebase Test Lab + uses: asadmansr/Firebase-Test-Lab-Action@v1.0 + with: + arg-spec: '.github/firebase-tests.yml:android-pixel-7' + env: + SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }} + + unit-tests: + runs-on: macos-15-intel + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '17' + cache: 'gradle' + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Run unit tests with coverage + run: ./gradlew :superwall:testDebugUnitTest -x lint -x lintDebug -x lintVitalRelease + + - name: Upload unit test coverage data + uses: actions/upload-artifact@v4 + if: always() + with: + name: unit-test-coverage + path: superwall/build/jacoco/ + + - name: Upload unit test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: unit-test-reports + path: | + superwall/build/reports/tests/ + superwall/build/test-results/ + + instrumentation-tests: + runs-on: macos-15-intel + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '17' + cache: 'gradle' + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Run instrumentation tests with coverage uses: reactivecircus/android-emulator-runner@v2 with: api-level: 33 @@ -61,21 +130,59 @@ jobs: -camera-back none -camera-front none -no-snapshot-save - script: ./gradlew :superwall:jacocoFullReport + script: ./gradlew :superwall:connectedDebugAndroidTest -x lint -x lintDebug -x lintVitalRelease - - name: Build test project - run: ./gradlew :app:assembleAndroidTest -DtestBuildType=debug + - name: Upload instrumentation test coverage data + uses: actions/upload-artifact@v4 + if: always() + with: + name: instrumentation-test-coverage + path: superwall/build/outputs/code_coverage/ - - name: Upload test reports + - name: Upload instrumentation test reports uses: actions/upload-artifact@v4 if: always() with: - name: test-reports + name: instrumentation-test-reports path: | - **/build/reports/tests/ - **/build/test-results/ - **/build/reports/androidTests/connected/ - **/build/outputs/code_coverage/ + superwall/build/reports/androidTests/connected/ + superwall/build/outputs/androidTest-results/ + + coverage-report: + runs-on: macos-15-intel + needs: [unit-tests, instrumentation-tests] + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set Up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '17' + cache: 'gradle' + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Download unit test coverage data + uses: actions/download-artifact@v4 + with: + name: unit-test-coverage + path: superwall/build/jacoco/ + + - name: Download instrumentation test coverage data + uses: actions/download-artifact@v4 + with: + name: instrumentation-test-coverage + path: superwall/build/outputs/code_coverage/ + + - name: Generate combined coverage report + run: ./gradlew :superwall:compileDebugSources :superwall:jacocoTestReport -x testDebugUnitTest -x lint -x lintDebug -x lintVitalRelease - name: Upload coverage report uses: actions/upload-artifact@v4 @@ -107,10 +214,3 @@ jobs: git commit -m "Update coverage badge [skip ci]" git push origin HEAD:${{ github.head_ref }} fi - - - name: Run tests on Firebase Test Lab - uses: asadmansr/Firebase-Test-Lab-Action@v1.0 - with: - arg-spec: '.github/firebase-tests.yml:android-pixel-7' - env: - SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }} diff --git a/.github/workflows/on-release.yml b/.github/workflows/on-release.yml index 80a19bc3c..8e2e371ae 100644 --- a/.github/workflows/on-release.yml +++ b/.github/workflows/on-release.yml @@ -13,4 +13,4 @@ jobs: with: token: ${{ secrets.MAIN_REPO_PAT }} repository: superwall/paywall-next - event-type: android-release + event-type: android-release \ No newline at end of file diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 7bec972dd..45c15c85e 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -5,7 +5,7 @@ on: branches: [ develop ] jobs: - test: + unit-tests: runs-on: macos-15-intel steps: @@ -37,7 +37,58 @@ jobs: - name: Change wrapper permissions run: chmod +x ./gradlew - - name: Run unit and instrumentation tests with coverage + - name: Run unit tests with coverage + run: ./gradlew :superwall:testDebugUnitTest -x lint -x lintDebug -x lintVitalRelease + + - name: Upload unit test coverage data + uses: actions/upload-artifact@v4 + if: always() + with: + name: unit-test-coverage + path: superwall/build/jacoco/ + + - name: Upload unit test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: unit-test-reports + path: | + superwall/build/reports/tests/ + superwall/build/test-results/ + + instrumentation-tests: + runs-on: macos-15-intel + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Run instrumentation tests with coverage uses: reactivecircus/android-emulator-runner@v2 with: api-level: 33 @@ -56,18 +107,71 @@ jobs: -camera-back none -camera-front none -no-snapshot-save - script: ./gradlew :superwall:jacocoFullReport + script: ./gradlew :superwall:connectedDebugAndroidTest -x lint -x lintDebug -x lintVitalRelease - - name: Upload test reports + - name: Upload instrumentation test coverage data uses: actions/upload-artifact@v4 if: always() with: - name: test-reports + name: instrumentation-test-coverage + path: superwall/build/outputs/code_coverage/ + + - name: Upload instrumentation test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: instrumentation-test-reports + path: | + superwall/build/reports/androidTests/connected/ + superwall/build/outputs/androidTest-results/ + + coverage-report: + runs-on: macos-15-intel + needs: [unit-tests, instrumentation-tests] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Cache Gradle packages + uses: actions/cache@v4 + with: path: | - **/build/reports/tests/ - **/build/test-results/ - **/build/reports/androidTests/connected/ - **/build/outputs/code_coverage/ + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Change wrapper permissions + run: chmod +x ./gradlew + + - name: Download unit test coverage data + uses: actions/download-artifact@v4 + with: + name: unit-test-coverage + path: superwall/build/jacoco/ + + - name: Download instrumentation test coverage data + uses: actions/download-artifact@v4 + with: + name: instrumentation-test-coverage + path: superwall/build/outputs/code_coverage/ + + - name: Generate combined coverage report + run: ./gradlew :superwall:compileDebugSources :superwall:jacocoTestReport -x testDebugUnitTest -x lint -x lintDebug -x lintVitalRelease - name: Upload coverage report uses: actions/upload-artifact@v4 diff --git a/gradle/jacoco-combined.gradle b/gradle/jacoco-combined.gradle index 8f28556db..37be5679a 100644 --- a/gradle/jacoco-combined.gradle +++ b/gradle/jacoco-combined.gradle @@ -135,7 +135,13 @@ project.afterEvaluate { // Create combined report task that includes BOTH unit tests and instrumentation tests tasks.register('jacocoTestReport', JacocoReport) { - dependsOn 'testDebugUnitTest' + // compileDebugKotlin / compileDebugJavaWithJavac are listed explicitly so + // the JacocoReport task declares a producer dependency on the class + // directories it reads via fileTree(...). Without this, Gradle 8 strict + // task validation fails when this task runs without testDebugUnitTest in + // the same graph (e.g. the CI coverage-report job, which downloads the + // .exec from an artifact and excludes testDebugUnitTest). + dependsOn 'testDebugUnitTest', 'compileDebugKotlin', 'compileDebugJavaWithJavac' // Note: Instrumentation tests must be run separately before generating the combined report // Run: ./gradlew :superwall:connectedDebugAndroidTest From efd5bc2c055841161ca060ff61ee960cd34ff92a Mon Sep 17 00:00:00 2001 From: Ian Rumac Date: Fri, 10 Apr 2026 19:30:47 +0200 Subject: [PATCH 2/5] Enable caching, improve avd issues --- .github/workflows/build+test.yml | 131 ++++++++++++++++++++++--------- .github/workflows/pr-tests.yml | 99 +++++++++++++---------- gradle.properties | 6 +- 3 files changed, 156 insertions(+), 80 deletions(-) diff --git a/.github/workflows/build+test.yml b/.github/workflows/build+test.yml index a0fdc0430..5f2ad15c8 100644 --- a/.github/workflows/build+test.yml +++ b/.github/workflows/build+test.yml @@ -18,29 +18,30 @@ concurrency: jobs: build: - runs-on: macos-15-intel + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - - name: Set Up JDK - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - distribution: 'zulu' # See 'Supported distributions' for available options java-version: '17' - cache: 'gradle' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 # Allow us to run the command - name: Change wrapper permissions run: chmod +x ./gradlew - # Run Build & Test the Project - - name: Build gradle project - run: ./gradlew build -x lint -x lintDebug -x lintVitalRelease + # Assemble (no tests / no lint — those run in dedicated jobs) + - name: Assemble gradle project + run: ./gradlew assemble -x lint -x lintDebug -x lintVitalRelease - name: Build test project run: ./gradlew :app:assembleAndroidTest -DtestBuildType=debug -x lint -x lintDebug -x lintVitalRelease @@ -53,21 +54,22 @@ jobs: SERVICE_ACCOUNT: ${{ secrets.SERVICE_ACCOUNT }} unit-tests: - runs-on: macos-15-intel + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - - name: Set Up JDK - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - distribution: 'zulu' java-version: '17' - cache: 'gradle' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Change wrapper permissions run: chmod +x ./gradlew @@ -92,36 +94,62 @@ jobs: superwall/build/test-results/ instrumentation-tests: - runs-on: macos-15-intel + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - - name: Set Up JDK - uses: actions/setup-java@v3 + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - distribution: 'zulu' java-version: '17' - cache: 'gradle' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Change wrapper permissions run: chmod +x ./gradlew - - name: Run instrumentation tests with coverage + # Cache the AVD across runs. Key is shared across all 5 shards because they + # all use the same api-level/target/arch/profile, so they all share one snapshot. + - name: AVD cache + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-api33-google_apis-x86_64-pixel_7_pro-v1 + + # Cold-cache only: create the AVD and let the emulator save a snapshot on shutdown. + # Note the deliberate ABSENCE of -no-snapshot-save in emulator-options here, and + # disable-animations is left false so the snapshot is generic and reusable. + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' uses: reactivecircus/android-emulator-runner@v2 with: api-level: 33 target: google_apis arch: x86_64 profile: pixel_7_pro - force-avd-creation: true + force-avd-creation: false ram-size: 4096M emulator-boot-timeout: 1800 - disable-animations: true + disable-animations: false emulator-options: > -no-window -gpu swiftshader_indirect @@ -129,42 +157,67 @@ jobs: -noaudio -camera-back none -camera-front none + script: echo "Generated AVD snapshot for caching." + + - name: Run instrumentation tests with coverage (shard ${{ matrix.shard }}/5) + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 33 + target: google_apis + arch: x86_64 + profile: pixel_7_pro + force-avd-creation: false + ram-size: 4096M + emulator-boot-timeout: 1800 + disable-animations: true + emulator-options: > -no-snapshot-save - script: ./gradlew :superwall:connectedDebugAndroidTest -x lint -x lintDebug -x lintVitalRelease + -no-window + -gpu swiftshader_indirect + -no-boot-anim + -noaudio + -camera-back none + -camera-front none + script: > + ./gradlew :superwall:connectedDebugAndroidTest + -Pandroid.testInstrumentationRunnerArguments.numShards=5 + -Pandroid.testInstrumentationRunnerArguments.shardIndex=${{ matrix.shard }} + -x lint -x lintDebug -x lintVitalRelease - name: Upload instrumentation test coverage data uses: actions/upload-artifact@v4 if: always() with: - name: instrumentation-test-coverage + name: instrumentation-test-coverage-shard-${{ matrix.shard }} path: superwall/build/outputs/code_coverage/ - name: Upload instrumentation test reports uses: actions/upload-artifact@v4 if: always() with: - name: instrumentation-test-reports + name: instrumentation-test-reports-shard-${{ matrix.shard }} path: | superwall/build/reports/androidTests/connected/ superwall/build/outputs/androidTest-results/ coverage-report: - runs-on: macos-15-intel + runs-on: ubuntu-latest needs: [unit-tests, instrumentation-tests] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - fetch-depth: 0 ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} - - name: Set Up JDK - uses: actions/setup-java@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - distribution: 'zulu' java-version: '17' - cache: 'gradle' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 - name: Change wrapper permissions run: chmod +x ./gradlew @@ -175,10 +228,10 @@ jobs: name: unit-test-coverage path: superwall/build/jacoco/ - - name: Download instrumentation test coverage data + - name: Download instrumentation test coverage data (all shards) uses: actions/download-artifact@v4 with: - name: instrumentation-test-coverage + pattern: instrumentation-test-coverage-shard-* path: superwall/build/outputs/code_coverage/ - name: Generate combined coverage report diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 45c15c85e..0eddfa415 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -6,7 +6,7 @@ on: jobs: unit-tests: - runs-on: macos-15-intel + runs-on: ubuntu-latest steps: - name: Checkout code @@ -24,16 +24,6 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Change wrapper permissions run: chmod +x ./gradlew @@ -57,7 +47,11 @@ jobs: superwall/build/test-results/ instrumentation-tests: - runs-on: macos-15-intel + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3, 4] steps: - name: Checkout code @@ -66,6 +60,12 @@ jobs: ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -75,58 +75,87 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Cache Gradle packages + - name: Change wrapper permissions + run: chmod +x ./gradlew + + # Cache the AVD across runs. Key is shared across all 5 shards because they + # all use the same api-level/target/arch/profile, so they all share one snapshot. + - name: AVD cache uses: actions/cache@v4 + id: avd-cache with: path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Change wrapper permissions - run: chmod +x ./gradlew + ~/.android/avd/* + ~/.android/adb* + key: avd-api33-google_apis-x86_64-pixel_7_pro-v1 + + # Cold-cache only: create the AVD and let the emulator save a snapshot on shutdown. + # Note the deliberate ABSENCE of -no-snapshot-save in emulator-options here, and + # disable-animations is left false so the snapshot is generic and reusable. + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 33 + target: google_apis + arch: x86_64 + profile: pixel_7_pro + force-avd-creation: false + ram-size: 4096M + emulator-boot-timeout: 1800 + disable-animations: false + emulator-options: > + -no-window + -gpu swiftshader_indirect + -no-boot-anim + -noaudio + -camera-back none + -camera-front none + script: echo "Generated AVD snapshot for caching." - - name: Run instrumentation tests with coverage + - name: Run instrumentation tests with coverage (shard ${{ matrix.shard }}/5) uses: reactivecircus/android-emulator-runner@v2 with: api-level: 33 target: google_apis arch: x86_64 profile: pixel_7_pro - force-avd-creation: true + force-avd-creation: false ram-size: 4096M emulator-boot-timeout: 1800 disable-animations: true emulator-options: > + -no-snapshot-save -no-window -gpu swiftshader_indirect -no-boot-anim -noaudio -camera-back none -camera-front none - -no-snapshot-save - script: ./gradlew :superwall:connectedDebugAndroidTest -x lint -x lintDebug -x lintVitalRelease + script: > + ./gradlew :superwall:connectedDebugAndroidTest + -Pandroid.testInstrumentationRunnerArguments.numShards=5 + -Pandroid.testInstrumentationRunnerArguments.shardIndex=${{ matrix.shard }} + -x lint -x lintDebug -x lintVitalRelease - name: Upload instrumentation test coverage data uses: actions/upload-artifact@v4 if: always() with: - name: instrumentation-test-coverage + name: instrumentation-test-coverage-shard-${{ matrix.shard }} path: superwall/build/outputs/code_coverage/ - name: Upload instrumentation test reports uses: actions/upload-artifact@v4 if: always() with: - name: instrumentation-test-reports + name: instrumentation-test-reports-shard-${{ matrix.shard }} path: | superwall/build/reports/androidTests/connected/ superwall/build/outputs/androidTest-results/ coverage-report: - runs-on: macos-15-intel + runs-on: ubuntu-latest needs: [unit-tests, instrumentation-tests] steps: @@ -145,16 +174,6 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Change wrapper permissions run: chmod +x ./gradlew @@ -164,10 +183,10 @@ jobs: name: unit-test-coverage path: superwall/build/jacoco/ - - name: Download instrumentation test coverage data + - name: Download instrumentation test coverage data (all shards) uses: actions/download-artifact@v4 with: - name: instrumentation-test-coverage + pattern: instrumentation-test-coverage-shard-* path: superwall/build/outputs/code_coverage/ - name: Generate combined coverage report diff --git a/gradle.properties b/gradle.properties index f2ca36dbc..74e3dc583 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,8 @@ android.enableR8.debugMode=true android.enableR8.fullMode=true android.debug.obsoleteApi=true mavenCentralPublishing=true -signAllPublications=true \ No newline at end of file +signAllPublications=true +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configuration-cache=true +org.gradle.workers.max=4 From b1b5d13d0ae8a40715961c7c4450b535aa3cb4cd Mon Sep 17 00:00:00 2001 From: Ian Rumac Date: Fri, 10 Apr 2026 19:46:15 +0200 Subject: [PATCH 3/5] Fix issues w ci --- .github/workflows/build+test.yml | 14 ++++++++++++++ .github/workflows/pr-tests.yml | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/.github/workflows/build+test.yml b/.github/workflows/build+test.yml index 5f2ad15c8..b6af75175 100644 --- a/.github/workflows/build+test.yml +++ b/.github/workflows/build+test.yml @@ -106,6 +106,20 @@ jobs: ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} + # Reclaim ~10+ GB on the runner before the AVD cache restore + emulator boot. + # Ubuntu runners ship near-full and instrumentation jobs can hit "no space + # left on device" around the emulator boot or coverage write otherwise. + - name: Free disk space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: false # keep Android SDK / tools + tool-cache: true # rm -rf "$AGENT_TOOLSDIRECTORY" + dotnet: true # rm -rf /usr/share/dotnet + haskell: true # rm -rf /opt/ghc + swap-storage: true # rm -f /mnt/swapfile (4 GiB) + docker-images: false # slow (~16s), skip + large-packages: false # slow (includes google-cloud-sdk), skip + - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 0eddfa415..5dd12b682 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -60,6 +60,20 @@ jobs: ref: ${{ github.head_ref }} token: ${{ secrets.GITHUB_TOKEN }} + # Reclaim ~10+ GB on the runner before the AVD cache restore + emulator boot. + # Ubuntu runners ship near-full and instrumentation jobs can hit "no space + # left on device" around the emulator boot or coverage write otherwise. + - name: Free disk space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: false # keep Android SDK / tools + tool-cache: true # rm -rf "$AGENT_TOOLSDIRECTORY" + dotnet: true # rm -rf /usr/share/dotnet + haskell: true # rm -rf /opt/ghc + swap-storage: true # rm -f /mnt/swapfile (4 GiB) + docker-images: false # slow (~16s), skip + large-packages: false # slow (includes google-cloud-sdk), skip + - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules From de675c1695e14e62b06f90fc499aef3898dba850 Mon Sep 17 00:00:00 2001 From: Ian Rumac Date: Fri, 10 Apr 2026 20:16:29 +0200 Subject: [PATCH 4/5] Fix review issues --- .github/workflows/build+test.yml | 79 +++++++++++++++++++++++++++++ .github/workflows/pr-tests.yml | 80 ++++++++++++++++++++++++++++++ superwall-compose/build.gradle.kts | 23 +++++---- superwall/build.gradle.kts | 23 +++++---- 4 files changed, 187 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build+test.yml b/.github/workflows/build+test.yml index b6af75175..7c8c8db82 100644 --- a/.github/workflows/build+test.yml +++ b/.github/workflows/build+test.yml @@ -93,8 +93,84 @@ jobs: superwall/build/reports/tests/ superwall/build/test-results/ + # Single-runner pre-job that populates the AVD snapshot cache before the matrix + # fans out, so the 5 shards don't race to create the same snapshot on a cold cache. + # On a warm cache this job is ~30s of overhead (checkout + cache restore + skip). + warm-emulator-cache: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: AVD cache + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-api33-google_apis-x86_64-pixel_7_pro-v1 + + # Everything below only runs on a cold cache. On a warm cache the job ends here. + - name: Free disk space + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: false + tool-cache: true + dotnet: true + haskell: true + swap-storage: true + docker-images: false + large-packages: false + + - name: Enable KVM + if: steps.avd-cache.outputs.cache-hit != 'true' + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Set up JDK 17 + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: gradle/actions/setup-gradle@v3 + + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 33 + target: google_apis + arch: x86_64 + profile: pixel_7_pro + force-avd-creation: false + ram-size: 4096M + emulator-boot-timeout: 1800 + disable-animations: false + emulator-options: > + -no-window + -gpu swiftshader_indirect + -no-boot-anim + -noaudio + -camera-back none + -camera-front none + script: echo "Generated AVD snapshot for caching." + instrumentation-tests: runs-on: ubuntu-latest + needs: [warm-emulator-cache] + # Run even if warm-emulator-cache failed — the per-shard warm-up step below + # acts as a fallback. Don't run on workflow cancellation. + if: ${{ !cancelled() }} strategy: fail-fast: false matrix: @@ -217,6 +293,9 @@ jobs: coverage-report: runs-on: ubuntu-latest needs: [unit-tests, instrumentation-tests] + # Run as long as unit-tests succeeded, even if some/all instrumentation + # shards failed — a partial report is better than no report or badge update. + if: ${{ !cancelled() && needs.unit-tests.result == 'success' }} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pr-tests.yml b/.github/workflows/pr-tests.yml index 5dd12b682..999a6cc67 100644 --- a/.github/workflows/pr-tests.yml +++ b/.github/workflows/pr-tests.yml @@ -46,8 +46,85 @@ jobs: superwall/build/reports/tests/ superwall/build/test-results/ + # Single-runner pre-job that populates the AVD snapshot cache before the matrix + # fans out, so the 5 shards don't race to create the same snapshot on a cold cache. + # On a warm cache this job is ~30s of overhead (checkout + cache restore + skip). + warm-emulator-cache: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: AVD cache + uses: actions/cache@v4 + id: avd-cache + with: + path: | + ~/.android/avd/* + ~/.android/adb* + key: avd-api33-google_apis-x86_64-pixel_7_pro-v1 + + # Everything below only runs on a cold cache. On a warm cache the job ends here. + - name: Free disk space + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: jlumbroso/free-disk-space@v1.3.1 + with: + android: false + tool-cache: true + dotnet: true + haskell: true + swap-storage: true + docker-images: false + large-packages: false + + - name: Enable KVM + if: steps.avd-cache.outputs.cache-hit != 'true' + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Set up JDK 17 + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Setup Gradle + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: gradle/actions/setup-gradle@v3 + + - name: Create AVD and generate snapshot for caching + if: steps.avd-cache.outputs.cache-hit != 'true' + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 33 + target: google_apis + arch: x86_64 + profile: pixel_7_pro + force-avd-creation: false + ram-size: 4096M + emulator-boot-timeout: 1800 + disable-animations: false + emulator-options: > + -no-window + -gpu swiftshader_indirect + -no-boot-anim + -noaudio + -camera-back none + -camera-front none + script: echo "Generated AVD snapshot for caching." + instrumentation-tests: runs-on: ubuntu-latest + needs: [warm-emulator-cache] + # Run even if warm-emulator-cache failed — the per-shard warm-up step below + # acts as a fallback. Don't run on workflow cancellation. + if: ${{ !cancelled() }} strategy: fail-fast: false matrix: @@ -171,6 +248,9 @@ jobs: coverage-report: runs-on: ubuntu-latest needs: [unit-tests, instrumentation-tests] + # Run as long as unit-tests succeeded, even if some/all instrumentation + # shards failed — a partial report is better than no report or badge update. + if: ${{ !cancelled() && needs.unit-tests.result == 'success' }} steps: - name: Checkout code diff --git a/superwall-compose/build.gradle.kts b/superwall-compose/build.gradle.kts index b5abb4710..c0f55be93 100644 --- a/superwall-compose/build.gradle.kts +++ b/superwall-compose/build.gradle.kts @@ -1,5 +1,4 @@ import groovy.json.JsonBuilder -import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Date @@ -23,14 +22,20 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - val gitSha = - project - .exec { - commandLine("git", "rev-parse", "--short", "HEAD") - standardOutput = ByteArrayOutputStream() - }.toString() - .trim() - buildConfigField("String", "GIT_SHA", "\"${gitSha}\"") + // Configuration-cache safe: use GITHUB_SHA from CI when set, otherwise + // fall back to `git rev-parse` via providers.exec (tracked Provider API). + // Final fallback prevents a crash if .git is missing. + val gitSha: String = runCatching { + providers + .environmentVariable("GITHUB_SHA") + .map { it.take(7) } + .orElse( + providers.exec { + commandLine("git", "rev-parse", "--short", "HEAD") + }.standardOutput.asText.map { it.trim() }, + ).get() + }.getOrDefault("unknown") + buildConfigField("String", "GIT_SHA", "\"$gitSha\"") val currentTime = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date()) buildConfigField("String", "BUILD_TIME", "\"${currentTime}\"") diff --git a/superwall/build.gradle.kts b/superwall/build.gradle.kts index 267012b60..58b9e38a4 100644 --- a/superwall/build.gradle.kts +++ b/superwall/build.gradle.kts @@ -1,6 +1,5 @@ import groovy.json.JsonBuilder -import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Date @@ -46,14 +45,20 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true - val gitSha = - project - .exec { - commandLine("git", "rev-parse", "--short", "HEAD") - standardOutput = ByteArrayOutputStream() - }.toString() - .trim() - buildConfigField("String", "GIT_SHA", "\"${gitSha}\"") + // Configuration-cache safe: use GITHUB_SHA from CI when set, otherwise + // fall back to `git rev-parse` via providers.exec (tracked Provider API). + // Final fallback prevents a crash if .git is missing. + val gitSha: String = runCatching { + providers + .environmentVariable("GITHUB_SHA") + .map { it.take(7) } + .orElse( + providers.exec { + commandLine("git", "rev-parse", "--short", "HEAD") + }.standardOutput.asText.map { it.trim() }, + ).get() + }.getOrDefault("unknown") + buildConfigField("String", "GIT_SHA", "\"$gitSha\"") val currentTime = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(Date()) buildConfigField("String", "BUILD_TIME", "\"${currentTime}\"") From c647babbaab883c78567aed081bba89c44394daa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 10 Apr 2026 18:39:33 +0000 Subject: [PATCH 5/5] Update coverage badge [skip ci] --- .github/badges/branches.svg | 2 +- .github/badges/jacoco.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg index 363f11af4..c1f4c6f7c 100644 --- a/.github/badges/branches.svg +++ b/.github/badges/branches.svg @@ -1 +1 @@ -branches39% \ No newline at end of file +branches28.7% \ No newline at end of file diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg index ea74503b7..6fafb1807 100644 --- a/.github/badges/jacoco.svg +++ b/.github/badges/jacoco.svg @@ -1 +1 @@ -coverage54.4% \ No newline at end of file +coverage37.6% \ No newline at end of file