name: android-ci permissions: id-token: write # needed for AWS on: push: branches: - main - android-*.*.x tags: - "android-*" pull_request: branches: - "*" concurrency: # cancel jobs on PRs only group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: pre_job: runs-on: ubuntu-latest outputs: should_skip: ${{ github.event_name != 'workflow_dispatch' && steps.changed-files.outputs.android_any_modified != 'true' }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - name: Get all Android files that have changed if: github.event_name != 'workflow_dispatch' id: changed-files uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 with: files_yaml_from_source_file: .github/changed-files.yml android-build: runs-on: ubuntu-24.04 needs: - pre_job if: needs.pre_job.outputs.should_skip != 'true' strategy: fail-fast: false matrix: renderer: [opengl, vulkan, multiBackend] defaults: run: working-directory: platform/android env: BUILDTYPE: Debug IS_LOCAL_DEVELOPMENT: false MLN_ANDROID_STL: c++_static SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_DSN: ${{ secrets.SENTRY_DSN_ANDROID }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - uses: ./.github/actions/setup-android-ci - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: ".nvmrc" - name: npm install run: npm ci --ignore-scripts working-directory: . shell: bash - name: run platform/android/scripts/generate-style-code.mjs run: node platform/android/scripts/generate-style-code.mjs working-directory: . shell: bash - run: | python3 -m venv venv source venv/bin/activate pip3 install pre-commit shell: bash - run: | source venv/bin/activate pre-commit run clang-format --all-files continue-on-error: true # this can mean files are modified, which is not an error shell: bash - run: | source venv/bin/activate pre-commit run clang-format --all-files rm -rf venv shell: bash - if: env.SENTRY_DSN != '' run: | echo "SENTRY_ORG=maplibre" >> "$GITHUB_ENV" echo "SENTRY_PROJECT=maplibre-android" >> "$GITHUB_ENV" - uses: ./.github/actions/tree-is-clean with: check_untracked: true - name: Configure sccache uses: ./.github/actions/configure-sccache-s3 with: vars: ${{ toJSON(vars) }} - name: Check code style if: matrix.renderer == 'opengl' run: make android-check - name: Run Android unit tests run: RENDERER=${{ matrix.renderer }} make run-android-unit-test - name: Build libmaplibre.so for arm-v8 run: RENDERER=${{ matrix.renderer }} make android-lib-arm-v8 - name: Build API documentation if: matrix.renderer == 'opengl' run: ./gradlew dokkaGenerate - name: Build Examples documentation if: matrix.renderer == 'opengl' run: make mkdocs-build - name: Copy developer config with API key for UI tests if: github.ref == 'refs/heads/main' run: | MAPLIBRE_DEVELOPER_CONFIG_XML='${{ secrets.MAPLIBRE_DEVELOPER_CONFIG_XML }}' if [ -n "${MAPLIBRE_DEVELOPER_CONFIG_XML}" ]; then echo "${MAPLIBRE_DEVELOPER_CONFIG_XML}" > MapLibreAndroidTestApp/src/main/res/values/developer-config.xml else echo "No secrets.MAPLIBRE_DEVELOPER_CONFIG_XML variable set, not copying..." fi - name: Build Benchmark, copy to platform/android run: | ./gradlew assemble${{ matrix.renderer }}Release assemble${{ matrix.renderer }}ReleaseAndroidTest -PtestBuildType=release -Pmaplibre.abis=arm64-v8a cp MapLibreAndroidTestApp/build/outputs/apk/${{ matrix.renderer }}/release/MapLibreAndroidTestApp-${{ matrix.renderer }}-release.apk . cp MapLibreAndroidTestApp/build/outputs/apk/androidTest/${{ matrix.renderer }}/release/MapLibreAndroidTestApp-${{ matrix.renderer }}-release-androidTest.apk . # https://developer.android.com/guide/practices/page-sizes - name: Check alignment of .apk run: | unzip -o MapLibreAndroidTestApp/build/outputs/apk/${{ matrix.renderer }}/release/MapLibreAndroidTestApp-${{ matrix.renderer }}-release.apk -d /tmp/my_apk_out scripts/check-alignment.sh /tmp/my_apk_out - name: Create artifact for benchmark APKs if: matrix.renderer == 'opengl' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: if-no-files-found: error name: benchmarkAPKs path: | platform/android/MapLibreAndroidTestApp-${{ matrix.renderer }}-release.apk platform/android/MapLibreAndroidTestApp-${{ matrix.renderer }}-release-androidTest.apk - if: github.event_name == 'pull_request' && matrix.renderer == 'opengl' uses: ./.github/actions/save-pr-number - name: Set renderer env var (OpenGL or Vulkan) shell: bash run: | case "${{ matrix.renderer }}" in opengl) echo "renderer=OpenGL" >> "$GITHUB_ENV" ;; vulkan) echo "renderer=Vulkan" >> "$GITHUB_ENV" ;; multiBackend) echo "renderer=MultiBackend" >> "$GITHUB_ENV" ;; *) echo "::error ::Unknown renderer '${{ matrix.renderer }}'"; exit 1 ;; esac - name: Build Instrumentation Tests (${{ matrix.renderer }}), copy to platform/android run: | ./gradlew assemble${{ matrix.renderer }}Debug assemble${{ matrix.renderer }}DebugAndroidTest -PtestBuildType=debug -Pmaplibre.abis=arm64-v8a cp MapLibreAndroidTestApp/build/outputs/apk/${{ matrix.renderer }}/debug/MapLibreAndroidTestApp-${{ matrix.renderer }}-debug.apk InstrumentationTestApp${{ env.renderer }}.apk cp MapLibreAndroidTestApp/build/outputs/apk/androidTest/${{ matrix.renderer }}/debug/MapLibreAndroidTestApp-${{ matrix.renderer }}-debug-androidTest.apk InstrumentationTests${{ env.renderer }}.apk - name: Install sentry-cli if: env.SENTRY_DSN != '' run: curl -sL https://sentry.io/get-cli/ | sh - name: Upload debug symbols to sentry if: env.SENTRY_DSN != '' run: sentry-cli debug-files upload MapLibreAndroidTestApp - name: Upload android-ui-test-${{ matrix.renderer }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: if-no-files-found: error name: android-ui-test-${{ matrix.renderer }} path: | platform/android/InstrumentationTestApp${{ env.renderer }}.apk platform/android/InstrumentationTests${{ env.renderer }}.apk - name: Show sccache stats run: sccache --show-stats android-build-cpp-test: runs-on: ubuntu-24.04 needs: - pre_job if: needs.pre_job.outputs.should_skip != 'true' defaults: run: working-directory: test/android steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - uses: ./.github/actions/setup-android-ci - name: Create data.zip in assets directory run: zip -r test/android/app/src/main/assets/data.zip -@ < test/android/app/src/main/assets/to_zip.txt working-directory: . - name: Configure sccache uses: ./.github/actions/configure-sccache-s3 with: vars: ${{ toJSON(vars) }} - name: Build C++ Unit Tests App run: | ./gradlew assembleDebug assembleAndroidTest -Pmaplibre.abis=arm64-v8a cp app/build/outputs/apk/debug/app-debug.apk . cp app/build/outputs/apk/androidTest/release/app-release-androidTest.apk . - name: Store C++ Unit Tests .apk files uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: android-cpp-tests if-no-files-found: error path: | ./test/android/app-debug.apk ./test/android/app-release-androidTest.apk android-build-render-test: strategy: fail-fast: false matrix: flavor: [opengl, vulkan] runs-on: ubuntu-24.04 needs: - pre_job if: needs.pre_job.outputs.should_skip != 'true' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - uses: ./.github/actions/setup-android-ci - name: Configure sccache uses: ./.github/actions/configure-sccache-s3 with: vars: ${{ toJSON(vars) }} - name: Build and Upload Render Test APKs (${{ matrix.flavor }}) uses: ./.github/actions/android-build-and-upload-render-test with: flavor: ${{ matrix.flavor }} android-build-webgpu-dawn: runs-on: ubuntu-24.04 needs: - pre_job if: needs.pre_job.outputs.should_skip != 'true' defaults: run: working-directory: platform/android env: IS_LOCAL_DEVELOPMENT: false steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - uses: ./.github/actions/setup-android-ci - name: Configure sccache uses: ./.github/actions/configure-sccache-s3 with: vars: ${{ toJSON(vars) }} - name: Build WebGPU Dawn library for arm64-v8a run: ./gradlew :MapLibreAndroid:assembleWebgpuDawnDebug -Pmaplibre.abis=arm64-v8a - name: Show sccache stats run: sccache --show-stats android-build-webgpu-wgpu: runs-on: ubuntu-24.04 needs: - pre_job if: needs.pre_job.outputs.should_skip != 'true' defaults: run: working-directory: platform/android env: IS_LOCAL_DEVELOPMENT: false ANDROID_ARCH: arm64-v8a RUST_TARGET: aarch64-linux-android C_COMPILER: aarch64-linux-android26-clang BINDGEN_TARGET: aarch64-linux-android26 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: recursive persist-credentials: false - uses: ./.github/actions/setup-android-ci - name: Install Rust toolchain uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable with: toolchain: stable - name: Configure cargo for Android NDK linker run: | LATEST_NDK=$(find "$ANDROID_SDK_ROOT/ndk" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n 1) NDK_BIN="$LATEST_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin" mkdir -p ../../vendor/wgpu-native/.cargo cat > ../../vendor/wgpu-native/.cargo/config.toml << EOF [target.${{ env.RUST_TARGET }}] linker = "$NDK_BIN/${{ env.C_COMPILER }}" EOF - name: Pre-build wgpu-native for Android working-directory: vendor/wgpu-native run: | # vendor/wgpu-native pins its own rust toolchain; install targets for that active toolchain ACTIVE_TOOLCHAIN=$(rustup show active-toolchain | awk '{print $1}') rustup target add ${{ env.RUST_TARGET }} --toolchain "$ACTIVE_TOOLCHAIN" rustup target list --toolchain "$ACTIVE_TOOLCHAIN" --installed | grep -x '${{ env.RUST_TARGET }}' LATEST_NDK=$(find "$ANDROID_SDK_ROOT/ndk" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n 1) NDK_PREBUILT="$LATEST_NDK/toolchains/llvm/prebuilt/linux-x86_64" RESOURCE_DIR=$("$NDK_PREBUILT/bin/clang" --print-resource-dir) BINDGEN_EXTRA_CLANG_ARGS=$(echo "${{ env.RUST_TARGET }}" | tr '-' '_') export BINDGEN_EXTRA_CLANG_ARGS ENV_VAR="BINDGEN_EXTRA_CLANG_ARGS_$BINDGEN_EXTRA_CLANG_ARGS" export "${ENV_VAR}=--sysroot=$NDK_PREBUILT/sysroot --target=${{ env.BINDGEN_TARGET }} -isystem $RESOURCE_DIR/include" cargo build --release --target ${{ env.RUST_TARGET }} --no-default-features --features vulkan,wgsl,spirv,glsl - name: Configure sccache uses: ./.github/actions/configure-sccache-s3 with: vars: ${{ toJSON(vars) }} - name: Build WebGPU wgpu-native library for Android run: ./gradlew :MapLibreAndroid:assembleWebgpuWgpuDebug -Pmaplibre.abis=${{ env.ANDROID_ARCH }} - name: Show sccache stats run: sccache --show-stats android-ci-result: runs-on: ubuntu-latest permissions: actions: write contents: write if: needs.pre_job.outputs.should_skip != 'true' && always() needs: - pre_job - android-build - android-build-cpp-test - android-build-render-test - android-build-webgpu-dawn - android-build-webgpu-wgpu steps: - name: Set success=true if: needs.android-build.result == 'success' && needs.android-build-cpp-test.result == 'success' && needs.android-build-render-test.result == 'success' && needs.android-build-webgpu-dawn.result == 'success' && needs.android-build-webgpu-wgpu.result == 'success' run: echo success=true > "$GITHUB_ENV" - name: Mark result as failed if: env.success != 'true' run: exit 1 # automatically trigger android-release when code is pushed to main # and the platform/android/VERSION file has changed - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 if: github.ref == 'refs/heads/main' && github.event_name == 'push' - name: VERSION file changed if: github.ref == 'refs/heads/main' && github.event_name == 'push' id: version-file-android-changed uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 with: files: platform/android/VERSION - name: Should make release? if: env.success && github.ref == 'refs/heads/main' && github.event_name == 'push' && steps.version-file-android-changed.outputs.any_changed == 'true' run: echo make_release=true >> "$GITHUB_ENV" - name: Validate and set version if: env.make_release == 'true' working-directory: . run: .github/scripts/validate-version.sh platform/android/VERSION - name: Create tag if it does not exist if: env.make_release == 'true' run: .github/scripts/ensure-tag.sh android-v${{ env.version }} ${{ github.sha }} env: GITHUB_TOKEN: ${{ github.token }} - name: Trigger release if: env.make_release == 'true' run: gh workflow run android-release.yml --ref android-v${{ env.version }} env: GH_TOKEN: ${{ github.token }}