From 4d61b34b85d06a7d4e650e2d7445c8902aa38913 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Tue, 6 Oct 2026 21:22:28 -0400 Subject: [PATCH] ci: the Windows installer waits for the Rust checks, like the Linux bundles do Clippy, the workspace tests and rustfmt move out of the Linux `build` job into their own `verify` job, and both publishing jobs need it. Before this, `windows` needed only `decide`, so on run 8411 a red clippy stopped the Linux lane while the Windows installer built and published to dev-rolling (#5184, rule 177). This commit also carries a deliberately failing step at the end of `verify`. It is the red half of the proof: both publishers must report `skipped`. The next commit removes it. Co-Authored-By: Claude Opus 5.5 --- .forgejo/workflows/desktop.yml | 87 +++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/.forgejo/workflows/desktop.yml b/.forgejo/workflows/desktop.yml index 3076a4b..aa1064d 100644 --- a/.forgejo/workflows/desktop.yml +++ b/.forgejo/workflows/desktop.yml @@ -75,41 +75,40 @@ jobs: sh packaging/guard-forward.sh desktop "$channel" echo "build=$(sh packaging/should-build.sh desktop "$channel")" >> $GITHUB_OUTPUT - build: - name: Tauri desktop (Linux) + # The workspace checks, in their OWN job, so that BOTH publishing jobs can need + # them (rule 177: nothing publishes on red). + # + # They used to be steps inside `build`, which gated the Linux publish and nothing + # else. `windows` needed only `decide`, so on run 8411 clippy failed, the Linux job + # stopped, and the Windows installer built and published to the dev release in the + # same minute (#5184). An edge from each publisher to this job is the gate; a + # verdict inside a sibling job is only a report. + # + # Both publishers need it directly rather than through each other, so a Windows + # failure still never blocks the Linux bundles, and neither waits on the other's + # bundling. No `always()` or `continue-on-error` anywhere on this path: a skipped + # or failed verify must leave both publishers skipped. + verify: + name: Clippy, tests and rustfmt needs: [decide] if: needs.decide.outputs.build == 'true' runs-on: python-ci container: image: git.fabledsword.com/bvandeusen/ci-tauri:1.97 - env: - # AppImage tooling (linuxdeploy) FUSE-mounts itself by default; CI containers - # have no /dev/fuse, so tell it to extract-and-run instead. Without this the - # AppImage bundle step fails with a FUSE error. - APPIMAGE_EXTRACT_AND_RUN: "1" steps: + # No version is derived here, so the default shallow checkout is enough. - uses: actions/checkout@v6 - with: - # DERIVES A VERSION -> needs the whole history. A depth-1 clone sees one - # commit and `git log -- ` produces a too-LOW value, silently, with - # the lane green — note 3127 §6.1, and the direction you cannot recover - # from. `packaging/version.sh` fails loudly on an empty result rather than - # emitting something plausible, which is what turns this into a red lane - # if it is ever dropped. - fetch-depth: 0 # tauri's generate_context! embeds the built frontend at compile time, so the - # frontend must exist before any cargo compile (clippy/test/build), not just - # at bundle time. + # frontend must exist before clippy or the tests can compile the desktop crate. - name: Build the shared frontend run: npm ci && npm run build 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). + # --locked on the FIRST cargo invocation is the lockfile gate: it fails the + # run if Cargo.toml and the committed Cargo.lock disagree, instead of silently + # re-resolving (issue 2102). Both publishing jobs need this job, so neither + # bundles a commit whose lockfile drifted. # # Run from the REPO ROOT with --workspace, not from desktop/src-tauri. # @@ -132,11 +131,45 @@ jobs: # but there is no Rust toolchain on the workstation (the desktop lane is # verified entirely here), so a formatting nit failing first SKIPS clippy and # the tests, and one CI cycle teaches nothing but whitespace. Running it here - # means every push reports its real problems too. Still before the ~20-40 min - # bundle build, so a fmt failure doesn't burn that. + # means every push reports its real problems too. Still before either bundle + # build, so a fmt failure doesn't burn one. - name: Rust format check run: cargo fmt --all --check + # TEMPORARY — the red half of rule 177's proof for #5184. Removed in the very + # next commit. Both publishers must report `skipped` on this run. + - name: Deliberately red, to prove the publishers wait (#5184) + run: exit 1 + + build: + name: Tauri desktop (Linux) + needs: [decide, verify] + if: needs.decide.outputs.build == 'true' + runs-on: python-ci + container: + image: git.fabledsword.com/bvandeusen/ci-tauri:1.97 + env: + # AppImage tooling (linuxdeploy) FUSE-mounts itself by default; CI containers + # have no /dev/fuse, so tell it to extract-and-run instead. Without this the + # AppImage bundle step fails with a FUSE error. + APPIMAGE_EXTRACT_AND_RUN: "1" + steps: + - uses: actions/checkout@v6 + with: + # DERIVES A VERSION -> needs the whole history. A depth-1 clone sees one + # commit and `git log -- ` produces a too-LOW value, silently, with + # the lane green — note 3127 §6.1, and the direction you cannot recover + # from. `packaging/version.sh` fails loudly on an empty result rather than + # emitting something plausible, which is what turns this into a red lane + # if it is ever dropped. + fetch-depth: 0 + + # tauri's generate_context! embeds the built frontend at compile time, so the + # frontend must exist before cargo compiles anything, not just at bundle time. + - name: Build the shared frontend + run: npm ci && npm run build + working-directory: frontend + # Frontend already built above; skip the beforeBuildCommand rebuild. # # createUpdaterArtifacts is applied only when a signing key exists (M10.9): @@ -303,7 +336,7 @@ jobs: # built, not that it runs. A real-machine check stays mandatory before trusting it. windows: name: Windows installer (cross-compiled) - needs: [decide] + needs: [decide, verify] if: needs.decide.outputs.build == 'true' runs-on: python-ci container: @@ -334,8 +367,8 @@ jobs: run: cargo tauri icon app-icon.png 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 + # This lane's own lockfile check (`verify` has already run `cargo clippy + # --locked` for the workspace). 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.