Build quick (each platform) #2100
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: Build quick (each platform) | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "0 0 * * *" | |
| pull_request: | |
| branches: | |
| - "**" | |
| push: | |
| branches: | |
| - main | |
| merge_group: | |
| env: | |
| DEBUG: 1 | |
| concurrency: | |
| # if a workflow is run against the same ref, only one at a time... | |
| # ...but allow different triggers to run concurrent (push, manual flake run, scheduled flake run...) | |
| group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # We may have a self-hosted runner available for macOS. Use it if so. | |
| determine-macos-runner: | |
| runs-on: ubuntu-latest | |
| concurrency: | |
| group: runner-determination | |
| cancel-in-progress: false | |
| outputs: | |
| runner: ${{ steps.determine-macos-runner.outputs.use-runner }} | |
| steps: | |
| - name: Wait for possible parallel workflow run job startup lag | |
| # After runner choice, the job that will use it has unavoidable job startup lag | |
| # Wait for that job start / runner state change before we choose the runner for this run | |
| run: sleep 30 | |
| - name: Use self-hosted runner if online and not busy, otherwise public runner | |
| id: determine-macos-runner | |
| uses: mikehardy/runner-fallback-action@v1 | |
| with: | |
| organization: "ankidroid" | |
| # 1- Choices are single labels, emitted array element 0 is used directly below | |
| # 2- self-hosted runner label is configured in Tartelet app on runner host | |
| # It is in format 'macos-*' to work with our label splitter below | |
| primary-runner: "macos-selfhosted" | |
| fallback-runner: "macos-26" | |
| primaries-required: 1 | |
| fallback-on-error: true | |
| # Actions secrets and Dependabot secrets are separate, and this is at org level:: | |
| # https://github.com/organizations/ankidroid/settings/secrets/dependabot | |
| # https://github.com/organizations/ankidroid/settings/secrets/actions | |
| github-token: ${{ secrets.MIKE_HARDY_ORG_ADMIN_KEY }} | |
| # We want to generate our matrix dynamically | |
| # Initial job generates the matrix as a JSON, and following job will use deserialize and use the result | |
| matrix_prep: | |
| needs: determine-macos-runner | |
| env: | |
| MACOS_RUNNER: ${{ needs.determine-macos-runner.outputs.runner }} | |
| # Do not run the scheduled jobs on forks | |
| if: (github.event_name == 'schedule' && github.repository == 'ankidroid/Anki-Android-Backend') || (github.event_name != 'schedule') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| matrix: ${{ steps.build-matrix.outputs.result }} | |
| steps: | |
| - id: build-matrix | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| // The determine-macos-runner step emits a JSON array of labels, but we need a single label | |
| // We configured it above to emit a single label, for either runner choice, parse it out | |
| const macosRunner = JSON.parse(process.env.MACOS_RUNNER)[0] | |
| // by default, we will include all 3 platforms we test on | |
| // "latest" would be easy everywhere, but "macos-15" isn't "latest" so we are specific there | |
| let osArray = ["ubuntu-latest", "windows-latest", macosRunner] | |
| let includeArray = []; | |
| for(const os of osArray) { | |
| // we do this work to define 'name' so we don't need to update branch protection rules | |
| // if the os changes, the name is used in the expanded workflow run name, which is then | |
| // used as a "required check" on branch protection merge rules, this keeps it stable | |
| // even if "macos-15" changes to "macos-latest" in the future so we just change the list here | |
| // but don't have to go into organization settings / protection rules etc etc | |
| includeArray.push({"os": os, "name": os.split('-')[0]}); | |
| } | |
| return { | |
| "os": osArray, | |
| "include": includeArray, | |
| } | |
| - name: Debug Output | |
| run: echo "${{ steps.build-matrix.outputs.result }}" | |
| # This uses the matrix generated from the matrix-prep stage | |
| build: | |
| needs: matrix_prep | |
| # Do not run the scheduled jobs on forks | |
| if: (github.event_name == 'schedule' && github.repository == 'ankidroid/Anki-Android-Backend') || (github.event_name != 'schedule') | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}} | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 80 | |
| # Stable name so branch protection required checks set is stable regardless of runner version (-latest, -15, etc) | |
| name: build (${{ matrix.name }}) | |
| steps: | |
| - name: Liberate disk space (Ubuntu) | |
| uses: jlumbroso/free-disk-space@main | |
| if: contains(matrix.os, 'ubuntu') | |
| with: | |
| tool-cache: false | |
| android: false | |
| dotnet: true | |
| haskell: true | |
| large-packages: false | |
| docker-images: true | |
| swap-storage: false | |
| - uses: actions/checkout@v6 | |
| - name: Install windows pre-requisites | |
| # Windows requires git and rsync to build correctly, and specifies them from msys2 | |
| # msys2 is already in the windows action runner images, but not in path so add it | |
| # use msys2 to install git and rsync per the README documentation in this repo | |
| # | |
| # note: zstd without absolute path is used in standard github cache restore though, | |
| # and msys2 zstd is 100x slower than default, so remove msys2 zstd | |
| if: contains(matrix.os, 'windows') | |
| run: | | |
| Add-Content $env:GITHUB_PATH "C:\msys64\usr\bin" | |
| c:\msys64\usr\bin\pacman.exe -S --noconfirm git rsync | |
| rm -force c:\msys64\usr\bin\zstd.exe | |
| - name: Install macos self-hosted pre-requisites | |
| # All platforms require cargo to build correctly, but our self-hosted tartelet | |
| # image doesn't have cargo (or rustup) installed by default. Easily fixed | |
| if: contains(runner.environment, 'self-hosted') && contains(runner.os, 'macOS') | |
| run: | | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain none | |
| echo "$HOME/.cargo/bin" >> $GITHUB_PATH | |
| - name: Ubuntu setup | |
| # We get KVM set up on Ubuntu as we run the emulator there (only platform with nested virt) | |
| # We also install some system software needed for the build on ubuntu | |
| if: contains(matrix.os, 'ubuntu') | |
| 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 | |
| sudo apt update | |
| sudo apt -y install liblzma-dev | |
| - name: Fetch submodules | |
| run: git submodule update --init | |
| - name: Restore Rust Cache (Windows) | |
| id: rust-cache-windows | |
| uses: actions/cache/restore@v5 | |
| if: contains(matrix.os, 'windows') | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| anki/out/rust | |
| anki/out/download | |
| anki/out/extracted | |
| # no node_modules, as it doesn't unpack properly | |
| key: ${{ runner.os }}-rust-debug-${{ hashFiles('Cargo.lock', 'anki/Cargo.lock', 'anki/yarn.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust-debug | |
| ${{ runner.os }}-rust | |
| - name: Restore Rust Cache (Unix) | |
| id: rust-cache-unix | |
| uses: actions/cache/restore@v5 | |
| if: contains(matrix.os, 'windows') == false && contains(runner.environment, 'self-hosted') == false | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| anki/out/rust | |
| anki/out/download | |
| anki/out/extracted | |
| anki/out/node_modules | |
| key: ${{ runner.os }}-rust-debug-${{ hashFiles('Cargo.lock', 'anki/Cargo.lock', 'anki/yarn.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-rust-debug | |
| ${{ runner.os }}-rust | |
| - name: Read configured NDK version | |
| run: | | |
| cargo install toml-cli | |
| ANDROID_NDK_VERSION=$(toml get gradle/libs.versions.toml versions.ndk --raw) | |
| ANDROID_NDK_MAJOR_VERSION=$(echo $ANDROID_NDK_VERSION | cut -f1 -d.) | |
| ANDROID_NDK_MINOR_VERSION=$(echo $ANDROID_NDK_VERSION | cut -f2 -d.) | |
| ANDROID_NDK_MARKETING_VERSION="r${ANDROID_NDK_MAJOR_VERSION}" | |
| if ! [ "$ANDROID_NDK_MINOR_VERSION" == "0" ]; then | |
| alphabet="abcdefghijklmnopqrstuvwxyz" | |
| ANDROID_NDK_MARKETING_MINOR_VERSION=$(echo ${alphabet:$ANDROID_NDK_MINOR_VERSION:1}) | |
| ANDROID_NDK_MARKETING_VERSION="${ANDROID_NDK_MARKETING_VERSION}${ANDROID_NDK_MARKETING_MINOR_VERSION}" | |
| fi | |
| echo "ANDROID_NDK_VERSION=$ANDROID_NDK_VERSION" >> $GITHUB_ENV | |
| echo "ANDROID_NDK_MARKETING_VERSION=$ANDROID_NDK_MARKETING_VERSION" >> $GITHUB_ENV | |
| shell: bash | |
| - name: Setup Android NDK | |
| uses: nttld/setup-ndk@v1 | |
| with: | |
| ndk-version: ${{ env.ANDROID_NDK_MARKETING_VERSION }} | |
| add-to-path: false | |
| link-to-sdk: true | |
| - name: Set NDK version (Unix) | |
| if: contains(matrix.os, 'ubuntu') || contains(matrix.os, 'macos') | |
| run: | | |
| export ANDROID_NDK_LATEST_HOME="${ANDROID_SDK_ROOT}/ndk/${ANDROID_NDK_VERSION}" | |
| echo "ANDROID_NDK_HOME=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV | |
| echo "ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME" >> $GITHUB_ENV | |
| - name: Set NDK version (Windows) | |
| if: contains(matrix.os, 'windows') | |
| run: | | |
| $env:ANDROID_NDK_LATEST_HOME = "$env:ANDROID_SDK_ROOT\ndk\$env:ANDROID_NDK_VERSION" | |
| Add-Content -Path $env:GITHUB_ENV -Value ANDROID_NDK_HOME=$env:ANDROID_NDK_LATEST_HOME | |
| Add-Content -Path $env:GITHUB_ENV -Value ANDROID_NDK_ROOT=$env:ANDROID_NDK_LATEST_HOME | |
| - name: Configure JDK | |
| uses: actions/setup-java@v5 | |
| with: | |
| distribution: "temurin" | |
| java-version: "21" # matches Anki-Android | |
| - name: Setup N2 | |
| run: bash ./anki/tools/install-n2 | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v5 | |
| timeout-minutes: 5 | |
| with: | |
| # Only write to the cache for builds on the 'main' branches, stops branches evicting main cache | |
| # Builds on other branches will only read from main branch cache writes | |
| # Comment this and the with: above out for performance testing on a branch | |
| cache-read-only: ${{ github.ref != 'refs/heads/main' }} | |
| - name: Build all (current platform) | |
| run: cargo run -p build_rust | |
| - name: Check Rust (Unix) | |
| if: contains(matrix.os, 'windows') == false | |
| run: ./check-rust.sh | |
| - name: Check Rust (Windows) | |
| if: contains(matrix.os, 'windows') | |
| run: ./check-rust.bat | |
| - name: Run tests (Unit) | |
| run: ./gradlew test rsdroid:lint --daemon | |
| - name: Run tests (Emulator) | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| if: contains(matrix.os, 'ubuntu') | |
| timeout-minutes: 30 | |
| with: | |
| api-level: 23 | |
| target: default | |
| arch: x86_64 | |
| profile: Nexus 6 | |
| script: | | |
| touch adb-log.txt | |
| $ANDROID_HOME/platform-tools/adb logcat '*:D' >> adb-log.txt & | |
| adb emu screenrecord start --time-limit 1800 video.webm | |
| sleep 5 | |
| ./gradlew rsdroid-instrumented:connectedCheck | |
| - name: Upload rsdroid AAR as artifact | |
| uses: actions/upload-artifact@v6 | |
| if: '!cancelled()' | |
| with: | |
| name: rsdroid-aar-${{ matrix.os }} | |
| if-no-files-found: error | |
| path: rsdroid/build/outputs/aar | |
| - name: Upload rsdroid-robo JAR as artifact | |
| uses: actions/upload-artifact@v6 | |
| if: '!cancelled()' | |
| with: | |
| name: rsdroid-robo-${{ matrix.os }} | |
| if-no-files-found: error | |
| path: rsdroid-testing/build/libs | |
| - name: Upload Emulator Log | |
| uses: actions/upload-artifact@v6 | |
| if: ('!cancelled()' && contains(matrix.os, 'ubuntu')) | |
| with: | |
| name: ${{ matrix.name }}-adb_logs | |
| path: adb-log.txt | |
| - name: Upload Emulator Screen Record | |
| uses: actions/upload-artifact@v6 | |
| if: ('!cancelled()' && contains(matrix.os, 'ubuntu')) | |
| with: | |
| name: ${{ matrix.name }}-adb_video | |
| path: video.webm | |
| - name: Save Rust Cache (Windows) | |
| uses: actions/cache/save@v5 | |
| if: contains(matrix.os, 'windows') && github.ref == 'refs/heads/main' | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| anki/out/rust | |
| anki/out/download | |
| anki/out/extracted | |
| # no node_modules, as it doesn't unpack properly | |
| key: ${{ steps.rust-cache-windows.outputs.cache-primary-key }} | |
| - name: Save Rust Cache (Unix) | |
| uses: actions/cache/save@v5 | |
| if: contains(matrix.os, 'windows') == false && github.ref == 'refs/heads/main' | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| anki/out/rust | |
| anki/out/download | |
| anki/out/extracted | |
| anki/out/node_modules | |
| key: ${{ steps.rust-cache-unix.outputs.cache-primary-key }} |