User-configurable bottom navigation + NomadNet browsing polish #2979
Workflow file for this run
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
| name: CI | |
| on: | |
| push: | |
| branches: [ main, 'v[0-9].[0-9]+.x', 'release/**' ] | |
| pull_request: | |
| branches: [ main, 'v[0-9].[0-9]+.x', 'release/**' ] | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| validate-wrapper: | |
| name: Validate Gradle Wrapper | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 2 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| - name: Validate Gradle wrapper | |
| uses: gradle/actions/wrapper-validation@v6.3.0 | |
| lint: | |
| name: Code Quality (ktlint + detekt + CPD + Android Lint) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Set up Python 3.11 | |
| # Android `lint` covers :rns-backend-py + the pythonBackend variants, which | |
| # trigger Chaquopy's installPythonRequirements — and Chaquopy 17 only accepts | |
| # buildPython 3.11 (same reason the kotlin-tests + release jobs set this up). | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Run all lint checks (ktlint, detekt, CPD, Android Lint) | |
| # Android `lint` enforces only the checks in `checkOnly` (round 1: NewApi); | |
| # see the Android Lint block in the root build.gradle.kts. No baseline. | |
| # `lint` covers each module's DEFAULT variant; the two extra variant tasks | |
| # cover the flavor source the default misses (app/src/noSentry, | |
| # rns-host/src/pythonBackend) so NewApi is enforced across all variants. | |
| run: >- | |
| ./gradlew ktlintCheck detekt cpdCheck | |
| lint :app:lintNoSentryKotlinBackendDebug :rns-host:lintPythonBackendDebug | |
| --continue | |
| - name: Check for forbidden test suppressions | |
| run: | | |
| echo "Checking for forbidden @Suppress annotations in test files..." | |
| # Count files with NoRelaxedMocks suppression | |
| RELAXED_COUNT=$(grep -r "Suppress.*NoRelaxedMocks" app/src/test reticulum/src/test --include="*.kt" -l 2>/dev/null | wc -l || echo "0") | |
| # Count files with NoVerifyOnlyTests suppression | |
| VERIFY_COUNT=$(grep -r "Suppress.*NoVerifyOnlyTests" app/src/test reticulum/src/test --include="*.kt" -l 2>/dev/null | wc -l || echo "0") | |
| echo "Files with @Suppress(\"NoRelaxedMocks\"): $RELAXED_COUNT" | |
| echo "Files with @Suppress(\"NoVerifyOnlyTests\"): $VERIFY_COUNT" | |
| # These rules exist to prevent useless tests that don't test production code. | |
| # Suppressing them defeats the purpose. Fix the tests instead. | |
| # | |
| # BASELINE: Ratchet down over time. Don't increase these numbers. | |
| # Current state reflects Android framework mocks (Context, BluetoothManager, etc.) | |
| # that are impractical to stub explicitly. Fix when possible. | |
| RELAXED_BASELINE=37 | |
| VERIFY_BASELINE=3 | |
| FAIL=false | |
| if [ "$RELAXED_COUNT" -gt "$RELAXED_BASELINE" ]; then | |
| echo "::error::NoRelaxedMocks suppressions increased from $RELAXED_BASELINE to $RELAXED_COUNT" | |
| echo "Do not add @Suppress(\"NoRelaxedMocks\"). Use explicit stubs instead of relaxed mocks." | |
| FAIL=true | |
| fi | |
| if [ "$VERIFY_COUNT" -gt "$VERIFY_BASELINE" ]; then | |
| echo "::error::NoVerifyOnlyTests suppressions increased from $VERIFY_BASELINE to $VERIFY_COUNT" | |
| echo "Do not add @Suppress(\"NoVerifyOnlyTests\"). Add assertions to your tests." | |
| FAIL=true | |
| fi | |
| if [ "$FAIL" = true ]; then | |
| exit 1 | |
| fi | |
| echo "✓ No new forbidden suppressions found" | |
| - name: Upload lint reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: lint-reports | |
| path: | | |
| **/build/reports/ktlint/ | |
| **/build/reports/detekt/ | |
| **/build/reports/lint-results-*.* | |
| build/reports/cpd/ | |
| retention-days: 7 | |
| threading-audit: | |
| name: Threading Architecture Audit | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| - name: Make audit script executable | |
| run: chmod +x audit-dispatchers.sh | |
| - name: Run dispatcher audit | |
| id: audit | |
| run: | | |
| echo "Running threading architecture audit..." | |
| ./audit-dispatchers.sh | |
| exit_code=$? | |
| echo "exit_code=$exit_code" >> $GITHUB_OUTPUT | |
| exit $exit_code | |
| - name: Upload audit report | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: threading-audit-report | |
| path: dispatcher-audit-report.txt | |
| retention-days: 30 | |
| - name: Comment audit results on PR | |
| if: failure() && github.event_name == 'pull_request' | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| let report = 'Dispatcher audit report not found'; | |
| try { | |
| report = fs.readFileSync('dispatcher-audit-report.txt', 'utf8'); | |
| } catch (e) { | |
| console.log('Could not read audit report'); | |
| } | |
| github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: `## ❌ Threading Architecture Audit Failed\n\n<details><summary>View Audit Report</summary>\n\n\`\`\`\n${report}\n\`\`\`\n\n</details>\n\nPlease fix the dispatcher violations before merging.` | |
| }); | |
| kotlin-tests: | |
| name: Kotlin Tests (Shard ${{ matrix.shard }}/4) | |
| needs: [lint, validate-wrapper] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [0, 1, 2, 3] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v6 | |
| with: | |
| distribution: temurin | |
| java-version: 25 | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Clean AIDL generated code | |
| run: | | |
| # Clean AIDL generated code to avoid stale cache issues | |
| # This MUST happen before Gradle cache restore to prevent cached stale AIDL files | |
| rm -rf app/build/generated/aidl_source_output_dir | |
| rm -rf */build/generated/aidl_source_output_dir | |
| - name: Clean JaCoCo execution data | |
| run: | | |
| # Clean stale JaCoCo execution data to avoid "exceeds max age" errors in Codecov | |
| rm -rf build/jacoco | |
| rm -rf */build/jacoco | |
| rm -rf build/reports/jacoco | |
| rm -rf */build/reports/jacoco | |
| - name: Cache Robolectric Android SDKs | |
| uses: actions/cache@v6 | |
| with: | |
| path: ~/.m2/repository/org/robolectric | |
| key: robolectric-4.16-${{ runner.os }} | |
| restore-keys: | | |
| robolectric-4.16- | |
| robolectric- | |
| - name: Resolve dependencies (with retry) | |
| # Pre-resolve all dependencies before running tests. This isolates transient | |
| # Maven repository errors (e.g. 502 from dl.google.com) from actual test failures, | |
| # and avoids retrying the entire test suite for a network blip. | |
| run: | | |
| for attempt in 1 2 3; do | |
| echo "Dependency resolution attempt $attempt/3..." | |
| if ./gradlew :app:dependencies --configuration noSentryKotlinBackendDebugUnitTestRuntimeClasspath -q 2>/dev/null; then | |
| echo "Dependencies resolved successfully." | |
| break | |
| fi | |
| if [ "$attempt" -eq 3 ]; then | |
| echo "::error::Failed to resolve dependencies after 3 attempts" | |
| exit 1 | |
| fi | |
| echo "::warning::Dependency resolution failed (attempt $attempt), retrying in 15s..." | |
| sleep 15 | |
| done | |
| - name: Run app unit tests (shard ${{ matrix.shard }}) | |
| # Build cache enabled for faster incremental builds | |
| # AIDL and JaCoCo caching issues are handled by clean steps above | |
| # NOTE: For app module, only run noSentry variant (sentry/noSentry share the same test code, | |
| # differing only in SENTRY_DSN buildConfigField which tests don't exercise). | |
| run: ./gradlew :app:testNoSentryKotlinBackendDebugUnitTest -PtestShard=${{ matrix.shard }} -PtestShardTotal=4 --stacktrace --max-workers=4 | |
| - name: Upload unit test results | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: kotlin-test-results-shard-${{ matrix.shard }} | |
| path: | | |
| **/build/test-results/test*/*.xml | |
| **/build/test-results/test*/*.html | |
| retention-days: 7 | |
| - name: Upload unit test reports | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: kotlin-test-reports-shard-${{ matrix.shard }} | |
| path: | | |
| **/build/reports/tests/ | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./**/build/reports/jacoco/**/*.xml | |
| flags: unittests | |
| name: codecov-shard-${{ matrix.shard }} | |
| fail_ci_if_error: false | |
| verbose: true | |
| - name: Upload Kotlin coverage reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: kotlin-coverage-reports-shard-${{ matrix.shard }} | |
| path: | | |
| **/build/reports/jacoco/ | |
| retention-days: 7 | |
| module-tests: | |
| name: Module Tests (reticulum + data + micron) | |
| needs: [lint, validate-wrapper] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v6 | |
| with: | |
| distribution: temurin | |
| java-version: 25 | |
| - name: Set up Python 3.11 | |
| # Required by Chaquopy's `installDebugPythonRequirements` task on | |
| # :rns-backend-py — Chaquopy 17 only accepts buildPython 3.11. | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Clean AIDL generated code | |
| run: | | |
| # Clean AIDL generated code to avoid stale cache issues | |
| # This MUST happen before Gradle cache restore to prevent cached stale AIDL files | |
| rm -rf app/build/generated/aidl_source_output_dir | |
| rm -rf */build/generated/aidl_source_output_dir | |
| - name: Clean JaCoCo execution data | |
| run: | | |
| # Clean stale JaCoCo execution data to avoid "exceeds max age" errors in Codecov | |
| rm -rf build/jacoco | |
| rm -rf */build/jacoco | |
| rm -rf build/reports/jacoco | |
| rm -rf */build/reports/jacoco | |
| - name: Cache Robolectric Android SDKs | |
| uses: actions/cache@v6 | |
| with: | |
| path: ~/.m2/repository/org/robolectric | |
| key: robolectric-4.16-${{ runner.os }} | |
| restore-keys: | | |
| robolectric-4.16- | |
| robolectric- | |
| - name: Run RNS, stats, data, and micron module tests | |
| run: ./gradlew :rns-api:testDebugUnitTest :rns-ipc:testDebugUnitTest :rns-host:testKotlinBackendDebugUnitTest :rns-host:testPythonBackendDebugUnitTest :rns-backend-kt:testDebugUnitTest :rns-backend-py:testDebugUnitTest :rns-stats:testDebugUnitTest :data:testDebugUnitTest :micron:test --stacktrace --max-workers=4 | |
| - name: Upload unit test results | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: module-test-results | |
| path: | | |
| **/build/test-results/test*/*.xml | |
| **/build/test-results/test*/*.html | |
| retention-days: 7 | |
| - name: Upload unit test reports | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: module-test-reports | |
| path: | | |
| **/build/reports/tests/ | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v7 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: ./**/build/reports/jacoco/**/*.xml | |
| flags: unittests | |
| name: codecov-modules | |
| fail_ci_if_error: false | |
| verbose: true | |
| proguard-verification: | |
| name: Verify Python-Kotlin Bridge (R8) | |
| # Builds the minified pythonBackend RELEASE APK and asserts, against R8's own | |
| # mapping.txt, that the @Keep'd Python<->Kotlin bridge classes/methods survive | |
| # un-renamed. Chaquopy resolves them by name, so obfuscation would silently | |
| # break the python flavor at runtime. No emulator needed — checking the rename | |
| # table is the canonical, deterministic way to verify keep rules. | |
| needs: [lint, validate-wrapper] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v6 | |
| with: | |
| distribution: temurin | |
| java-version: 25 | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Install Python build dependencies | |
| run: pip install build | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Build minified pythonBackend release (generates R8 mapping) | |
| # noSentry + no SENTRY_AUTH_TOKEN keeps the Sentry mapping-upload path out; | |
| # unsigned is fine — minification (and mapping.txt) happens regardless. | |
| timeout-minutes: 40 | |
| run: ./gradlew :app:assembleNoSentryPythonBackendRelease --stacktrace --max-workers=2 | |
| - name: Assert bridge classes/methods survived R8 | |
| run: | | |
| python scripts/assert_bridges_kept.py \ | |
| app/build/outputs/mapping/noSentryPythonBackendRelease/mapping.txt | |
| - name: Upload R8 mapping file | |
| # Only on failure — the mapping (~130 MB for the python flavor) is only | |
| # useful for debugging a gate failure, and uploading it every run would | |
| # dominate the Actions storage quota. | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: r8-mapping-pythonBackend | |
| path: app/build/outputs/mapping/noSentryPythonBackendRelease/mapping.txt | |
| retention-days: 14 | |
| - name: Cleanup emulator | |
| if: always() | |
| run: | | |
| adb devices | grep emulator | cut -f1 | xargs -I {} adb -s {} emu kill || true | |
| pkill -9 qemu-system || true | |
| adb kill-server || true | |
| instrumented-tests: | |
| name: Instrumented Tests (Kotlin + Identity Recovery) | |
| # Run in parallel with other jobs | |
| needs: [lint, validate-wrapper] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v6 | |
| with: | |
| distribution: temurin | |
| java-version: 25 | |
| - name: Set up Python 3.11 | |
| # Required by Chaquopy when building the Python-backend recovery tests. | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Clean AIDL generated code | |
| run: | | |
| # Clean AIDL generated code to avoid stale cache issues | |
| rm -rf app/build/generated/aidl_source_output_dir | |
| rm -rf */build/generated/aidl_source_output_dir | |
| - 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: Pre-build test APKs | |
| run: | | |
| echo "Building APKs before starting emulator..." | |
| ./gradlew :app:assembleNoSentryKotlinBackendDebugAndroidTest --stacktrace --max-workers=2 | |
| ./gradlew :app:assembleNoSentryKotlinBackendDebug --stacktrace --max-workers=2 | |
| ./gradlew :app:assembleNoSentryPythonBackendDebugAndroidTest :app:assembleNoSentryPythonBackendDebug --stacktrace --max-workers=2 | |
| ./gradlew :rns-backend-py:assembleDebugAndroidTest :rns-backend-py:assembleDebug --stacktrace --max-workers=2 | |
| echo "APKs built successfully" | |
| - name: AVD cache | |
| uses: actions/cache@v6 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-34-x86_64-v6 | |
| - name: Create AVD and generate snapshot for caching | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@v2.38.0 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| target: google_apis | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -memory 2048 | |
| disable-animations: true | |
| emulator-boot-timeout: 600 | |
| script: | | |
| echo "Waiting for emulator to be ready..." | |
| adb wait-for-device | |
| adb shell getprop sys.boot_completed | grep -q 1 || sleep 10 | |
| echo "Emulator ready, generating snapshot for caching." | |
| - name: Run instrumented tests | |
| uses: reactivecircus/android-emulator-runner@v2.38.0 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| target: google_apis | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -memory 2048 | |
| disable-animations: true | |
| emulator-boot-timeout: 600 | |
| # The emulator action executes each literal-script line as a separate shell. | |
| # Fold this into one command so status is retained across all test groups. | |
| script: >- | |
| echo "Running emulator health check..."; | |
| adb shell getprop ro.build.version.sdk; | |
| adb shell pm list packages | head -5; | |
| echo "Emulator is responsive, starting tests..."; | |
| status=0; | |
| ./gradlew :app:connectedNoSentryKotlinBackendDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=network.columba.app.smoke.SmokeTest,network.columba.app.integration.MessageDataFlowTest,network.columba.app.integration.ConversationCreationFlowTest,network.columba.app.ui.components.MicronTrueColorRenderingTest --stacktrace || status=1; | |
| ./gradlew :app:connectedNoSentryPythonBackendDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=network.columba.app.integration.IdentityRecoveryIpcInstrumentedTest,network.columba.app.integration.IdentityRecoveryPersistenceInstrumentedTest --stacktrace || status=1; | |
| ./gradlew :rns-backend-py:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=network.columba.app.rns.backend.py.PythonRnsCoreIdentityRecoveryInstrumentedTest --stacktrace || status=1; | |
| exit "$status" | |
| - name: Upload instrumented test results | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: instrumented-test-results | |
| path: | | |
| **/build/outputs/androidTest-results/**/*.xml | |
| **/build/outputs/androidTest-results/**/*.html | |
| retention-days: 7 | |
| - name: Upload instrumented test reports | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: instrumented-test-reports | |
| path: | | |
| **/build/reports/androidTests/ | |
| retention-days: 7 | |
| - name: Capture logcat on failure | |
| if: failure() | |
| timeout-minutes: 1 | |
| continue-on-error: true | |
| run: | | |
| mkdir -p artifacts | |
| timeout 30 adb logcat -d > artifacts/logcat.log || true | |
| - name: Upload logcat output | |
| if: failure() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: logcat | |
| path: artifacts/logcat.log | |
| retention-days: 7 | |
| if-no-files-found: ignore | |
| - name: Cleanup emulator | |
| if: always() | |
| run: | | |
| adb devices | grep emulator | cut -f1 | xargs -I {} adb -s {} emu kill || true | |
| pkill -9 qemu-system || true | |
| adb kill-server || true | |
| transfer-progress-e2e: | |
| name: Real LXMF Resource Progress E2E | |
| needs: [lint, validate-wrapper] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Set up JDK 25 | |
| uses: actions/setup-java@v6 | |
| with: | |
| distribution: temurin | |
| java-version: 25 | |
| - name: Set up Python 3.11 | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.11' | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v6.3.0 | |
| with: | |
| cache-read-only: false | |
| - name: Install E2E host dependencies | |
| run: | | |
| python -m pip install --disable-pip-version-check -r tests/transfer_progress_e2e/requirements.txt | |
| python -m pytest -q tests/transfer_progress_e2e/test_ui_driver.py | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - name: Build Python-backend E2E APK | |
| run: ./gradlew :app:assembleNoSentryPythonBackendDebug --stacktrace --max-workers=2 | |
| - 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: Run real two-peer Resource progress E2E | |
| uses: reactivecircus/android-emulator-runner@v2.38.0 | |
| with: | |
| api-level: 34 | |
| arch: x86_64 | |
| target: google_apis | |
| force-avd-creation: true | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -memory 3072 | |
| disable-animations: true | |
| emulator-boot-timeout: 600 | |
| script: >- | |
| COLUMBA_EMULATOR_SERIAL=emulator-5554 | |
| COLUMBA_E2E_APK=app/build/outputs/apk/noSentryPythonBackend/debug/app-noSentry-pythonBackend-x86_64-debug.apk | |
| COLUMBA_E2E_ARTIFACT_DIR=artifacts/transfer-progress-e2e | |
| python -m pytest -v tests/transfer_progress_e2e/test_transfer_progress_e2e.py | |
| --junit-xml=artifacts/transfer-progress-e2e/results.xml; | |
| - name: Upload Resource progress E2E evidence | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: transfer-progress-e2e-evidence | |
| path: artifacts/transfer-progress-e2e/ | |
| retention-days: 14 | |
| if-no-files-found: error | |
| - name: Cleanup emulator | |
| if: always() | |
| run: | | |
| adb devices | grep emulator | cut -f1 | xargs -I {} adb -s {} emu kill || true | |
| pkill -9 qemu-system || true | |
| adb kill-server || true | |