test: Port tests to Roboelectric #356
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # about runners https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories | |
| name: CNPlus CI (Android) App Build | |
| env: | |
| # The name of the main module repository | |
| main_project_module: app | |
| # The name of the Play Store | |
| app_name: "Calendar Notifications Plus" | |
| app_file_name_prefix: "calendar_notifications_plus" | |
| # Explicitly set CI flag to true for our Gradle task | |
| CI: true | |
| # Android Emulator Configuration | |
| ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL: 5 | |
| ANDROID_API_LEVEL: 34 | |
| ANDROID_TARGET: google_apis | |
| ANDROID_ARCH: x86_64 | |
| ANDROID_PROFILE: 7.6in Foldable | |
| ANDROID_BUILD_TOOLS_VERSION: "34.0.0" | |
| # Define constant for each job to use | |
| GRADLE_OPTS: "-Dorg.gradle.daemon=true -Dorg.gradle.workers.max=4 -Dorg.gradle.parallel=true -Dorg.gradle.caching=true" | |
| # Setup ccache environment variables | |
| CCACHE_DEBUG: 1 | |
| CCACHE_VERBOSE: 1 | |
| CCACHE_MAXSIZE: 5G | |
| CCACHE_BASEDIR: ${{ github.workspace }} | |
| on: | |
| push: | |
| tags: | |
| - '**' | |
| pull_request: | |
| branches: | |
| - "**" # https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#patterns-to-match-branches-and-tags | |
| # Allows you to run this workflow manually from the Actions tab | |
| workflow_dispatch: | |
| jobs: | |
| set_build_datetime: | |
| name: Set Build Datetime | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set Build Datetime | |
| id: set_date | |
| run: | | |
| echo "build_datetime=$(date +'%Y-%m-%d %H_%M_%S')" >> $GITHUB_OUTPUT | |
| outputs: | |
| build_datetime: ${{ steps.set_date.outputs.build_datetime }} | |
| build: | |
| name: Build Android App | |
| needs: set_build_datetime | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| arch: [arm64-v8a, x86_64] | |
| fail-fast: false | |
| env: | |
| ENTRY_FILE: "index.tsx" | |
| GRADLE_ABI: ${{ matrix.arch }} | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| timeout-minutes: 30 | |
| outputs: | |
| repository_name: ${{ steps.set_repo.outputs.repository_name }} | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 1 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| create-symlink: true | |
| # Set repository name as env variable | |
| - name: Set repository name as env variable | |
| id: set_repo | |
| run: | | |
| echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_ENV | |
| echo "repository_name=$(echo '${{ github.repository }}' | awk -F '/' '{print $2}')" >> $GITHUB_OUTPUT | |
| - name: Common Setup | |
| id: common-setup | |
| uses: ./.github/actions/common-setup | |
| with: | |
| gradle_max_workers: "4" | |
| node_version: "22.x" | |
| arch: ${{ matrix.arch }} | |
| # Debug Main Build Task Graph | |
| - name: Debug Main Build Task Graph | |
| run: | | |
| cd android && \ | |
| echo "=== Main Build Task Graph ===" && \ | |
| ./gradlew -PBUILD_ARCH="${BUILD_ARCH}" \ | |
| -PreactNativeArchitectures="${BUILD_ARCH}" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Debug" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Release" \ | |
| ":${{ env.main_project_module }}:bundle${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Release" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugAndroidTest" \ | |
| --dry-run \ | |
| --info \ | |
| --console=verbose \ | |
| --parallel --max-workers=4 --build-cache --configure-on-demand | |
| env: | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| - name: Ccachify the Native Modules | |
| shell: bash | |
| run: | | |
| chmod +x scripts/ccachify_native_modules.sh | |
| ./scripts/ccachify_native_modules.sh | |
| - name: Build Android APKs (Debug and Release) with Gradle | |
| run: | | |
| echo "Building for architecture: $BUILD_ARCH" | |
| cd android && \ | |
| # Pass BUILD_ARCH directly to Gradle process to ensure it's available during configuration | |
| ./gradlew -PBUILD_ARCH="${BUILD_ARCH}" \ | |
| -PreactNativeArchitectures="${BUILD_ARCH}" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Debug" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Release" \ | |
| ":${{ env.main_project_module }}:bundle${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}Release" \ | |
| ":${{ env.main_project_module }}:assemble${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugAndroidTest" \ | |
| --parallel --max-workers=4 --build-cache --configure-on-demand | |
| env: | |
| # Explicitly set BUILD_ARCH for Gradle process | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| - name: Setup tmate session | |
| if: ${{ failure() }} | |
| uses: mxschmitt/action-tmate@v3 | |
| with: | |
| limit-access-to-actor: true | |
| # Save the build artifacts to be used in other jobs | |
| - name: Upload APK artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-apk-artifacts-${{ matrix.arch }} | |
| path: android/${{ env.main_project_module }}/build/outputs/apk/ | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # Save the build artifacts to be used in other jobs | |
| - name: Upload Artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-coverage-artifacts-${{ matrix.arch }} | |
| path: | | |
| android/${{ env.main_project_module }}/build/intermediates/classes | |
| android/${{ env.main_project_module }}/build/intermediates/javac | |
| android/${{ env.main_project_module }}/build/tmp/kotlin-classes | |
| retention-days: 1 | |
| if-no-files-found: error | |
| - name: Upload Bundle artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-bundle-artifacts-${{ matrix.arch }} | |
| path: android/${{ env.main_project_module }}/build/outputs/bundle/ | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # Update all caches | |
| - name: Update Caches | |
| if: always() | |
| uses: ./.github/actions/cache-update | |
| with: | |
| arch: ${{ matrix.arch }} | |
| sign: | |
| name: Sign Android APKs and Bundles (${{ matrix.arch }}) | |
| needs: [set_build_datetime, build] | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| arch: [arm64-v8a, x86_64] | |
| env: | |
| ENTRY_FILE: "index.tsx" | |
| GRADLE_ABI: ${{ matrix.arch }} | |
| timeout-minutes: 10 | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 1 | |
| # Import build variables from build job | |
| - name: Set build variables | |
| run: | | |
| echo "repository_name=${{ needs.build.outputs.repository_name }}" >> $GITHUB_ENV | |
| # Download build artifacts for current architecture | |
| - name: Download APK artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-apk-artifacts-${{ matrix.arch }} | |
| path: artifacts/apk/ | |
| - name: Download Bundle artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-bundle-artifacts-${{ matrix.arch }} | |
| path: artifacts/bundle/ | |
| # Prepare directory structure for signing | |
| - name: Prepare directories for signing | |
| run: | | |
| mkdir -p signing/apk/debug | |
| mkdir -p signing/apk/release | |
| mkdir -p signing/bundle/release | |
| # Organize APKs by type | |
| find artifacts/apk/ -path "*/debug/*" -name "*.apk" -exec cp {} signing/apk/debug/ \; | |
| find artifacts/apk/ -path "*/release/*" -name "*.apk" -exec cp {} signing/apk/release/ \; | |
| find artifacts/bundle/ -name "*.aab" -exec cp {} signing/bundle/release/ \; | |
| # Verify contents | |
| echo "Files to sign:" | |
| find signing -type f | sort | |
| # Sign the APKs and AAB | |
| - uses: r0adkll/sign-android-release@v1 | |
| name: Sign debug app APKs | |
| id: sign_debug_app | |
| with: | |
| releaseDirectory: signing/apk/debug/ | |
| signingKeyBase64: ${{ secrets.DEBUG_SIGNING_KEYSTORE }} | |
| alias: ${{ secrets.DEBUG_KEYSTORE_ALIAS }} | |
| keyStorePassword: ${{ secrets.DEBUG_KEYSTORE_PASSWORD }} | |
| keyPassword: ${{ secrets.DEBUG_KEYSTORE_ALIAS_PASS }} | |
| env: | |
| BUILD_TOOLS_VERSION: ${{ env.ANDROID_BUILD_TOOLS_VERSION }} | |
| - uses: r0adkll/sign-android-release@v1 | |
| name: Sign RELEASE app APKs | |
| id: sign_release_app | |
| with: | |
| releaseDirectory: signing/apk/release/ | |
| signingKeyBase64: ${{ secrets.RELEASE_SIGNING_KEYSTORE }} | |
| alias: ${{ secrets.RELEASE_KEYSTORE_ALIAS }} | |
| keyStorePassword: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} | |
| keyPassword: ${{ secrets.RELEASE_KEYSTORE_ALIAS_PASS }} | |
| env: | |
| BUILD_TOOLS_VERSION: ${{ env.ANDROID_BUILD_TOOLS_VERSION }} | |
| - uses: r0adkll/sign-android-release@v1 | |
| name: Sign RELEASE app AABs | |
| id: sign_release_aab | |
| with: | |
| releaseDirectory: signing/bundle/release/ | |
| signingKeyBase64: ${{ secrets.RELEASE_SIGNING_KEYSTORE }} | |
| alias: ${{ secrets.RELEASE_KEYSTORE_ALIAS }} | |
| keyStorePassword: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }} | |
| keyPassword: ${{ secrets.RELEASE_KEYSTORE_ALIAS_PASS }} | |
| env: | |
| BUILD_TOOLS_VERSION: ${{ env.ANDROID_BUILD_TOOLS_VERSION }} | |
| # Rename APKs | |
| - name: Rename and organize signed artifacts | |
| run: | | |
| mkdir -p renamed_apks | |
| sanitize_branch_name() { | |
| echo "$1" | sed 's/[^a-zA-Z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//' | cut -c1-20 | |
| } | |
| if [[ $GITHUB_REF == refs/tags/* ]]; then | |
| # For tags | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| SUFFIX="-${VERSION}" | |
| elif [[ $GITHUB_REF == refs/pull/* ]]; then | |
| # For pull requests | |
| PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }') | |
| BRANCH_NAME=$(sanitize_branch_name "$GITHUB_HEAD_REF") | |
| SHORT_SHA=$(echo $GITHUB_SHA | cut -c1-7) | |
| SUFFIX="-pr-${PR_NUMBER}-${BRANCH_NAME}-${SHORT_SHA}" | |
| else | |
| # For pushes to branches other than main | |
| BRANCH_NAME=$(sanitize_branch_name "${GITHUB_REF#refs/heads/}") | |
| SHORT_SHA=$(echo $GITHUB_SHA | cut -c1-7) | |
| SUFFIX="-${BRANCH_NAME}-${SHORT_SHA}" | |
| fi | |
| # Find and rename signed debug APKs | |
| find signing/apk/debug/ -name "*-signed.apk" | while read apk; do | |
| cp "$apk" renamed_apks/${{env.app_file_name_prefix}}${SUFFIX}-${{ matrix.arch }}-debug.apk | |
| done | |
| # Find and rename signed release APKs | |
| find signing/apk/release/ -name "*-signed.apk" | while read apk; do | |
| cp "$apk" renamed_apks/${{env.app_file_name_prefix}}${SUFFIX}-${{ matrix.arch }}-release.apk | |
| done | |
| # Find and rename signed AABs | |
| find signing/bundle/release/ -name "*-signed.aab" | while read aab; do | |
| cp "$aab" renamed_apks/${{env.app_file_name_prefix}}${SUFFIX}-${{ matrix.arch }}.aab | |
| done | |
| # List all renamed files | |
| echo "Renamed files:" | |
| ls -la renamed_apks/ | |
| # Upload signed artifacts | |
| - name: Upload SIGNED APK Debug | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "signed-${{ needs.set_build_datetime.outputs.build_datetime }}-${{ env.app_name }}-APK-debug-${{ matrix.arch }}" | |
| path: renamed_apks/${{env.app_file_name_prefix}}-*-debug.apk | |
| - name: Upload SIGNED APK RELEASE | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "signed-${{ needs.set_build_datetime.outputs.build_datetime }}-${{ env.app_name }}-APK-release-${{ matrix.arch }}" | |
| path: renamed_apks/${{env.app_file_name_prefix}}-*-release.apk | |
| - name: Upload SIGNED AAB (App Bundle) Release | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: "signed-${{ needs.set_build_datetime.outputs.build_datetime }}-${{ env.app_name }}-AAB-release-${{ matrix.arch }}" | |
| path: renamed_apks/${{env.app_file_name_prefix}}-*.aab | |
| comment-pr: | |
| name: Comment on PR with build links | |
| if: github.event_name == 'pull_request' | |
| needs: sign | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Comment PR | |
| uses: actions/github-script@v6 | |
| with: | |
| github-token: ${{secrets.GITHUB_TOKEN}} | |
| script: | | |
| const workflowUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `Build artifacts for PR #${context.issue.number} (commit ${context.sha}) are available: | |
| - [Debug APKs (arm64-v8a, x86_64)](${workflowUrl}#artifacts) | |
| - [Release APKs (arm64-v8a, x86_64)](${workflowUrl}#artifacts) | |
| - [AAB](${workflowUrl}#artifacts) | |
| You can download these artifacts from the "Artifacts" section of the workflow run.` | |
| }); | |
| unit-tests: | |
| name: Run Unit Tests | |
| runs-on: ubuntu-latest | |
| needs: set_build_datetime | |
| timeout-minutes: 15 | |
| strategy: | |
| matrix: | |
| arch: [x86_64] # Only test on x86_64 to save time and resources | |
| fail-fast: false | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 1 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| create-symlink: true | |
| # Execute the common setup | |
| - name: Common Setup | |
| uses: ./.github/actions/common-setup | |
| with: | |
| optional_cache_key: "unit-tests" | |
| gradle_max_workers: "4" | |
| node_version: "22.x" | |
| arch: ${{ matrix.arch }} | |
| # Debug unit test task graph | |
| - name: Debug Unit Test Task Graph | |
| run: | | |
| cd android && \ | |
| echo "=== Unit Test Task Graph ===" && \ | |
| ./gradlew -PBUILD_ARCH="${{ matrix.arch }}" \ | |
| -PreactNativeArchitectures="${{ matrix.arch }}" \ | |
| :${{ env.main_project_module }}:test${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugUnitTest \ | |
| :${{ env.main_project_module }}:create${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugUnitTestCoverageReport \ | |
| --dry-run \ | |
| --info \ | |
| --console=verbose \ | |
| --continue --configure-on-demand | |
| env: | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| # Run the unit tests | |
| - name: Run Unit Tests | |
| run: | | |
| cd android && \ | |
| ./gradlew -PBUILD_ARCH="${{ matrix.arch }}" \ | |
| -PreactNativeArchitectures="${{ matrix.arch }}" \ | |
| :${{ env.main_project_module }}:generatePackageList \ | |
| :${{ env.main_project_module }}:test${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugUnitTest \ | |
| :${{ env.main_project_module }}:create${{ matrix.arch == 'arm64-v8a' && 'Arm64V8a' || 'X8664' }}DebugUnitTestCoverageReport \ | |
| --continue --configure-on-demand \ | |
| -Pandroid.externalNativeBuild.skip=true \ | |
| -x preBuild -x preDebugBuild -x preReleaseBuild | |
| env: | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| # Generate Jacoco coverage report | |
| # always() is used to ensure the coverage report is generated even if the tests fail | |
| - name: Generate Jacoco Coverage Report | |
| if: always() | |
| run: | | |
| cd android && \ | |
| ./gradlew jacocoAndroidTestReport --parallel --max-workers=4 --build-cache --info --stacktrace \ | |
| -Pandroid.externalNativeBuild.skip=true | |
| # Publish test results using dorny/test-reporter | |
| - name: Publish Unit Test Results | |
| uses: dorny/test-reporter@v2 | |
| if: always() | |
| with: | |
| name: 'Unit Tests' | |
| path: 'android/${{ env.main_project_module }}/build/test-results/test*DebugUnitTest/**/*.xml' | |
| reporter: java-junit | |
| use-actions-summary: 'true' | |
| fail-on-error: true | |
| # Upload test results | |
| - name: Upload Unit Test Results | |
| if: always() # Always upload results even if tests fail | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: unit-test-results-${{ needs.set_build_datetime.outputs.build_datetime }} | |
| path: | | |
| android/app/build/reports/ | |
| android/app/build/test-results/ | |
| android/app/build/outputs/logs/ | |
| retention-days: 90 | |
| # connected-android-integration-test: | |
| # name: Test Android App | |
| # needs: [build] | |
| # runs-on: ubuntu-latest | |
| # strategy: | |
| # matrix: | |
| # arch: [x86_64] # Only test on x86_64 to save time and resources | |
| # fail-fast: false | |
| # env: | |
| # ENTRY_FILE: "index.tsx" | |
| # GRADLE_ABI: ${{ matrix.arch }} | |
| # BUILD_ARCH: ${{ matrix.arch }} | |
| # timeout-minutes: 30 | |
| # steps: | |
| # - uses: actions/checkout@v3 | |
| # with: | |
| # fetch-depth: 1 | |
| # # Execute the common setup with emulator configuration | |
| # - name: Common Setup | |
| # id: common-setup | |
| # uses: ./.github/actions/common-setup | |
| # with: | |
| # gradle_max_workers: "4" | |
| # node_version: "22.x" | |
| # arch: ${{ matrix.arch }} | |
| # android_api_level: ${{ env.ANDROID_API_LEVEL }} | |
| # android_target: ${{ env.ANDROID_TARGET }} | |
| # android_profile: ${{ env.ANDROID_PROFILE }} | |
| # run_emulator_setup: "true" | |
| # optional_cache_key: "connected-android-integration-test" | |
| # - name: Make scripts executable | |
| # run: | | |
| # chmod +x scripts/wait_for_emulator.sh | |
| # chmod +x scripts/run_android_tests.sh | |
| # - name: Download APK artifacts | |
| # uses: actions/download-artifact@v4 | |
| # with: | |
| # name: android-apk-artifacts-${{ matrix.arch }} | |
| # path: android/app/build/outputs/apk | |
| # - name: Run Android Tests | |
| # id: run_tests | |
| # uses: reactivecircus/android-emulator-runner@v2 | |
| # with: | |
| # api-level: ${{ env.ANDROID_API_LEVEL }} | |
| # target: ${{ env.ANDROID_TARGET }} | |
| # arch: ${{ matrix.arch }} | |
| # profile: ${{ env.ANDROID_PROFILE }} | |
| # avd-name: 'connected-android-integration-test' | |
| # force-avd-creation: false | |
| # emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot -memory 2048 | |
| # disable-animations: true | |
| # script: ./scripts/wait_for_emulator.sh && ./scripts/deprecated_run_android_tests.sh ${{ matrix.arch }} ${{ env.main_project_module }} ${{ env.ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL }} | |
| # # Step 3: Generate JaCoCo coverage report | |
| # - name: Generate Jacoco Coverage Report | |
| # if: always() | |
| # run: | | |
| # cd android && \ | |
| # ./gradlew jacocoAndroidTestReport --parallel --max-workers=4 --build-cache --info --stacktrace | |
| # # Publish integration test results | |
| # - name: Publish Integration Test Results | |
| # uses: dorny/test-reporter@v2 | |
| # if: always() | |
| # with: | |
| # name: 'Integration Tests' | |
| # path: | | |
| # android/${{ env.main_project_module }}/build/outputs/androidTest-results/connected/**/*.xml | |
| # android/${{ env.main_project_module }}/build/outputs/connected/**/*.xml | |
| # reporter: java-junit | |
| # fail-on-error: true | |
| # - name: Upload Coverage Report | |
| # if: always() | |
| # uses: actions/upload-artifact@v4 | |
| # with: | |
| # name: android-test-coverage-report | |
| # path: | | |
| # android/app/build/reports/ | |
| # android/app/build/reports/jacoco/jacocoAndroidTestReport/ | |
| # android/app/build/outputs/code_coverage/ | |
| # android/app/build/outputs/logs/ | |
| # android/app/build/test-results/ | |
| # retention-days: 90 | |
| # - name: Upload Emulator Log | |
| # if: always() | |
| # uses: actions/upload-artifact@v4 | |
| # with: | |
| # name: emulator-log | |
| # path: emulator.log | |
| # retention-days: 90 | |
| # # Update all caches | |
| # - name: Update Caches | |
| # if: always() | |
| # uses: ./.github/actions/cache-update | |
| # with: | |
| # arch: ${{ matrix.arch }} | |
| # run_emulator_setup: "true" | |
| # - name: Setup tmate session | |
| # if: ${{ failure() }} | |
| # uses: mxschmitt/action-tmate@v3 | |
| # with: | |
| # limit-access-to-actor: true | |
| # TODO: before instrument-test can take over from integration-test (and drop rebuilding the app for no reason) | |
| # 1. we have to figure out how mark tests failing as a failure but still get the coverage files exported ( just occured to me we can use same xml file as below!) | |
| # 2. we need to get the xml file that does the test results for the dorny thing | |
| integration-test: | |
| name: Test Android App | |
| needs: [set_build_datetime, build] | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| arch: [x86_64] # Only test on x86_64 to save time and resources | |
| fail-fast: false | |
| env: | |
| ENTRY_FILE: "index.tsx" | |
| GRADLE_ABI: ${{ matrix.arch }} | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 1 | |
| # Execute the common setup with emulator configuration | |
| - name: Common Setup | |
| id: common-setup | |
| uses: ./.github/actions/common-setup | |
| with: | |
| optional_cache_key: "integration-test" | |
| gradle_max_workers: "4" | |
| node_version: "22.x" | |
| arch: ${{ matrix.arch }} | |
| android_api_level: ${{ env.ANDROID_API_LEVEL }} | |
| android_target: ${{ env.ANDROID_TARGET }} | |
| android_profile: ${{ env.ANDROID_PROFILE }} | |
| run_emulator_setup: "true" | |
| - name: Make scripts executable | |
| run: | | |
| chmod +x scripts/wait_for_emulator.sh | |
| chmod +x scripts/run_android_tests.sh | |
| chmod +x scripts/generate_android_coverage.sh | |
| - name: Download APK artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-apk-artifacts-${{ matrix.arch }} | |
| path: android/app/build/outputs/apk | |
| - name: Download Coverage artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-coverage-artifacts-${{ matrix.arch }} | |
| path: android/${{ env.main_project_module }}/build/ | |
| - name: Debug directory structure | |
| run: | | |
| echo "Directory structure after download:" | |
| find android/${{ env.main_project_module }}/build/ -type d | |
| - name: Run Android Tests | |
| id: run_tests | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: ${{ env.ANDROID_API_LEVEL }} | |
| target: ${{ env.ANDROID_TARGET }} | |
| arch: ${{ matrix.arch }} | |
| profile: ${{ env.ANDROID_PROFILE }} | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -no-snapshot -memory 2048 | |
| disable-animations: true | |
| # TODO: make a PR so this thing can properly take a multiline script call | |
| # too easy to make mistakes with line this long | |
| # super annoying to have to make yet another script to forget to chmod +x on accident | |
| script: ./scripts/wait_for_emulator.sh && ./scripts/run_android_tests.sh ${{ matrix.arch }} ${{ env.main_project_module }} ${{ env.ANDROID_EMULATOR_WAIT_TIME_BEFORE_KILL }} && ./scripts/generate_android_coverage.sh ${{ matrix.arch }} ${{ env.main_project_module }} | |
| # Step 3: Generate JaCoCo coverage report | |
| - name: Generate Jacoco Coverage Report | |
| if: always() | |
| run: | | |
| cd android && \ | |
| ./gradlew jacocoAndroidTestReport --parallel --max-workers=4 --build-cache --info --stacktrace | |
| # Publish integration test results | |
| - name: Publish Integration Test Results | |
| uses: dorny/test-reporter@v2 | |
| if: always() | |
| with: | |
| name: 'Integration Tests' | |
| path: android/app/build/outputs/*.xml,android/app/build/outputs/**/*.xml | |
| reporter: java-junit | |
| fail-on-error: true | |
| - name: Upload Coverage Report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-test-coverage-report-${{ needs.set_build_datetime.outputs.build_datetime }} | |
| path: | | |
| android/app/build/reports/ | |
| android/app/build/reports/jacoco/jacocoAndroidTestReport/ | |
| android/app/build/outputs/code_coverage/ | |
| android/app/build/outputs/logs/ | |
| android/app/build/test-results/ | |
| retention-days: 90 | |
| - name: Upload Emulator Log | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: emulator-log | |
| path: emulator.log | |
| retention-days: 90 | |
| # Update all caches | |
| - name: Update Caches | |
| if: always() | |
| uses: ./.github/actions/cache-update | |
| with: | |
| arch: ${{ matrix.arch }} | |
| run_emulator_setup: "true" | |
| - name: Setup tmate session | |
| if: ${{ failure() }} | |
| uses: mxschmitt/action-tmate@v3 | |
| with: | |
| limit-access-to-actor: true | |
| coverage-report: | |
| name: Process Code Coverage | |
| needs: [set_build_datetime,unit-tests, build, integration-test] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 1 | |
| # Download coverage results from both unit and integration tests | |
| - name: Download unit test coverage | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: unit-test-results-${{ needs.set_build_datetime.outputs.build_datetime }} | |
| # path relative to build directory. because highest common directory is build | |
| path: coverage-data/unit-tests/android/app/build/ | |
| continue-on-error: true | |
| - name: Download integration test coverage | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-test-coverage-report-${{ needs.set_build_datetime.outputs.build_datetime }} | |
| # path relative to build directory. because highest common directory is build | |
| path: coverage-data/integration-tests/android/app/build/ | |
| continue-on-error: true | |
| # Process and generate coverage report from Jacoco XML files | |
| - name: Generate JaCoCo Coverage Report | |
| id: jacoco | |
| uses: madrapps/jacoco-report@v1.6.1 | |
| with: | |
| paths: | | |
| ${{ github.workspace }}/coverage-data/unit-tests/android/app/build/reports/coverage/**/*.xml | |
| ${{ github.workspace }}/coverage-data/integration-tests/android/app/build/reports/jacoco/**/*.xml | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| min-coverage-overall: 30 | |
| min-coverage-changed-files: 60 | |
| title: 'Code Coverage Report' | |
| update-comment: true | |
| # Upload detailed HTML coverage report | |
| - name: Upload HTML Coverage Report | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: code-coverage-report-html-${{ needs.set_build_datetime.outputs.build_datetime }} | |
| path: | | |
| coverage-data/unit-tests/android/app/build/reports/jacoco/**/html | |
| coverage-data/integration-tests/android/app/build/reports/jacoco/**/html | |
| retention-days: 90 | |
| # Create a PR comment with coverage report link if this is a PR | |
| - name: PR Comment with Coverage Report | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v6 | |
| with: | |
| github-token: ${{secrets.GITHUB_TOKEN}} | |
| script: | | |
| const workflowUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const coverageSummary = ` | |
| | Coverage Type | Coverage | | |
| | ---- | -------- | | |
| | Overall | ${{ steps.jacoco.outputs.coverage-overall }} | | |
| | Changed Files | ${{ steps.jacoco.outputs.coverage-changed-files }} | | |
| [View detailed coverage report](${workflowUrl}#artifacts) | |
| `; | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `## 📊 Code Coverage Summary\n${coverageSummary}` | |
| }); | |
| create-release: | |
| name: Create GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: sign | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: signed-* | |
| path: artifacts | |
| - name: Generate Changelog | |
| run: | | |
| echo "# Release ${GITHUB_REF#refs/tags/}" > ${{ github.workspace }}-CHANGELOG.txt | |
| echo "" >> ${{ github.workspace }}-CHANGELOG.txt | |
| echo "## What's Changed" >> ${{ github.workspace }}-CHANGELOG.txt | |
| git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:"* %s" >> ${{ github.workspace }}-CHANGELOG.txt | |
| - name: Organize release files | |
| run: | | |
| mkdir -p release_files | |
| find artifacts/ -name "*.apk" -o -name "*.aab" | xargs -I{} cp {} release_files/ | |
| - name: Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| body_path: ${{ github.workspace }}-CHANGELOG.txt | |
| files: release_files/* |