desktop: commit Cargo.lock and gate CI on it (issue 2102)
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m52s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 4m37s
Desktop (Tauri) / Update manifest (push) Successful in 5s

The desktop crate is a binary, and binaries commit their lockfile. Without one
every run re-resolved the graph: a tagged .deb/.AppImage/.exe couldn't be
rebuilt from its tag, any semver-compatible upstream release landed
automatically on the next build — the failure mode hardest to read, because the
commit that broke it changed nothing relevant — and Renovate had no lockfile to
bump, leaving Rust dependency movement invisible to the Dashboard.

Generated with cargo generate-lockfile inside ci-tauri:1.97, the same image CI
builds in, so the format and the picked versions are what CI would have chosen
itself. That takes the artifact-upload round-trip the issue proposed off the
table: ci-requirements.md already blesses the image for cargo fmt, and resolving
a dependency graph is no more a build than formatting is. 503 packages.

Enforcement goes on each job's FIRST cargo invocation rather than the bundle
build: cargo clippy --locked on Linux, and its own cargo fetch --locked step on
Windows, whose only crate-graph command is otherwise the cross-compile itself.
Drift fails in the first thirty seconds instead of thirty minutes in, and
everything after the gate in that job compiles the recorded versions anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-16 10:41:18 -04:00
co-authored by Claude Opus 5
parent 2cfe049f9c
commit 5c1ae574f6
3 changed files with 5357 additions and 2 deletions
+16 -2
View File
@@ -52,12 +52,17 @@ jobs:
run: npm ci && npm run build run: npm ci && npm run build
working-directory: frontend working-directory: frontend
# --locked on the FIRST cargo invocation of the job is the lockfile gate: it
# fails the run if Cargo.toml and the committed Cargo.lock disagree, instead
# of silently re-resolving. Everything after it in this job then compiles the
# exact versions recorded in the lockfile, so the flag isn't repeated on the
# bundle build (issue 2102).
- name: Clippy - name: Clippy
run: cargo clippy --all-targets -- -D warnings run: cargo clippy --locked --all-targets -- -D warnings
working-directory: desktop/src-tauri working-directory: desktop/src-tauri
- name: Test - name: Test
run: cargo test run: cargo test --locked
working-directory: desktop/src-tauri working-directory: desktop/src-tauri
# Deliberately AFTER clippy + test, not before. # Deliberately AFTER clippy + test, not before.
@@ -244,6 +249,15 @@ jobs:
run: cargo tauri icon app-icon.png run: cargo tauri icon app-icon.png
working-directory: desktop/src-tauri working-directory: desktop/src-tauri
# This lane's lockfile gate (the Linux job gets it from `cargo clippy
# --locked`). It has to be its own step here because the build is this job's
# only crate-graph command, and discovering the drift 30 minutes into a
# cross-compile is the expensive way to learn it. Fetching for the Windows
# target also pre-warms exactly the crates the build will want.
- name: Verify the lockfile and fetch dependencies
run: cargo fetch --locked --target x86_64-pc-windows-msvc
working-directory: desktop/src-tauri
# --runner cargo-xwin swaps cargo for the cross-compiling driver (it supplies # --runner cargo-xwin swaps cargo for the cross-compiling driver (it supplies
# the MSVC CRT/SDK, pre-warmed into the image, and links with lld-link). # the MSVC CRT/SDK, pre-warmed into the image, and links with lld-link).
# Frontend already built above; skip the beforeBuildCommand rebuild. # Frontend already built above; skip the beforeBuildCommand rebuild.
+30
View File
@@ -179,6 +179,36 @@ files behind; `CARGO_HOME` points somewhere writable for that user.
exceed 100 characters and survive only because rustfmt cannot break a string exceed 100 characters and survive only because rustfmt cannot break a string
literal — copying that shape caused one of the four failures. literal — copying that shape caused one of the four failures.
## The desktop lockfile
`desktop/src-tauri/Cargo.lock` is **committed**, per Cargo's own guidance for
binary crates. Without it every CI run re-resolved the graph, which meant a
released `.deb`/`.AppImage`/`.exe` couldn't be rebuilt from its tag, a build
could break with no repo change, and Renovate had nothing to bump (issue 2102).
Enforced by `--locked` on each job's **first** cargo invocation — `cargo clippy
--locked` on Linux, a dedicated `cargo fetch --locked --target
x86_64-pc-windows-msvc` step on Windows. If the manifest and the lockfile
disagree, the run fails there instead of silently re-resolving; everything after
it in the same job then compiles the recorded versions, so the flag isn't
repeated on the bundle build. The Windows step exists separately because that
job's only crate-graph command is the cross-compile itself, and drift is cheaper
to learn in the first thirty seconds than thirty minutes in.
To regenerate it after a dependency change — same reasoning as `cargo fmt`
above, and resolution is neither a test run nor a build:
```
docker run --rm --user "$(id -u):$(id -g)" -e CARGO_HOME=/tmp/cargo \
-v "$PWD/desktop/src-tauri:/w" -w /w \
git.fabledsword.com/bvandeusen/ci-tauri:1.97 cargo generate-lockfile
```
Resolving inside the CI image rather than against some other cargo is what keeps
the lockfile format and the picked versions identical to what CI would have
chosen. Commit the result in the same change as the `Cargo.toml` edit — a
manifest change pushed without it fails the gate.
## Pushing: `dev` is both a branch and a tag ## Pushing: `dev` is both a branch and a tag
`git push origin dev` fails in this repo: `git push origin dev` fails in this repo:
+5311
View File
File diff suppressed because it is too large Load Diff