Files
max-sixty__worktrunk/.github/workflows/ci.yaml
T
Worktrunk Bot 126f022416 ci: bump Windows runner images from windows-2022 to windows-2025 (#3974)
## Summary

`windows-2022` was deliberately held back, and the reason recorded in
`ci.yaml`'s header no longer holds. The comment reads "Pinned because
windows-2025 lacks D: drive" — but GitHub reversed that removal:
[actions/runner-images#12744](https://github.com/actions/runner-images/issues/12744)
announced that from 2025-08-18 the Windows Server 2025 image again
exposes a `D:\` drive, with the working directory back on `D:\`,
explicitly reversing
[#12416](https://github.com/actions/runner-images/issues/12416).
`windows-2022` has also fallen off the `windows-latest` label, so the
pin is due on the ordinary weekly rule too.

This moves the four `windows-2022` matrix entries to `windows-2025` and
rewrites the header comment, which no longer needs a hold-back line.
Split from the week's other pin bumps
([#3971](https://github.com/max-sixty/worktrunk/pull/3971)) so a red
Windows matrix decides only this bump.

The `D:\` drive is what prompted the change because the workflows use
it: `ci.yaml`, `affected.yaml`, and `nightly.yaml` each run a `Use fast
D: drive for temp files (Windows)` step that creates `D:\tmp` and points
`TEMP`/`TMP` at it. Those steps are unchanged and keep working, since
#12744 restored exactly that drive.

## The label also swaps the MSVC toolset

`D:\` is not the only variable this bump moves. `windows-2025` is no
longer the Visual Studio 2022 image:
[actions/runner-images#14017](https://github.com/actions/runner-images/issues/14017)
moved `windows-latest` and `windows-2025` onto the Windows Server 2025 +
VS 2026 image in June 2026. The availability table now carries a single
Windows Server 2025 row whose three labels — `windows-latest`,
`windows-2025`, `windows-2025-vs2026` — all resolve to
`Windows2025-VS2026-Readme.md`. So CI's Windows legs compile with the VS
2026 toolset after this PR.

The second-order effect is that CI and the release build no longer share
a toolset. `dist` computes the release matrix itself and is untouched
here: v0.69.2 built `x86_64-pc-windows-msvc` on `build-local-artifacts
(windows-2022, …)`, and `windows-2022` is not deprecated, so `dist`
keeps picking it. Nothing now exercises the toolset the shipped binary
is compiled and linked with. That is a real gap, but a low-risk one and
a separate decision from this bump — nothing in the tree pins a VS path
(`build.rs` is vergen-only; the `cc` / `find-msvc-tools` route discovers
VS through vswhere), and `affected tests (windows, advisory)` is green
on this head. Aligning the release runner is a release-matrix change
that belongs in its own PR.

## Verification

This PR's own `test (windows)`, `full-tests (windows-2025, windows)`,
and `affected tests (windows, advisory)` legs are the only place this
gets tested — the weekly runner is Linux.

Two failure shapes to look for if Windows goes red, not one:

- **`D:`-shaped** — the `D:\tmp` step fails to create the directory,
meaning #12744's restoration doesn't apply to this repo's runners and
the pin should go back.
- **Toolset-shaped** — a compile or link failure from the VS 2026 move.
[#14004](https://github.com/actions/runner-images/issues/14004) is an
open report of this same label swap breaking hardcoded VS 2022 paths;
nothing here hardcodes one, but a future Windows-only break is as likely
to be this shape as the first.

The throughput question this description originally left open is
answered: no measurable regression. Every Windows leg here lands inside
the same-day `windows-2022` spread from the sibling pin PRs, and the
advisory leg is the fastest of the five — durations are tabulated in
[the first
review](https://github.com/max-sixty/worktrunk/pull/3974#pullrequestreview-5064565794).
The remaining bias runs against this PR, not for it: `save-if` gates the
rust-cache write to `main`, so this run restored a cache written on
`windows-2022`.
[#12647](https://github.com/actions/runner-images/issues/12647)'s
reported slowdown does not reproduce here.

`ci.yaml`'s header comment now keeps each pin on its own line, so this
PR's edit and
[#3973](https://github.com/max-sixty/worktrunk/pull/3973)'s `macos-15` →
`macos-26` edit stay on separate lines. They are still adjacent lines,
so whichever lands second needs a one-hunk resolution keeping both —
details in [this
comment](https://github.com/max-sixty/worktrunk/pull/3974#issuecomment-5476167137).
The three matrix files auto-merge clean.

<details><summary>Availability table rows this reads from</summary>

From `actions/runner-images` `README.md`:

- Windows Server 2025 — `windows-latest`, `windows-2025`,
`windows-2025-vs2026`; Included Software links
`images/windows/Windows2025-VS2026-Readme.md`
- Windows Server 2022 — `windows-2022` (no `-latest`), linking
`images/windows/Windows2022-Readme.md`

`windows-2022` is not badged `deprecated` yet, but it is now the older
of the two GA images, which the weekly rule treats as next due. That it
is undeprecated is also why `dist` still selects it for the release
build.

</details>

---------

Co-authored-by: worktrunk-bot <254187624+worktrunk-bot@users.noreply.github.com>
2026-08-31 20:19:53 -07:00

234 lines
7.7 KiB
YAML

name: ci
# Runner versions are pinned to avoid surprise breaking changes from -latest migrations.
# The tend-weekly workflow checks for updates and opens bump PRs (see
# .claude/skills/running-tend/SKILL.md → "Weekly Maintenance: CI Pin Bumps").
# - windows-2025: Pinned to current -latest equivalent
# - ubuntu-24.04, macos-26: Pinned to current -latest equivalents
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
env:
CARGO_TERM_COLOR: always
# CI does clean builds, so incremental compilation tracking adds I/O overhead
# with no benefit. Especially impactful on Windows where I/O is the bottleneck.
CARGO_INCREMENTAL: 0
RUSTFLAGS: -C debuginfo=0
jobs:
lint:
runs-on: ubuntu-24.04
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- name: 💰 Cache
uses: Swatinem/rust-cache@v2
with:
# Own cache (clippy builds check artifacts the shared test cache can't
# supply), gated to main so PRs restore but never write.
cache-bin: "false"
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: 🔍 Pre-commit hooks
uses: pre-commit/action@v3.0.1
feature-check:
# Guards the library/CLI cleave: the `cli` feature gates clap, skim,
# crossterm, termimad, env_logger, humantime. If anything reachable from
# `pub mod` in src/lib.rs starts using one of those crates, library
# consumers (e.g. worktrunk-sync) would silently regain the transitive
# dependency graph. These checks fail before that can ship.
runs-on: ubuntu-24.04
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- name: 💰 Cache
uses: Swatinem/rust-cache@v2
with:
# Own cache (its `--no-default-features` builds want a smaller dep set
# than the shared test cache), gated to main.
cache-bin: "false"
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Library builds without any features
run: cargo check --lib --no-default-features
- name: Library builds with syntax-highlighting only
run: cargo check --lib --no-default-features --features syntax-highlighting
- name: Binary builds without syntax-highlighting
run: cargo check --bin wt --no-default-features --features cli
test:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-24.04
name: linux
- os: macos-26
name: macos
- os: windows-2025
name: windows
name: test (${{ matrix.name }})
runs-on: ${{ matrix.os }}
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- uses: ./.github/actions/test-setup
- name: Install wt
uses: baptiste0928/cargo-install@v3
with:
crate: worktrunk
version: "=0.75.0"
- name: "Use fast D: drive for temp files (Windows)"
if: runner.os == 'Windows'
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path "D:\tmp" | Out-Null
echo "TEMP=D:\tmp" >> $env:GITHUB_ENV
echo "TMP=D:\tmp" >> $env:GITHUB_ENV
# The full nextest suite (snapshots included) — CI's long-pole. The fast
# pre-merge gates (lockfile/doctest/rustdoc) live in the `fast-checks` job,
# so this job is now just the suite and can later be swapped for the
# cargo-affected legs on PRs without disturbing those gates. The `insta`
# step runs `--unreferenced reject` on Linux (orphan-snapshot detection) —
# an intrinsically full-suite check that rides this job on push-to-main and,
# after the flip, stays there.
- name: 🧪 Full test suite
run: wt hook pre-merge --yes insta
# Anything tests leave behind in the working tree is a leak we want to see
# before it lands. `target/` is gitignored, so a normal run is clean —
# untracked files here mean a test wrote outside its sandbox (the
# `default_*.profraw` stray that prompted this guard is the canonical case).
- name: 🧹 Verify clean working tree
shell: bash
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "::error::tests left files behind in the working tree:"
git status --porcelain
exit 1
fi
- name: 📊 Upload test timing
if: always()
uses: actions/upload-artifact@v7
with:
name: junit-${{ matrix.name }}
path: target/nextest/default/junit.xml
- name: 📊 Upload test results to Codecov
if: ${{ !cancelled() }}
uses: codecov/codecov-action@v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/nextest/default/junit.xml
report_type: test_results
# The pre-merge gates that aren't the nextest suite: Cargo.lock freshness,
# doctests, and the rustdoc `-Dwarnings` build. Split out of `test` so the
# full suite (the long-pole) can later be replaced on PRs by the
# cargo-affected legs without dropping these — they stay required and
# untouched by that flip. Linux-only: all three are platform-independent. The
# one blind spot is doc/doctest inside `#[cfg(windows)]` / `#[cfg(target_os
# = …)]` code, which only the full `test` matrix compiles; acceptable for a
# gate whose job is broken links and stale locks, not platform behavior.
fast-checks:
runs-on: ubuntu-24.04
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- uses: ./.github/actions/test-setup
- name: Install wt
uses: baptiste0928/cargo-install@v3
with:
crate: worktrunk
version: "=0.75.0"
- name: 🧪 Lockfile, doctests, rustdoc
run: wt hook pre-merge --yes lockfile doctest doc
# Check if Cargo/CI files changed (for conditional jobs below)
changes:
runs-on: ubuntu-24.04
permissions:
contents: read
pull-requests: read
outputs:
cargo: ${{ steps.filter.outputs.cargo }}
docs: ${{ steps.filter.outputs.docs }}
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
cargo:
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.github/workflows/ci.yaml'
docs:
- 'docs/**'
- 'src/cli/mod.rs'
- '.github/actions/docs-build/**'
- '.github/workflows/ci.yaml'
- '.github/workflows/nightly.yaml'
- '.github/workflows/publish-docs.yaml'
msrv:
runs-on: ubuntu-24.04
needs: changes
if: needs.changes.outputs.cargo == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- name: 💰 Cache
uses: Swatinem/rust-cache@v2
with:
# Own cache — `cargo msrv verify` builds with older toolchains whose
# rustc hash never matches the shared (stable) test cache. Gated to
# main; this job only runs when Cargo/toolchain files change.
cache-bin: "false"
save-if: ${{ github.ref == 'refs/heads/main' }}
- uses: baptiste0928/cargo-install@v3
with:
crate: cargo-msrv
version: "=0.19.3"
- name: Verify minimum rust version
run: cargo msrv verify
check-docs:
runs-on: ubuntu-24.04
needs: changes
if: needs.changes.outputs.docs == 'true' || github.event_name == 'workflow_dispatch'
steps:
- name: 📂 Checkout code
uses: actions/checkout@v7
- name: 🕷️ Check and build docs
uses: ./.github/actions/docs-build