Update dependency @tauri-apps/plugin-updater to v2.11.0 #1579
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] | |
| pull_request: | |
| permissions: | |
| contents: read | |
| jobs: | |
| build-and-test: | |
| name: Build & Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: package.json | |
| - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # The disclosed dependency snapshots are a `FAIL IF` in docs/specs/security-supply-chain.md | |
| # (Disclosure), but until this step existed the only thing | |
| # that ever ran the generator was the nightly security audit — strictly | |
| # after the fact, and only if it reached that check. Two prod-dependency | |
| # bumps shipped undisclosed that way (`ws` via vscode-ext, `hono` via a | |
| # Renovate lockfile-only bump). Running it here makes forgetting to | |
| # regenerate a failed PR instead of a finding filed the next morning. | |
| # | |
| # The generator needs `node_modules` to match the lockfile — it resolves | |
| # every dependency by walking real directories and throws rather than | |
| # under-reporting — which is exactly what `--frozen-lockfile` above | |
| # guarantees. | |
| - name: Dependency disclosure is current | |
| run: | | |
| node website/scripts/generate-deps.js | |
| if ! git diff --quiet -- website/src/data/; then | |
| echo "::error::The disclosed dependency snapshots are stale. Run \`node website/scripts/generate-deps.js\` and commit the result." | |
| git --no-pager diff -- website/src/data/ | |
| exit 1 | |
| fi | |
| # zsh is not on the ubuntu runner image, and it is the default shell on | |
| # macOS — our primary platform. Without it, `standalone/sidecar`'s | |
| # shell-integration suite silently covered only bash, which is half of | |
| # what stands behind an emit-side security fix (docs/specs/terminal-escapes.md's OSC 633 | |
| # rules; the emitters are the boundary, since the parser cannot defend | |
| # against a terminator that arrives inside a directory name). The suite | |
| # names the shells it covered on every run, so a future image change that | |
| # drops one is visible rather than silent. | |
| # `update` first: the runner image's apt lists are baked at image build, so | |
| # once the archive rotates zsh's version the cached Packages entry 404s. | |
| # `timeout` because a degraded Azure mirror dribbles bytes rather than | |
| # failing — see the fuller treatment in Standalone Smoketest below, which | |
| # also rotates mirrors. That much machinery is not worth it for one | |
| # package; the retry here covers the common case without turning a | |
| # `Build & Test` red for a reason unrelated to the diff. | |
| # 13 minutes, not 6: the budget has to outlast the schedule it wraps, or | |
| # Actions kills the step mid-attempt and the ::error:: below — the line | |
| # that says what the failure costs — never prints. Worst case is | |
| # 3 x (120 update + 120 install) + 2 x 15 sleep = 750s, and the last sleep | |
| # is guarded to keep that `2 x` true: unguarded it is 3 x 15 = 765s, which | |
| # halves the headroom for pure dead time before a step that is already | |
| # failing. | |
| - name: Install zsh (shell-integration tests) | |
| timeout-minutes: 13 | |
| run: | | |
| for i in 1 2 3; do | |
| if sudo timeout 120 apt-get update -q \ | |
| && sudo timeout 120 apt-get install -y -q --no-install-recommends zsh; then | |
| exit 0 | |
| fi | |
| echo "::warning::apt attempt $i for zsh failed or timed out" | |
| if [ "$i" -lt 3 ]; then sleep 15; fi | |
| done | |
| echo "::error::could not install zsh; the shell-integration suite would silently cover only bash" | |
| exit 1 | |
| - name: Test | |
| run: pnpm test | |
| - name: Build | |
| run: pnpm build | |
| # The Linux installer is the only one CI can execute, and most of what it | |
| # does is shared with its two siblings: the release build, the staging, | |
| # the self-contained runtime copy, the candidate health probe on an | |
| # ephemeral port, the `current` switch, the prune, and the generated | |
| # `manage`. Test mode stops short of systemd and Serve, and the injected | |
| # origin is what lets it run on a machine with no tailnet. | |
| - name: Self-host installer (Linux, test mode) | |
| env: | |
| DORMOUSE_INSTALL_TEST: '1' | |
| DORMOUSE_INSTALL_ORIGIN: https://ci.example.ts.net | |
| run: | | |
| set -euo pipefail | |
| root="$(mktemp -d)/dormouse-relay" | |
| offer="$root/run/enroll-offer.json" | |
| # Prints the token, because it must rotate on every run before the | |
| # first Host enrollment — and this job is the only executable check. | |
| # | |
| # The shape check is the server's own `isEnrollmentOffer`, imported | |
| # rather than restated: a CI copy of the token regex and the origin | |
| # rule is free to drift from the validator the redemption path | |
| # actually runs, and then agrees with itself while the server rejects | |
| # what the installer writes. No build step is needed for it: the | |
| # `Test` step above builds remote-lib-common, as does the | |
| # `pnpm --filter relay build` inside every install below. | |
| read_offer() { | |
| node --input-type=module -e ' | |
| import { readFileSync, statSync } from "node:fs"; | |
| import { isEnrollmentOffer } from "./remote-lib-common/dist/index.js"; | |
| const [path, wantOrigin] = process.argv.slice(1); | |
| const mode = statSync(path).mode & 0o777; | |
| if (mode !== 0o600) throw new Error(`${path} is mode ${mode.toString(8)}, expected 600`); | |
| const offer = JSON.parse(readFileSync(path, "utf8")); | |
| if (!isEnrollmentOffer(offer)) throw new Error(`not an enrollment offer: ${JSON.stringify(offer)}`); | |
| // Beyond the structural guard, and nothing else checks it: the | |
| // server hard-rejects a mintedAt it cannot parse, which the shape | |
| // guard — a length bound — accepts. | |
| if (Number.isNaN(Date.parse(offer.mintedAt))) throw new Error(`mintedAt is ${offer.mintedAt}`); | |
| if (offer.origin !== wantOrigin) throw new Error(`origin is ${offer.origin}`); | |
| process.stdout.write(offer.token); | |
| ' "$offer" "$DORMOUSE_INSTALL_ORIGIN" | |
| } | |
| # Twice: the first is a first install, the second exercises the update | |
| # path — previous pointer, prune, and preserving config/relay.env. | |
| DORMOUSE_INSTALL_ROOT="$root" ./deploy/local/install-linux.sh --yes | |
| before="$(sha256sum "$root/config/relay.env" | cut -d' ' -f1)" | |
| first_token="$(read_offer)" | |
| DORMOUSE_INSTALL_ROOT="$root" ./deploy/local/install-linux.sh --yes | |
| after="$(sha256sum "$root/config/relay.env" | cut -d' ' -f1)" | |
| second_token="$(read_offer)" | |
| [ "$before" = "$after" ] || { echo "::error::relay.env was not preserved across an update"; exit 1; } | |
| [ "$first_token" != "$second_token" ] || { echo "::error::the enrollment offer was not re-minted on the update"; exit 1; } | |
| # burrows.json existence, not its current row count, is the durable | |
| # "first Burrow happened" marker. Even an empty hand-edited file keeps | |
| # a later installer run from reopening bootstrap. | |
| printf '[]\n' > "$root/state/burrows.json" | |
| chmod 0600 "$root/state/burrows.json" | |
| DORMOUSE_INSTALL_ROOT="$root" ./deploy/local/install-linux.sh --yes | |
| [ ! -e "$offer" ] || { echo "::error::the enrollment offer survived after burrows.json existed"; exit 1; } | |
| [ -L "$root/previous" ] || { echo "::error::no previous release retained after an update"; exit 1; } | |
| bash -n "$root/bin/manage" | |
| bash -n "$root/bin/run-relay" | |
| webview-smoketest: | |
| name: Webview Boot Smoketest | |
| runs-on: ubuntu-latest | |
| # Its own job, in parallel with Build & Test: this is the only check that | |
| # needs a browser, and pinning that cost to one job keeps `pnpm test` free of | |
| # it. It is also the only check that runs the shipped bundle rather than | |
| # inspecting it — the VS Code webview's CSP is enforced by Chromium, so a | |
| # policy that blocks the app is invisible to every string-level test we have | |
| # (docs/specs/vscode.md -> "CSP policy"). | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: package.json | |
| - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| # Frozen for the same supply-chain reason as every other job here. | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # `playwright-core` ships no browser, deliberately — a unit-test run should | |
| # not drag one down. Fetch it here, where it is actually used. | |
| - name: Install Chromium | |
| run: pnpm --filter dormouse exec playwright-core install --with-deps chromium | |
| # The smoketest loads what the build emits, so the build has to precede | |
| # it. `build:frontend` alone — this job never touches the extension host | |
| # bundle — and its `prebuild:frontend` hook builds the two workspace | |
| # packages the webview bundle imports, which a clean checkout has no dist | |
| # for. | |
| - name: Build the webview frontend | |
| run: pnpm --filter dormouse build:frontend | |
| - name: Smoketest | |
| run: pnpm --filter dormouse test:smoke | |
| standalone-smoketest: | |
| name: Standalone Smoketest | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # `cargo check` runs standalone/src-tauri/build.rs, which fails the build | |
| # unless the Node.js on PATH exactly matches package.json's | |
| # devEngines.runtime.version pin. setup-node's node-version-file reads that | |
| # exact pin from package.json (volta.node → devEngines.runtime → | |
| # engines.node), so the smoketest can't drift off the pin the way a bare | |
| # `node-version: 24` would when the runner image bumps its 24.x. | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: package.json | |
| - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable | |
| - name: Install system dependencies | |
| # apt mirrors are flaky: a healthy run finishes in <1min, but a degraded | |
| # Azure mirror can dribble bytes for 15-80min, and retrying the SAME mirror | |
| # doesn't help. Wrap each apt call in `timeout` (turns "slow" into a failed | |
| # attempt) and rotate the mirror host between attempts so a sustained | |
| # single-mirror problem is escaped. Azure first (fast, same datacenter), | |
| # archive.ubuntu.com as fallback. --no-install-recommends trims the download. | |
| # | |
| # 16 minutes, not 10: the budget has to outlast the schedule it wraps, or | |
| # Actions kills the step mid-attempt and the ::error:: below — the line | |
| # that says the mirrors were rotated and still failed — never prints, | |
| # leaving a bare timeout in exactly the sustained-outage case the mirror | |
| # rotation exists for. Worst case is | |
| # 3 x (120 update + 180 install) + 3 x 15 sleep = 945s (the loop | |
| # sleeps after the third failed attempt too, before the ::error::). | |
| timeout-minutes: 16 | |
| run: | | |
| mirrors=(azure.archive.ubuntu.com archive.ubuntu.com) | |
| src_files=(/etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list) | |
| for i in 0 1 2; do | |
| host=${mirrors[$(( i % ${#mirrors[@]} ))]} | |
| for f in "${src_files[@]}"; do | |
| [ -f "$f" ] && sudo sed -i -E "s|https?://[a-z.]*archive\.ubuntu\.com/ubuntu|http://$host/ubuntu|g" "$f" | |
| done | |
| echo "::group::apt attempt $((i + 1)) via $host" | |
| if sudo timeout 120 apt-get update -q && \ | |
| sudo timeout 180 apt-get install -y -q --no-install-recommends \ | |
| libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev; then | |
| echo "::endgroup::"; exit 0 | |
| fi | |
| echo "::endgroup::" | |
| echo "::warning::apt via $host failed or timed out; rotating mirror, retrying in 15s" | |
| sleep 15 | |
| done | |
| echo "::error::apt failed after 3 attempts across mirrors" | |
| exit 1 | |
| # Frozen for the same supply-chain reason as every other job here. | |
| - name: Install npm dependencies | |
| run: pnpm install --frozen-lockfile | |
| working-directory: standalone | |
| - name: TypeScript check | |
| run: npx tsc --noEmit | |
| working-directory: standalone | |
| # `cargo test` rather than `cargo check`: the crate carries unit tests | |
| # (path/session/sidecar resolution in lib.rs) that no job ran, so they | |
| # compiled at most as `cargo check` and never executed. Building the test | |
| # harness also links, which `check` does not. | |
| - name: Cargo test | |
| run: cargo test | |
| working-directory: standalone/src-tauri | |
| standalone-platform-check: | |
| # The job above runs on Linux only, so it compiles none of the crate's | |
| # `#[cfg(windows)]` / `#[cfg(target_os = "macos")]` code — two whole modules | |
| # (clipboard_win.rs, pe_subsystem.rs), the platform arms throughout lib.rs | |
| # and build.rs, and the unit tests gated with them. Without this job their | |
| # first compile is the Windows / macOS leg of release.yml, which runs only on | |
| # a `v*` tag: a typo in Windows-only Rust surfaces as a failed release rather | |
| # than as a failed PR. Deliberately a separate job from the smoketest above, | |
| # so that job keeps its registered check name. | |
| name: Standalone Platform Check (${{ matrix.platform }}) | |
| strategy: | |
| # Each platform's compile is independent; a Windows failure must not hide | |
| # what macOS would have said. | |
| fail-fast: false | |
| matrix: | |
| platform: [windows-latest, macos-latest] | |
| runs-on: ${{ matrix.platform }} | |
| # A hung compile on a premium-billed runner must not ride the 6-hour job | |
| # default. Cold-cache runs land at ~2min (macOS) and ~5min (Windows). | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # Same pin-driven setup as the smoketest above: build.rs fails unless the | |
| # Node.js on PATH matches package.json's devEngines.runtime.version. | |
| - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version-file: package.json | |
| - uses: dtolnay/rust-toolchain@6bed0761d98439e5a578e2877258200ad565ba87 # stable | |
| # Windows and macOS runners bill at a multiple of Linux, so the dependency | |
| # compile is cached rather than paid on every PR. | |
| - name: Rust cache | |
| uses: swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2 | |
| with: | |
| workspaces: standalone/src-tauri | |
| # No pnpm install and no TypeScript check here. The TS check is | |
| # platform-independent and the smoketest above already runs it; and the Rust | |
| # build never reads node_modules — build.rs needs only `node` on PATH (it | |
| # resolves the binary with `node -p process.execPath`) plus the root | |
| # package.json version pin. This job exists for the Rust the smoketest | |
| # cannot see. | |
| - name: Cargo test | |
| run: cargo test | |
| working-directory: standalone/src-tauri |