@@ -22,21 +22,59 @@ inputs:
2222runs :
2323 using : " composite"
2424 steps :
25- - name : Extract Rust version from rust-toolchain.toml
25+ - name : Extract Rust version and compose the cache key
2626 id : rustver
2727 shell : bash
28+ env :
29+ CACHE_POOL : ${{ inputs.cache-pool }}
2830 run : |
2931 rust_version=$(grep -E '^[[:space:]]*channel[[:space:]]*=' rust-toolchain.toml \
3032 | sed -E 's/.*"([^"]+)".*/\1/')
3133 echo "rust_version=${rust_version}" >>"$GITHUB_OUTPUT"
3234 echo "Rust version: ${rust_version}"
35+ # Folded into this step rather than its own: `pr_loc.yaml` checks out two
36+ # revisions in one job and runs this action from each, and a differing
37+ # step list between them trips the runner's post-step bookkeeping with
38+ # "Index was out of range".
39+ image_family="${ImageOS:-unknown}"
40+ echo "image_family=${image_family}" >>"$GITHUB_OUTPUT"
41+ echo "Runner image family: ${image_family}"
42+ # rust-cache appends its `key` input only on the branch that builds a
43+ # per-job key; when `shared-key` is set it takes that branch instead and
44+ # `key` is dropped entirely. So a pooled job has to carry the image
45+ # family inside the shared key itself. Empty when no pool is configured,
46+ # which leaves rust-cache on the per-job key where `key` does apply.
47+ if [ -n "${CACHE_POOL}" ]; then
48+ echo "shared_key=${CACHE_POOL}-${image_family}" >>"$GITHUB_OUTPUT"
49+ else
50+ echo "shared_key=" >>"$GITHUB_OUTPUT"
51+ fi
3352
3453 - name : Install Rust
3554 uses : dtolnay/rust-toolchain@master
3655 with :
3756 toolchain : ${{ steps.rustver.outputs.rust_version }}
3857 components : ${{ inputs.components }}
3958
59+ # rust-cache keys on `runner.os` and `runner.arch` only, which are `Linux`
60+ # and `x64` for both ubuntu-22.04 and ubuntu-24.04 — so a pool shared by
61+ # jobs on different images resolves to one entry spanning both. Cached
62+ # proc-macro `.so`s are dynamically linked against the glibc of whichever
63+ # image built them, so restoring a 24.04 entry into a 22.04 job produces
64+ # `GLIBC_2.39 not found` when rustc dlopens the macro, and the crate fails
65+ # with a wall of unresolved-import errors that name the dependency rather
66+ # than the cache.
67+ #
68+ # `ImageOS` (ubuntu22 / ubuntu24 / macos15) is the image family, so it
69+ # separates the glibc floors without keying on `ImageVersion`, which
70+ # changes with every runner image release and would evict the pool weekly.
71+ # It goes into `shared-key`, not `key`: rust-cache ignores `key` whenever
72+ # `shared-key` is set, so keying the image family there alone left every
73+ # pooled job on the shared, image-blind key it was meant to split.
74+ # Read via `$GITHUB_OUTPUT` rather than `env.ImageOS` directly: the `env`
75+ # context is not reliably available to `with:` on a `uses:` step inside a
76+ # composite action. Self-hosted runners may not set it; they then share one
77+ # `unknown` bucket, which is still consistent within itself.
4078 # The Actions cache budget defaults to 10GB per repository and is evicted
4179 # LRU, while one target dir here runs 1.2-2.75GB, so the budget holds only a
4280 # handful of entries. Two rules keep them from evicting each other:
5896 - name : Add Rust Cache
5997 uses : Swatinem/rust-cache@v2
6098 with :
61- shared-key : ${{ inputs.cache-pool }}
99+ shared-key : ${{ steps.rustver.outputs.shared_key }}
100+ key : ${{ steps.rustver.outputs.image_family }}
62101 save-if : ${{ inputs.save-cache == 'true' && github.ref == 'refs/heads/main' }}
63102 workspaces : |
64103 .
0 commit comments