Files
fips/.github/scripts/keep-pinned-toolchain.sh
Johnathan Corgan 12a7e11032 Give each CI job its own dependency-only cargo cache, saved from master
The build legs, the unit-test jobs and both Linux architectures all wrote
the cache key `<os>-cargo-<lock>`, so whichever job saved first owned the
entry and the others recompiled every dependency on an exact hit: a Windows
unit-test run compiled 285 crates against the release build's entry. The
repository's cache had also grown past its 10 GB limit, with every branch
saving its own copies.

Each job that builds Rust now uses Swatinem/rust-cache with a key per job,
OS and architecture, a hash of the installed toolchains and Cargo
environment, and a hash of Cargo.lock and the manifests with the workspace
versions blanked, so a version bump keeps the key. It caches the registry
and the dependency artifacts only, and only a push to master saves; every
other ref restores master's entry.

rust-cache hashes every installed toolchain, and GitHub's runner images
ship their own stable Rust that changes with each image release, so each
cached job first removes every toolchain but the one rust-toolchain.toml
pins, and fails unless only that one is left.
2026-10-21 16:58:11 +00:00

66 lines
2.5 KiB
Bash
Executable File

#!/bin/bash
# ── Remove every Rust toolchain but the pinned one ──────────────────────────
# rust-cache puts a hash of every installed toolchain's `rustc -vV` in its cache
# key. GitHub's runner images ship their own stable Rust, which moves with each
# image release, so while it stays installed the key changes whenever GitHub
# rolls a new image, and during a rollout runs alternate between two keys and
# miss each other's entries (rustc 1.99.0 on image 20261012, 1.99.1 on
# 20261018). Removing it leaves only rust-toolchain.toml's channel in the hash.
#
# The channel is read from a line exactly `channel = "<version>"`; any other
# spelling fails closed with "no channel".
#
# Called from .github/workflows/ci.yml between the toolchain install and the
# cache step of every job that uses rust-cache.
#
# Exit 0 = `rustup toolchain list --quiet`, the list rust-cache hashes, names
# only the pinned channel. Non-zero = the pin could not be read, the pinned
# toolchain is missing, or another toolchain is still installed afterwards.
# ─────────────────────────────────────────────────────────────────────────────
set -euo pipefail
pinned=$(sed -n 's/^channel = "\(.*\)"$/\1/p' rust-toolchain.toml)
if [[ -z $pinned ]]; then
echo "keep-pinned-toolchain: no channel in rust-toolchain.toml" >&2
exit 1
fi
# Names only, without the Windows carriage return; rustup exits 0 when asked
# to uninstall a name it does not have, so a mangled name would otherwise
# leave its toolchain in place unnoticed.
toolchains() {
local list
list=$(rustup toolchain list --quiet)
printf '%s\n' "${list//$'\r'/}"
}
list=$(toolchains)
kept=""
while read -r name; do
if [[ $name == "$pinned"-* ]]; then
kept=${kept:-$name}
fi
done <<<"$list"
if [[ -z $kept ]]; then
echo "keep-pinned-toolchain: toolchain $pinned is not installed" >&2
exit 1
fi
# The image's stable is usually the default; repoint it first so nothing is
# left defaulting to a removed toolchain.
rustup default "$kept"
while read -r name; do
if [[ -n $name && $name != "$pinned"-* ]]; then
rustup toolchain uninstall "$name"
fi
done <<<"$list"
left=$(toolchains)
while read -r name; do
if [[ $name != "$pinned"-* ]]; then
echo "keep-pinned-toolchain: still installed: '$name'" >&2
exit 1
fi
done <<<"$left"
printf '%s\n' "$left"