# Copyright 2019-2023 Tauri Programme within The Commons Conservancy # SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: MIT name: Test Rust on: push: branches: - v1 - v2 paths: &paths - '.github/workflows/test-rust.yml' - '.scripts/ci/affected-rust-packages.js' - 'plugins/**' - '!plugins/*/guest-js/**' - '!plugins/*/android/**' - '!plugins/*/*.md' - '!plugins/*/package.json' - '!plugins/*/api-iife.js' - 'Cargo.toml' - 'Cargo.lock' pull_request: branches: - v1 - v2 paths: *paths workflow_dispatch: env: CARGO_PROFILE_DEV_DEBUG: 0 # no debuginfo: faster builds and a much smaller target dir to cache CROSS_REV: 51f46f296253d8122c927c5bb933e3c4f27cc317 concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: # Lists the plugins the change affects (see .scripts/ci/affected-rust-packages.js) and splits # them in shards. Each shard is tested in a single job per platform, one package after the other # in the same target dir, so the dependencies the plugins share (tauri, wry, tao...) are built # (or restored) once per job instead of once per package, and a change that affects every plugin # needs a few jobs per platform instead of one per plugin. changes: runs-on: ubuntu-latest outputs: shards: ${{ steps.affected.outputs.shards }} steps: - uses: actions/checkout@v7 with: # a pull request checks out its merge commit, whose first parent is the base branch fetch-depth: 2 - id: affected run: node .scripts/ci/affected-rust-packages.js env: BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || github.event.before }} ALL: ${{ github.event_name == 'workflow_dispatch' }} GLOBAL_PATHS: .github/workflows/test-rust.yml test: name: test (${{ matrix.platform.target }}, ${{ matrix.shard.name }}) needs: changes if: ${{ needs.changes.outputs.shards != '[]' && needs.changes.outputs.shards != '' }} strategy: fail-fast: false matrix: shard: ${{ fromJSON(needs.changes.outputs.shards) }} platform: - { target: x86_64-pc-windows-msvc, os: windows-latest, runner: 'cargo', command: 'test' } - { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04, runner: 'cargo', command: 'test' } - { target: aarch64-apple-darwin, os: macos-latest, runner: 'cargo', command: 'test' } - { target: aarch64-apple-ios, os: macos-latest, runner: 'cargo', command: 'build' } - { target: aarch64-linux-android, os: ubuntu-latest, runner: 'cross', command: 'build' } runs-on: ${{ matrix.platform.os }} # a stuck step (e.g. `apt-get` waiting on an unreachable mirror) otherwise runs for 6 hours timeout-minutes: 90 steps: - uses: actions/checkout@v7 - name: install webkit2gtk if: contains(matrix.platform.target, 'unknown-linux') run: | sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev - uses: dtolnay/rust-toolchain@1.90 with: targets: ${{ matrix.platform.target }} - uses: Swatinem/rust-cache@v2 with: # One entry per platform, shared by every shard: the plugins share most of their # dependency tree (tauri, wry, tao...), and an entry per package or shard and platform # never fits the repository's 10 GB cache limit. key: ${{ matrix.platform.target }} # Only `v2`/`v3` write to the cache: an entry saved from a pull request lives on its # merge ref, where nothing else can restore it, and every write evicts an entry # that other runs would have hit (10 GB repository limit, LRU eviction). # A single shard saves the shared entry, so the jobs don't race for the key; it is the # one holding the plugins with the widest dependency trees. save-if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && matrix.shard.save }} # `cross` is built from a pinned git revision (its latest release predates the Android # targets we need). Keep the binary in its own small cache entry (accessed by every run, # so it stays hot) instead of rebuilding it from source on every run. - name: restore cross if: ${{ matrix.platform.runner == 'cross' }} id: cross-cache uses: actions/cache@v6 with: path: ${{ runner.tool_cache }}/cross key: cross-${{ runner.os }}-${{ env.CROSS_REV }} - name: install cross if: ${{ matrix.platform.runner == 'cross' && steps.cross-cache.outputs.cache-hit != 'true' }} run: cargo +stable install cross --git https://github.com/cross-rs/cross --rev ${{ env.CROSS_REV }} --locked --root ${{ runner.tool_cache }}/cross - name: add cross to PATH if: ${{ matrix.platform.runner == 'cross' }} run: echo "${{ runner.tool_cache }}/cross/bin" >> $GITHUB_PATH # The shard's packages run one after the other, and a failing package does not stop the # others (like `fail-fast: false` across jobs): the step fails at the end if any did. - name: ${{ matrix.platform.command }} ${{ matrix.shard.name }} shell: bash env: PACKAGES: ${{ matrix.shard.packages }} RUNNER: ${{ matrix.platform.runner }} COMMAND: ${{ matrix.platform.command }} TARGET: ${{ matrix.platform.target }} run: | failed=() for package in $PACKAGES; do # the features of `http` and `dialog` cannot all be enabled at once case "$package" in tauri-plugin-http|tauri-plugin-dialog) features=() ;; *) features=(--all-features) ;; esac echo "::group::$COMMAND $package" if ! $RUNNER $COMMAND --package "$package" --target "$TARGET" --all-targets "${features[@]}"; then failed+=("$package") # `--all-targets` above does not include doc tests, so they are run separately. # Only on the platforms we actually test on, since cargo skips doc tests when cross compiling. elif [ "$COMMAND" = test ] && ! $RUNNER test --doc --package "$package" --target "$TARGET" "${features[@]}"; then failed+=("$package (doc tests)") fi echo "::endgroup::" done if [ ${#failed[@]} -gt 0 ]; then for package in "${failed[@]}"; do echo "::error title=$COMMAND failed::$package" done exit 1 fi