mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-10-10 22:38:44 +02:00
ci: only run what a change affects, consolidate jobs (#3674)
* ci: only run what a change affects, consolidate jobs - detect affected plugins from changed files and Cargo.lock dependency trees - test/lint Rust per platform in shards instead of one job per plugin - fold check-generated-files into lint-javascript, fmt into a single job - skip fat LTO in integration test builds, single ABI in test-android * ci: run e2e on bumps, use published tauri CLI, PR-safe pnpm cache - trigger e2e on Cargo/pnpm lockfile and manifest changes - install the latest 2.x tauri-cli with cargo-binstall in integration tests - add setup-pnpm action: restore the store everywhere, save only on v2/v3 * fix clippy, ios build * fix bumps [skip ci]
This commit is contained in:
1 parent
974af951a5
commit
9a74a78c71
18 files changed
+600
-630
No files matched your search
@@ -0,0 +1,8 @@
|
||||
---
|
||||
biometric: patch
|
||||
nfc: patch
|
||||
biometric-js: patch
|
||||
nfc-js: patch
|
||||
---
|
||||
|
||||
Fix compilation on iOS when the `wry` feature of `tauri` is not enabled by another crate.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: setup pnpm
|
||||
description: Installs Node.js, pnpm and the workspace dependencies, restoring the pnpm store from the cache
|
||||
|
||||
# `pnpm/action-setup`'s own cache saves from every run, pull requests included. 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), so here every run
|
||||
# restores the store and only `v2`/`v3` save it.
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
|
||||
- id: store
|
||||
shell: bash
|
||||
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- id: cache
|
||||
uses: actions/cache/restore@v6
|
||||
with:
|
||||
path: ${{ steps.store.outputs.path }}
|
||||
key: pnpm-store-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
|
||||
restore-keys: pnpm-store-${{ runner.os }}-${{ runner.arch }}-
|
||||
|
||||
- shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# a store restored from an older lockfile still holds the packages it no longer uses
|
||||
- if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && steps.cache.outputs.cache-hit != 'true' }}
|
||||
shell: bash
|
||||
run: pnpm store prune
|
||||
|
||||
- if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && steps.cache.outputs.cache-hit != 'true' }}
|
||||
uses: actions/cache/save@v6
|
||||
with:
|
||||
path: ${{ steps.store.outputs.path }}
|
||||
key: ${{ steps.cache.outputs.cache-primary-key }}
|
||||
@@ -38,12 +38,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
- name: audit
|
||||
run: pnpm audit
|
||||
@@ -1,175 +0,0 @@
|
||||
# Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: check generated files
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/check-generated-files.yml'
|
||||
- pnpm-lock.yaml
|
||||
- '**/guest-js/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: ${{ steps.filter.outputs.changes }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
autostart:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/autostart/guest-js/**
|
||||
- plugins/autostart/src/api-iife.js
|
||||
cli:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/cli/guest-js/**
|
||||
- plugins/cli/src/api-iife.js
|
||||
clipboard-manager:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/clipboard-manager/guest-js/**
|
||||
- plugins/clipboard-manager/src/api-iife.js
|
||||
dialog:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/dialog/guest-js/**
|
||||
- plugins/dialog/src/api-iife.js
|
||||
fs:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/fs/guest-js/**
|
||||
- plugins/fs/src/api-iife.js
|
||||
geolocation:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/geolocation/guest-js/**
|
||||
- plugins/geolocation/src/api-iife.js
|
||||
global-shortcut:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/global-shortcut/guest-js/**
|
||||
- plugins/global-shortcut/src/api-iife.js
|
||||
opener:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/opener/guest-js/**
|
||||
- plugins/opener/src/api-iife.js
|
||||
haptics:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/haptics/guest-js/**
|
||||
- plugins/haptics/src/api-iife.js
|
||||
http:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/http/guest-js/**
|
||||
- plugins/http/src/api-iife.js
|
||||
log:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/log/guest-js/**
|
||||
- plugins/log/src/api-iife.js
|
||||
notification:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/notification/guest-js/**
|
||||
- plugins/notification/src/api-iife.js
|
||||
os:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/os/guest-js/**
|
||||
- plugins/os/src/api-iife.js
|
||||
positioner:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/positioner/guest-js/**
|
||||
- plugins/positioner/src/api-iife.js
|
||||
process:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/process/guest-js/**
|
||||
- plugins/process/src/api-iife.js
|
||||
shell:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/shell/guest-js/**
|
||||
- plugins/shell/src/api-iife.js
|
||||
sql:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/sql/guest-js/**
|
||||
- plugins/sql/src/api-iife.js
|
||||
store:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/store/guest-js/**
|
||||
- plugins/store/src/api-iife.js
|
||||
stronghold:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/stronghold/guest-js/**
|
||||
- plugins/stronghold/src/api-iife.js
|
||||
updater:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/updater/guest-js/**
|
||||
- plugins/updater/src/api-iife.js
|
||||
upload:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/upload/guest-js/**
|
||||
- plugins/upload/src/api-iife.js
|
||||
websocket:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/websocket/guest-js/**
|
||||
- plugins/websocket/src/api-iife.js
|
||||
window-state:
|
||||
- .github/workflows/check-generated-files.yml
|
||||
- pnpm-lock.yaml
|
||||
- plugins/window-state/guest-js/**
|
||||
- plugins/window-state/src/api-iife.js
|
||||
|
||||
test:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.packages != '[]' && needs.changes.outputs.packages != '' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
|
||||
- name: build api
|
||||
working-directory: plugins/${{ matrix.package }}
|
||||
run: pnpm install && pnpm build
|
||||
|
||||
- name: check diff
|
||||
run: |
|
||||
./.scripts/ci/has-diff.sh
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dorny/paths-filter@v2
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
list-files: shell
|
||||
|
||||
+41
-26
@@ -7,49 +7,64 @@ name: check formatting
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
rustfmt:
|
||||
# A single job whose checks only run when files they cover changed: the checks themselves take
|
||||
# seconds, so a runner each would mostly be spent waiting for and setting up runners.
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read # to list the changed files
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
rust:
|
||||
- .github/workflows/fmt.yml
|
||||
- '**/*.rs'
|
||||
prettier:
|
||||
- .github/workflows/fmt.yml
|
||||
- .prettierrc
|
||||
- .prettierignore
|
||||
- pnpm-lock.yaml
|
||||
- '**/*.{js,cjs,mjs,jsx,ts,cts,mts,tsx,json,json5,jsonc,md,mdx,yml,yaml,css,scss,less,html,vue,svelte}'
|
||||
toml:
|
||||
- .github/workflows/fmt.yml
|
||||
- '**/*.toml'
|
||||
|
||||
- name: install Rust stable and rustfmt
|
||||
if: steps.filter.outputs.rust == 'true'
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- name: run cargo fmt
|
||||
- name: rustfmt
|
||||
if: steps.filter.outputs.rust == 'true'
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
prettier:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- run: pnpm format:check
|
||||
|
||||
taplo:
|
||||
name: taplo (.toml files)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: install taplo-cli
|
||||
if: ${{ !cancelled() && steps.filter.outputs.toml == 'true' }}
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: taplo-cli
|
||||
|
||||
- run: taplo fmt --check --diff
|
||||
- name: taplo (.toml files)
|
||||
if: ${{ !cancelled() && steps.filter.outputs.toml == 'true' }}
|
||||
run: taplo fmt --check --diff
|
||||
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
if: ${{ !cancelled() && steps.filter.outputs.prettier == 'true' }}
|
||||
|
||||
- name: prettier
|
||||
if: ${{ !cancelled() && steps.filter.outputs.prettier == 'true' }}
|
||||
run: pnpm format:check
|
||||
@@ -8,22 +8,36 @@ on:
|
||||
workflow_dispatch:
|
||||
# run before publishing, see covector-version-or-publish.yml
|
||||
workflow_call:
|
||||
inputs:
|
||||
all:
|
||||
description: Run even if the updater is not affected by the pushed changes
|
||||
type: boolean
|
||||
default: true
|
||||
push:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/integration-tests.yml'
|
||||
- 'plugins/updater/src/**'
|
||||
- 'plugins/updater/tests/**'
|
||||
- '.scripts/ci/affected-rust-packages.js'
|
||||
- 'plugins/updater/**'
|
||||
- '!plugins/updater/guest-js/**'
|
||||
- '!plugins/updater/*.md'
|
||||
- '!plugins/updater/package.json'
|
||||
- '!plugins/updater/api-iife.js'
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
pull_request:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
- '.github/workflows/integration-tests.yml'
|
||||
- 'plugins/updater/src/**'
|
||||
- 'plugins/updater/tests/**'
|
||||
paths: *paths
|
||||
|
||||
concurrency:
|
||||
# `github.workflow` is the caller's name in a `workflow_call` run, so the suffix keeps this
|
||||
# workflow from sharing (and cancelling) the group of the other workflows the caller runs.
|
||||
group: ${{ github.workflow }}-integration-tests-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -32,8 +46,30 @@ env:
|
||||
CARGO_PROFILE_DEV_DEBUG: 0 # no debuginfo: faster builds and a much smaller target dir to cache
|
||||
|
||||
jobs:
|
||||
# A `Cargo.lock` change only runs the tests when it touches the updater's dependency tree; see
|
||||
# .scripts/ci/affected-rust-packages.js.
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
updater: ${{ contains(fromJSON(steps.affected.outputs.packages), 'tauri-plugin-updater') }}
|
||||
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: ${{ inputs.all == true || github.event_name == 'workflow_dispatch' }}
|
||||
GLOBAL_PATHS: .github/workflows/integration-tests.yml
|
||||
|
||||
run-integration-tests:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.updater == 'true'
|
||||
runs-on: ${{ matrix.platform }}
|
||||
# a stuck step (e.g. `apt-get` waiting on an unreachable mirror) otherwise runs for 6 hours
|
||||
timeout-minutes: 90
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -61,12 +97,27 @@ jobs:
|
||||
# that other runs would have hit (10 GB repository limit, LRU eviction).
|
||||
save-if: ${{ github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3' }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-binstall
|
||||
|
||||
# The latest published 2.x CLI, downloaded from its GitHub release. `--pkg-fmt` because the
|
||||
# crate's binstall metadata expects a .tgz on aarch64-apple-darwin, where the release only
|
||||
# ships a .zip, and the disabled strategies make a missing binary fail the step instead of
|
||||
# silently compiling the CLI from source. The token authenticates the GitHub requests and the
|
||||
# longer timeout and retries ride out a slow response, which binstall otherwise reports as a
|
||||
# missing binary.
|
||||
- name: install Tauri CLI
|
||||
# `--debug --target-dir target`: a dev-profile build in the workspace target dir reuses
|
||||
# the cached dependencies (shared with the test build below) instead of a from-scratch
|
||||
# release build in a temporary dir. `--locked` keeps the dependency versions stable
|
||||
# between runs, so the cached artifacts stay valid.
|
||||
run: cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch dev --locked --debug --target-dir target
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
for attempt in 1 2 3; do
|
||||
cargo binstall -y tauri-cli@^2 --pkg-fmt ${{ runner.os == 'Linux' && 'tgz' || 'zip' }} --disable-strategies quick-install,compile --maximum-resolution-timeout 60 && exit 0
|
||||
echo "::warning::cargo binstall failed (attempt $attempt)"
|
||||
sleep 10
|
||||
done
|
||||
exit 1
|
||||
|
||||
- name: grant admin rights without a prompt
|
||||
if: startsWith(matrix.platform, 'macos')
|
||||
@@ -80,4 +131,10 @@ jobs:
|
||||
echo "TAURI_UPDATER_ADMIN_PROMPT_TEST=1" >> "$GITHUB_ENV"
|
||||
|
||||
- name: run integration tests
|
||||
env:
|
||||
# The tests bundle the app with `cargo tauri build` over and over, changing nothing but
|
||||
# its version, and the workspace release profile (fat LTO, a single codegen unit) spends
|
||||
# about a minute linking each of those builds. None of it matters to the updater.
|
||||
CARGO_PROFILE_RELEASE_LTO: 'false'
|
||||
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: '16'
|
||||
run: cargo test --test '*' -- --ignored
|
||||
@@ -9,26 +9,27 @@ on:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/lint-javascript.yml'
|
||||
- '.github/actions/setup-pnpm/**'
|
||||
- 'plugins/*/guest-js/**'
|
||||
# generated by `pnpm build`, checked below
|
||||
- 'plugins/*/api-iife.js'
|
||||
- 'plugins/*/src/init-iife.js'
|
||||
- 'plugins/*/rollup.config.js'
|
||||
- 'plugins/*/tsconfig.json'
|
||||
- 'shared/**'
|
||||
- 'packages/**'
|
||||
- '.eslintignore'
|
||||
- '.eslintrc.json'
|
||||
- '.prettierignore'
|
||||
- 'eslint.config.js'
|
||||
- 'tsconfig.base.json'
|
||||
- 'pnpm-lock.yaml'
|
||||
- 'pnpm-workspace.yaml'
|
||||
- '**/package.json'
|
||||
pull_request:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
- '.github/workflows/lint-javascript.yml'
|
||||
- 'plugins/*/guest-js/**'
|
||||
- 'packages/**'
|
||||
- '.eslintignore'
|
||||
- '.eslintrc.json'
|
||||
- '.prettierignore'
|
||||
- '**/package.json'
|
||||
paths: *paths
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -42,18 +43,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
# the type-checked lint rules (and packages/api-e2e) resolve the plugins'
|
||||
# types from their dist-js build output
|
||||
- name: build
|
||||
id: build
|
||||
run: pnpm build
|
||||
- name: eslint
|
||||
run: pnpm lint
|
||||
- name: ts:check
|
||||
run: pnpm ts:check
|
||||
# the build above regenerates the `api-iife.js` and `init-iife.js` files the Rust crates
|
||||
# embed, which must be committed along with the guest-js changes they come from
|
||||
- name: check generated files are up to date
|
||||
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
|
||||
run: ./.scripts/ci/has-diff.sh
|
||||
+58
-126
@@ -9,20 +9,23 @@ on:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/lint-rust.yml'
|
||||
- 'plugins/*/src/**'
|
||||
- '!plugins/*/src/api-iife.js'
|
||||
- '**/Cargo.toml'
|
||||
- '.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:
|
||||
- '.github/workflows/lint-rust.yml'
|
||||
- 'plugins/*/src/**'
|
||||
- '!plugins/*/src/api-iife.js'
|
||||
- '**/Cargo.toml'
|
||||
paths: *paths
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@@ -32,128 +35,35 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Lists the plugins the change affects and splits them in shards, linted one after the other in
|
||||
# a single job per platform; see test-rust.yml for the reasoning.
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
packages: ${{ steps.filter.outputs.changes }}
|
||||
saver: ${{ steps.saver.outputs.saver }}
|
||||
shards: ${{ steps.affected.outputs.shards }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
tauri-plugin-autostart:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/autostart/**
|
||||
tauri-plugin-cli:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/cli/**
|
||||
tauri-plugin-clipboard-manager:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/clipboard-manager/**
|
||||
tauri-plugin-deep-link:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/deep-link/**
|
||||
tauri-plugin-dialog:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/dialog/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-fs:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/fs/**
|
||||
tauri-plugin-geolocation:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/geolocation/**
|
||||
tauri-plugin-global-shortcut:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/global-shortcut/**
|
||||
tauri-plugin-opener:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/opener/**
|
||||
tauri-plugin-haptics:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/haptics/**
|
||||
tauri-plugin-http:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/http/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-localhost:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/localhost/**
|
||||
tauri-plugin-log:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/log/**
|
||||
tauri-plugin-notification:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/notification/**
|
||||
tauri-plugin-os:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/os/**
|
||||
tauri-plugin-persisted-scope:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/persisted-scope/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-positioner:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/positioner/**
|
||||
tauri-plugin-process:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/process/**
|
||||
tauri-plugin-shell:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/shell/**
|
||||
tauri-plugin-single-instance:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/single-instance/**
|
||||
tauri-plugin-sql:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/sql/**
|
||||
tauri-plugin-store:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/store/**
|
||||
tauri-plugin-stronghold:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/stronghold/**
|
||||
tauri-plugin-updater:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/updater/**
|
||||
tauri-plugin-upload:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/upload/**
|
||||
tauri-plugin-websocket:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/websocket/**
|
||||
tauri-plugin-window-state:
|
||||
- .github/workflows/lint-rust.yml
|
||||
- plugins/window-state/**
|
||||
|
||||
# Every package job for a platform restores the same rust-cache entry, and exactly one
|
||||
# of them saves it (see the `save-if` of the rust-cache step). Prefer the packages whose
|
||||
# dependency tree covers most of the other plugins' dependencies, so the entry is as
|
||||
# useful as possible for the packages that did not save it.
|
||||
- name: pick the package that saves the cache
|
||||
id: saver
|
||||
# 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:
|
||||
PACKAGES: ${{ steps.filter.outputs.changes }}
|
||||
run: |
|
||||
saver="$(jq -rn --argjson packages "${PACKAGES:-[]}" '
|
||||
["tauri-plugin-upload", "tauri-plugin-sql", "tauri-plugin-updater", "tauri-plugin-single-instance"]
|
||||
| map(select(. as $p | $packages | index($p) != null))
|
||||
| first // $packages[0] // empty
|
||||
')"
|
||||
echo "saver=$saver" >> "$GITHUB_OUTPUT"
|
||||
BASE: ${{ github.event_name == 'pull_request' && 'HEAD^1' || github.event.before }}
|
||||
ALL: ${{ github.event_name == 'workflow_dispatch' }}
|
||||
GLOBAL_PATHS: .github/workflows/lint-rust.yml
|
||||
|
||||
clippy:
|
||||
name: clippy (${{ matrix.platform.target }}, ${{ matrix.shard.name }})
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.packages != '[]' && needs.changes.outputs.packages != '' }}
|
||||
if: ${{ needs.changes.outputs.shards != '[]' && needs.changes.outputs.shards != '' }}
|
||||
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
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
||||
shard: ${{ fromJSON(needs.changes.outputs.shards) }}
|
||||
platform:
|
||||
- target: x86_64-pc-windows-msvc
|
||||
os: windows-latest
|
||||
@@ -194,14 +104,36 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# One entry shared by every package job (the default key is per job, not per
|
||||
# matrix entry), only written from `v2`/`v3` and by a single package job; see
|
||||
# test-rust.yml for the reasoning.
|
||||
save-if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && matrix.package == needs.changes.outputs.saver }}
|
||||
# One entry per target shared by every shard (the default key is per job and runner
|
||||
# OS, which the Linux and Android jobs, and the macOS and iOS ones, would share), only
|
||||
# written from `v2`/`v3` and by a single shard; see test-rust.yml for the reasoning.
|
||||
key: ${{ matrix.platform.target }}
|
||||
save-if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && matrix.shard.save }}
|
||||
|
||||
- name: clippy ${{ matrix.package }}
|
||||
run: ${{ matrix.platform.cargo }} clippy --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets -- -D warnings
|
||||
# The shard's packages are linted one after the other, and a failing package does not stop
|
||||
# the others: the step fails at the end if any did.
|
||||
- name: clippy ${{ matrix.shard.name }}
|
||||
shell: bash
|
||||
env:
|
||||
PACKAGES: ${{ matrix.shard.packages }}
|
||||
CARGO: ${{ matrix.platform.cargo }}
|
||||
TARGET: ${{ matrix.platform.target }}
|
||||
run: |
|
||||
failed=()
|
||||
for package in $PACKAGES; do
|
||||
echo "::group::clippy $package"
|
||||
if ! $CARGO clippy --package "$package" --target "$TARGET" --all-targets -- -D warnings; then
|
||||
failed+=("$package")
|
||||
# the features of `dialog` cannot all be enabled at once
|
||||
elif [ "$package" != tauri-plugin-dialog ] && ! $CARGO clippy --package "$package" --target "$TARGET" --all-targets --all-features -- -D warnings; then
|
||||
failed+=("$package (--all-features)")
|
||||
fi
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
- name: clippy ${{ matrix.package }} --all-features
|
||||
if: matrix.package != 'tauri-plugin-dialog'
|
||||
run: ${{ matrix.platform.cargo }} clippy --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets --all-features -- -D warnings
|
||||
if [ ${#failed[@]} -gt 0 ]; then
|
||||
for package in "${failed[@]}"; do
|
||||
echo "::error title=clippy failed::$package"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
@@ -27,14 +27,7 @@ jobs:
|
||||
- name: Fetch git tags
|
||||
run: git fetch origin 'refs/tags/*:refs/tags/*'
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
|
||||
- name: Build packages
|
||||
run: pnpm build
|
||||
|
||||
@@ -27,21 +27,16 @@ jobs:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
# No caches on purpose: this workflow only runs on pull requests, so anything it saved
|
||||
# would live on the merge ref where no other run can restore it, while still counting
|
||||
# against the repository's 10 GB cache limit and evicting entries that `v2`/`v3` saved.
|
||||
# Nothing is saved to the cache on purpose: this workflow only runs on pull requests, so
|
||||
# anything it saved would live on the merge ref where no other run can restore it, while
|
||||
# still counting against the repository's 10 GB cache limit and evicting entries that
|
||||
# `v2`/`v3` saved. Restoring is fine, as setup-pnpm does with the pnpm store.
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: dtolnay/rust-toolchain@1.90
|
||||
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
run_install: true
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
@@ -71,8 +66,10 @@ jobs:
|
||||
- name: build API
|
||||
run: pnpm build
|
||||
|
||||
# A release build, so R8 runs with the plugins' ProGuard rules, of a single ABI: the
|
||||
# other three would only repeat the (uncached) release build of the Rust code.
|
||||
- name: build APK
|
||||
working-directory: ./examples/api
|
||||
run: pnpm tauri android build
|
||||
run: pnpm tauri android build --target aarch64
|
||||
env:
|
||||
NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }}
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/test-api-e2e-mobile.yml'
|
||||
- 'packages/api-e2e/**'
|
||||
- 'examples/api/**'
|
||||
@@ -21,19 +21,16 @@ on:
|
||||
- 'plugins/*/ios/**'
|
||||
- 'plugins/*/permissions/**'
|
||||
- 'plugins/*/build.rs'
|
||||
# dependency bumps
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'plugins/*/Cargo.toml'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '**/package.json'
|
||||
pull_request:
|
||||
branches:
|
||||
- v2
|
||||
paths:
|
||||
- '.github/workflows/test-api-e2e-mobile.yml'
|
||||
- 'packages/api-e2e/**'
|
||||
- 'examples/api/**'
|
||||
- 'plugins/*/guest-js/**'
|
||||
- 'plugins/*/src/**'
|
||||
- 'plugins/*/android/**'
|
||||
- 'plugins/*/ios/**'
|
||||
- 'plugins/*/permissions/**'
|
||||
- 'plugins/*/build.rs'
|
||||
paths: *paths
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
@@ -63,13 +60,7 @@ jobs:
|
||||
with:
|
||||
targets: x86_64-linux-android
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
@@ -122,9 +113,6 @@ jobs:
|
||||
# that other runs would have hit (10 GB repository limit, LRU eviction).
|
||||
save-if: ${{ github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3' }}
|
||||
|
||||
- name: install dependencies
|
||||
run: pnpm i --frozen-lockfile
|
||||
|
||||
- name: build plugins
|
||||
run: pnpm build
|
||||
|
||||
@@ -186,13 +174,7 @@ jobs:
|
||||
with:
|
||||
targets: aarch64-apple-ios-sim
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
@@ -202,9 +184,6 @@ jobs:
|
||||
# that other runs would have hit (10 GB repository limit, LRU eviction).
|
||||
save-if: ${{ github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3' }}
|
||||
|
||||
- name: install dependencies
|
||||
run: pnpm i --frozen-lockfile
|
||||
|
||||
- name: build plugins
|
||||
run: pnpm build
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/test-api-e2e.yml'
|
||||
- 'packages/api-e2e/**'
|
||||
- 'examples/api/**'
|
||||
@@ -19,17 +19,16 @@ on:
|
||||
- 'plugins/*/src/**'
|
||||
- 'plugins/*/permissions/**'
|
||||
- 'plugins/*/build.rs'
|
||||
# dependency bumps
|
||||
- 'Cargo.toml'
|
||||
- 'Cargo.lock'
|
||||
- 'plugins/*/Cargo.toml'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '**/package.json'
|
||||
pull_request:
|
||||
branches:
|
||||
- v2
|
||||
paths:
|
||||
- '.github/workflows/test-api-e2e.yml'
|
||||
- 'packages/api-e2e/**'
|
||||
- 'examples/api/**'
|
||||
- 'plugins/*/guest-js/**'
|
||||
- 'plugins/*/src/**'
|
||||
- 'plugins/*/permissions/**'
|
||||
- 'plugins/*/build.rs'
|
||||
paths: *paths
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
@@ -63,13 +62,7 @@ jobs:
|
||||
- name: install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 'lts/*'
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
cache: true # the pnpm store, keyed by OS and lockfile hash and shared by every workflow
|
||||
- uses: ./.github/actions/setup-pnpm
|
||||
|
||||
- name: install Linux dependencies
|
||||
if: runner.os == 'Linux'
|
||||
@@ -117,9 +110,6 @@ jobs:
|
||||
# that other runs would have hit (10 GB repository limit, LRU eviction).
|
||||
save-if: ${{ github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3' }}
|
||||
|
||||
- name: install dependencies
|
||||
run: pnpm i --frozen-lockfile
|
||||
|
||||
- name: build plugins
|
||||
run: pnpm build
|
||||
|
||||
|
||||
+69
-194
@@ -9,22 +9,23 @@ on:
|
||||
branches:
|
||||
- v1
|
||||
- v2
|
||||
paths:
|
||||
paths: &paths
|
||||
- '.github/workflows/test-rust.yml'
|
||||
- 'plugins/*/src/**'
|
||||
- '!plugins/*/src/api-iife.js'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
- '.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:
|
||||
- '.github/workflows/test-rust.yml'
|
||||
- 'plugins/*/src/**'
|
||||
- '!plugins/*/src/api-iife.js'
|
||||
- '**/Cargo.toml'
|
||||
- '**/Cargo.lock'
|
||||
paths: *paths
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
CARGO_PROFILE_DEV_DEBUG: 0 # no debuginfo: faster builds and a much smaller target dir to cache
|
||||
@@ -38,182 +39,35 @@ 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
|
||||
permissions:
|
||||
pull-requests: read
|
||||
outputs:
|
||||
packages: ${{ steps.filter.outputs.changes }}
|
||||
saver: ${{ steps.saver.outputs.saver }}
|
||||
shards: ${{ steps.affected.outputs.shards }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dorny/paths-filter@v2
|
||||
id: filter
|
||||
with:
|
||||
base: v2
|
||||
filters: |
|
||||
tauri-plugin-autostart:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/autostart/**
|
||||
tauri-plugin-cli:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/cli/**
|
||||
tauri-plugin-clipboard-manager:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/clipboard-manager/**
|
||||
tauri-plugin-deep-link:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/deep-link/**
|
||||
tauri-plugin-dialog:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/dialog/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-fs:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/fs/**
|
||||
tauri-plugin-geolocation:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/geolocation/**
|
||||
tauri-plugin-global-shortcut:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/global-shortcut/**
|
||||
tauri-plugin-opener:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/opener/**
|
||||
tauri-plugin-haptics:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/haptics/**
|
||||
tauri-plugin-http:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/http/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-localhost:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/localhost/**
|
||||
tauri-plugin-log:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/log/**
|
||||
tauri-plugin-notification:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/notification/**
|
||||
tauri-plugin-os:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/os/**
|
||||
tauri-plugin-persisted-scope:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/persisted-scope/**
|
||||
- plugins/fs/**
|
||||
tauri-plugin-positioner:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/positioner/**
|
||||
tauri-plugin-process:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/process/**
|
||||
tauri-plugin-shell:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/shell/**
|
||||
tauri-plugin-single-instance:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/single-instance/**
|
||||
tauri-plugin-sql:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/sql/**
|
||||
tauri-plugin-store:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/store/**
|
||||
tauri-plugin-stronghold:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/stronghold/**
|
||||
tauri-plugin-updater:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/updater/**
|
||||
tauri-plugin-upload:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/upload/**
|
||||
tauri-plugin-websocket:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/websocket/**
|
||||
tauri-plugin-window-state:
|
||||
- .github/workflows/test-rust.yml
|
||||
- Cargo.toml
|
||||
- Cargo.lock
|
||||
- plugins/window-state/**
|
||||
|
||||
# Every package job for a platform restores the same rust-cache entry, and exactly one
|
||||
# of them saves it (see the `save-if` of the rust-cache step). Prefer the packages whose
|
||||
# dependency tree covers most of the other plugins' dependencies, so the entry is as
|
||||
# useful as possible for the packages that did not save it.
|
||||
- name: pick the package that saves the cache
|
||||
id: saver
|
||||
# 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:
|
||||
PACKAGES: ${{ steps.filter.outputs.changes }}
|
||||
run: |
|
||||
saver="$(jq -rn --argjson packages "${PACKAGES:-[]}" '
|
||||
["tauri-plugin-upload", "tauri-plugin-sql", "tauri-plugin-updater", "tauri-plugin-single-instance"]
|
||||
| map(select(. as $p | $packages | index($p) != null))
|
||||
| first // $packages[0] // empty
|
||||
')"
|
||||
echo "saver=$saver" >> "$GITHUB_OUTPUT"
|
||||
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.packages != '[]' && needs.changes.outputs.packages != '' }}
|
||||
if: ${{ needs.changes.outputs.shards != '[]' && needs.changes.outputs.shards != '' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package: ${{ fromJSON(needs.changes.outputs.packages) }}
|
||||
shard: ${{ fromJSON(needs.changes.outputs.shards) }}
|
||||
platform:
|
||||
- {
|
||||
target: x86_64-pc-windows-msvc,
|
||||
@@ -247,6 +101,8 @@ jobs:
|
||||
}
|
||||
|
||||
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
|
||||
@@ -263,15 +119,16 @@ jobs:
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# One entry per platform, shared by every package job: the plugins share most of
|
||||
# their dependency tree (tauri, wry, tao...), and an entry per package and platform
|
||||
# (26 × 5 entries of 500-800 MB each) never fits the repository's 10 GB cache limit.
|
||||
# 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 package job saves the shared entry, so the jobs don't race for the key.
|
||||
save-if: ${{ (github.ref == 'refs/heads/v2' || github.ref == 'refs/heads/v3') && matrix.package == needs.changes.outputs.saver }}
|
||||
# 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,
|
||||
@@ -292,20 +149,38 @@ jobs:
|
||||
if: ${{ matrix.platform.runner == 'cross' }}
|
||||
run: echo "${{ runner.tool_cache }}/cross/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: test ${{ matrix.package }}
|
||||
if: ${{ matrix.package != 'tauri-plugin-http' && matrix.package != 'tauri-plugin-dialog' }}
|
||||
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets --all-features
|
||||
# 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
|
||||
|
||||
- name: test ${{ matrix.package }}
|
||||
if: ${{ matrix.package == 'tauri-plugin-http' || matrix.package == 'tauri-plugin-dialog' }}
|
||||
run: ${{ matrix.platform.runner }} ${{ matrix.platform.command }} --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-targets
|
||||
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
|
||||
|
||||
# `--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.
|
||||
- name: doc test ${{ matrix.package }}
|
||||
if: ${{ matrix.platform.command == 'test' && matrix.package != 'tauri-plugin-http' && matrix.package != 'tauri-plugin-dialog' }}
|
||||
run: ${{ matrix.platform.runner }} test --doc --package ${{ matrix.package }} --target ${{ matrix.platform.target }} --all-features
|
||||
|
||||
- name: doc test ${{ matrix.package }}
|
||||
if: ${{ matrix.platform.command == 'test' && (matrix.package == 'tauri-plugin-http' || matrix.package == 'tauri-plugin-dialog') }}
|
||||
run: ${{ matrix.platform.runner }} test --doc --package ${{ matrix.package }} --target ${{ matrix.platform.target }}
|
||||
if [ ${#failed[@]} -gt 0 ]; then
|
||||
for package in "${failed[@]}"; do
|
||||
echo "::error title=$COMMAND failed::$package"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,254 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Lists the plugin crates a change affects, so the Rust workflows only build and test those.
|
||||
//
|
||||
// A plugin is affected when:
|
||||
// - a file in its directory changed (except the JS / docs / Android files `cargo` never reads),
|
||||
// - a plugin it depends on is affected (e.g. `fs` for `dialog`), or
|
||||
// - a `Cargo.lock` entry in its dependency tree changed (so a dependency bump only tests the
|
||||
// plugins that actually use that dependency).
|
||||
// Every plugin is affected when a workspace-wide file (root `Cargo.toml`, toolchain, cargo
|
||||
// config, this script, the files in `GLOBAL_PATHS`) changed, or when there is nothing to diff
|
||||
// against.
|
||||
//
|
||||
// Environment:
|
||||
// - BASE: the revision to diff `HEAD` against; empty or all zeros means every plugin.
|
||||
// - ALL: `true` to select every plugin.
|
||||
// - GLOBAL_PATHS: newline separated paths (usually the calling workflow) that affect every plugin.
|
||||
// - SHARD_SIZE: the maximum number of plugins per shard (default 8).
|
||||
//
|
||||
// Writes `packages` (JSON array) and `shards` (JSON array of `{ name, packages, save }`, where
|
||||
// `packages` is space separated and `save` is true for the shard that should save the cache)
|
||||
// to `$GITHUB_OUTPUT`.
|
||||
|
||||
import { execFileSync } from 'child_process'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
// Crates whose dependency trees cover most of the other plugins' dependencies. They go first so
|
||||
// the shard that saves the cache (the first one) restores as much as possible for the others.
|
||||
const preferredFirst = [
|
||||
'tauri-plugin-upload',
|
||||
'tauri-plugin-sql',
|
||||
'tauri-plugin-updater',
|
||||
'tauri-plugin-single-instance'
|
||||
]
|
||||
|
||||
const globalPaths = [
|
||||
'Cargo.toml',
|
||||
'rust-toolchain',
|
||||
'rust-toolchain.toml',
|
||||
'.cargo/',
|
||||
'.scripts/ci/affected-rust-packages.js',
|
||||
...(process.env.GLOBAL_PATHS ?? '')
|
||||
.split('\n')
|
||||
.map((p) => p.trim())
|
||||
.filter(Boolean)
|
||||
]
|
||||
|
||||
// Paths inside a plugin directory that never reach `cargo`.
|
||||
const ignoredInPlugin = [
|
||||
/^guest-js\//,
|
||||
/^dist-js\//,
|
||||
/^node_modules\//,
|
||||
/^android\//,
|
||||
/(^|\/)[^/]+\.md$/,
|
||||
/^package\.json$/,
|
||||
/^tsconfig\.json$/,
|
||||
/^rollup\.config\.js$/,
|
||||
/^api-iife\.js$/,
|
||||
/^banner\.png$/,
|
||||
/^LICENSE/,
|
||||
/^contributors\//
|
||||
]
|
||||
|
||||
function git(...args) {
|
||||
return execFileSync('git', args, {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 64 * 1024 * 1024,
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
})
|
||||
}
|
||||
|
||||
function readPlugins() {
|
||||
const plugins = new Map() // directory -> crate name
|
||||
for (const dir of fs.readdirSync('plugins')) {
|
||||
const manifest = path.join('plugins', dir, 'Cargo.toml')
|
||||
if (!fs.existsSync(manifest)) continue
|
||||
const name = fs
|
||||
.readFileSync(manifest, 'utf8')
|
||||
.match(/^\[package\][^[]*?^name\s*=\s*"([^"]+)"/m)?.[1]
|
||||
if (name) plugins.set(dir, name)
|
||||
}
|
||||
return plugins
|
||||
}
|
||||
|
||||
// Parses the `[[package]]` entries of a Cargo.lock into a map of
|
||||
// `name version (source)` -> `{ name, version, source, checksum, dependencies, fingerprint }`.
|
||||
function parseLock(text) {
|
||||
const packages = new Map()
|
||||
for (const block of text.split(/^\[\[package\]\]$/m).slice(1)) {
|
||||
const field = (key) =>
|
||||
block.match(new RegExp(`^${key} = "([^"]*)"`, 'm'))?.[1]
|
||||
const name = field('name')
|
||||
const version = field('version')
|
||||
const source = field('source')
|
||||
const dependencies = [
|
||||
...(block.match(/^dependencies = \[([^\]]*)\]/m)?.[1] ?? '').matchAll(
|
||||
/"([^"]+)"/g
|
||||
)
|
||||
].map((m) => m[1])
|
||||
const id = source ? `${name} ${version} (${source})` : `${name} ${version}`
|
||||
const checksum = field('checksum')
|
||||
packages.set(id, { name, version, source, checksum, dependencies })
|
||||
}
|
||||
|
||||
const byName = new Map()
|
||||
for (const [id, pkg] of packages) {
|
||||
byName.set(pkg.name, [...(byName.get(pkg.name) ?? []), id])
|
||||
}
|
||||
// dependency entries are `name`, `name version` or `name version (source)`
|
||||
const resolve = (dep) => {
|
||||
if (packages.has(dep)) return dep
|
||||
const [name, version] = dep.split(' ')
|
||||
const candidates = byName.get(name) ?? []
|
||||
return version
|
||||
? candidates.find((id) => packages.get(id).version === version)
|
||||
: candidates[0]
|
||||
}
|
||||
for (const pkg of packages.values()) {
|
||||
pkg.dependencies = pkg.dependencies.map(resolve).filter(Boolean).sort()
|
||||
// what identifies the resolved entry: dependencies are compared by what they resolve to, as
|
||||
// cargo spells a dependency differently (`name` or `name version`) depending on how many
|
||||
// versions of it the lockfile holds
|
||||
pkg.fingerprint = JSON.stringify([pkg.checksum, pkg.dependencies])
|
||||
}
|
||||
return packages
|
||||
}
|
||||
|
||||
function affectedPackages(plugins) {
|
||||
const all = [...plugins.values()]
|
||||
const base = process.env.BASE?.trim()
|
||||
|
||||
if (process.env.ALL === 'true') {
|
||||
console.log('every plugin selected')
|
||||
return all
|
||||
}
|
||||
if (!base || /^0+$/.test(base)) {
|
||||
console.log('nothing to diff against, selecting every plugin')
|
||||
return all
|
||||
}
|
||||
|
||||
let changedFiles
|
||||
try {
|
||||
try {
|
||||
git('cat-file', '-e', `${base}^{commit}`)
|
||||
} catch {
|
||||
git('fetch', '--no-tags', '--depth=1', 'origin', base)
|
||||
}
|
||||
changedFiles = git('diff', '--name-only', base, 'HEAD')
|
||||
.split('\n')
|
||||
.filter(Boolean)
|
||||
} catch (error) {
|
||||
console.log(
|
||||
`could not diff against ${base}, selecting every plugin: ${error.stderr ?? error}`
|
||||
)
|
||||
return all
|
||||
}
|
||||
console.log(`changed files:\n ${changedFiles.join('\n ')}`)
|
||||
|
||||
const global = changedFiles.find((file) =>
|
||||
globalPaths.some((p) => (p.endsWith('/') ? file.startsWith(p) : file === p))
|
||||
)
|
||||
if (global) {
|
||||
console.log(`${global} affects every plugin`)
|
||||
return all
|
||||
}
|
||||
|
||||
const lock = parseLock(fs.readFileSync('Cargo.lock', 'utf8'))
|
||||
const seeds = new Set()
|
||||
|
||||
for (const file of changedFiles) {
|
||||
const [, dir, rest] = file.match(/^plugins\/([^/]+)\/(.+)$/) ?? []
|
||||
const name = plugins.get(dir)
|
||||
if (!name || ignoredInPlugin.some((pattern) => pattern.test(rest))) continue
|
||||
for (const [id, pkg] of lock) {
|
||||
if (pkg.name === name && !pkg.source) seeds.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
if (changedFiles.includes('Cargo.lock')) {
|
||||
let baseLock
|
||||
try {
|
||||
baseLock = parseLock(git('show', `${base}:Cargo.lock`))
|
||||
} catch {
|
||||
console.log('no Cargo.lock to compare with, selecting every plugin')
|
||||
return all
|
||||
}
|
||||
for (const [id, pkg] of lock) {
|
||||
if (baseLock.get(id)?.fingerprint !== pkg.fingerprint) seeds.add(id)
|
||||
}
|
||||
}
|
||||
|
||||
if (seeds.size) console.log(`changed crates:\n ${[...seeds].join('\n ')}`)
|
||||
|
||||
// a plugin is affected when its dependency tree (itself included) reaches a seed,
|
||||
// so walk the reverse dependency graph from the seeds
|
||||
const dependents = new Map()
|
||||
for (const [id, pkg] of lock) {
|
||||
for (const dep of pkg.dependencies) {
|
||||
dependents.set(dep, [...(dependents.get(dep) ?? []), id])
|
||||
}
|
||||
}
|
||||
const reached = new Set(seeds)
|
||||
const queue = [...seeds]
|
||||
while (queue.length) {
|
||||
for (const dependent of dependents.get(queue.pop()) ?? []) {
|
||||
if (!reached.has(dependent)) {
|
||||
reached.add(dependent)
|
||||
queue.push(dependent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const reachedWorkspaceCrates = new Set(
|
||||
[...reached]
|
||||
.map((id) => lock.get(id))
|
||||
.filter((pkg) => !pkg.source)
|
||||
.map((pkg) => pkg.name)
|
||||
)
|
||||
return all.filter((name) => reachedWorkspaceCrates.has(name))
|
||||
}
|
||||
|
||||
const plugins = readPlugins()
|
||||
const packages = affectedPackages(plugins).sort((a, b) => {
|
||||
const rank = (p) =>
|
||||
preferredFirst.includes(p) ? preferredFirst.indexOf(p) : Infinity
|
||||
return rank(a) - rank(b) || a.localeCompare(b)
|
||||
})
|
||||
|
||||
const shardSize = Number(process.env.SHARD_SIZE) || 8
|
||||
const shardCount = Math.ceil(packages.length / shardSize)
|
||||
const perShard = Math.ceil(packages.length / Math.max(shardCount, 1))
|
||||
const shards = Array.from({ length: shardCount }, (_, i) => {
|
||||
const shard = packages.slice(i * perShard, (i + 1) * perShard)
|
||||
return {
|
||||
name:
|
||||
shard.length <= 3
|
||||
? shard.map((p) => p.replace('tauri-plugin-', '')).join(', ')
|
||||
: `${i + 1}/${shardCount}`,
|
||||
packages: shard.join(' '),
|
||||
save: i === 0
|
||||
}
|
||||
})
|
||||
|
||||
console.log(`affected packages:\n ${packages.join('\n ') || '(none)'}`)
|
||||
|
||||
if (process.env.GITHUB_OUTPUT) {
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_OUTPUT,
|
||||
`packages=${JSON.stringify(packages)}\nshards=${JSON.stringify(shards)}\n`
|
||||
)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub enum MacosLauncher {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
impl MacosLauncher {
|
||||
fn to_auto_launch(&self) -> auto_launch::MacOSLaunchMode {
|
||||
fn to_auto_launch(self) -> auto_launch::MacOSLaunchMode {
|
||||
match self {
|
||||
MacosLauncher::LaunchAgent => auto_launch::MacOSLaunchMode::LaunchAgent,
|
||||
MacosLauncher::AppleScript => auto_launch::MacOSLaunchMode::AppleScript,
|
||||
|
||||
@@ -31,3 +31,6 @@ tauri = { workspace = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde_repr = "0.1"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
tauri = { workspace = true, features = ["wry"] }
|
||||
@@ -30,3 +30,6 @@ tauri = { workspace = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
serde_repr = "0.1"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
tauri = { workspace = true, features = ["wry"] }
|
||||
Reference in new issue
Block a user