Skip to content

Commit d32cab8

Browse files
committed
rust-cache: honor CARGO_HOME and add a restore-only save toggle
The cached paths were hardcoded to $HOME/.cargo, so a container image that bakes the toolchain elsewhere (e.g. /opt/cargo via CARGO_HOME) cached a directory cargo never touches. The new save input (default true) switches the inner step to the restore-only variant of actions/cache — for PR jobs that consume a cache a default-branch warmup job maintains. Selftests cover both: a sentinel round-trips through a custom CARGO_HOME, and a save:false run leaves no cache entry behind.
1 parent b1bd485 commit d32cab8

3 files changed

Lines changed: 91 additions & 4 deletions

File tree

‎.github/actions/rust-cache/action.yml‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ inputs:
1414
description: Cache-family name, so independent jobs (build, tauri, …) keep separate caches.
1515
required: false
1616
default: rust
17+
save:
18+
description: >-
19+
Also save the cache at job end (the actions/cache post step). Set "false" for restore-only
20+
consumers — e.g. PR jobs reading a cache that a default-branch warmup job maintains, where
21+
per-branch saves would only burn quota and job minutes.
22+
required: false
23+
default: "true"
1724
cache-target:
1825
description: >-
1926
Also cache the build target/ directory (keeps compiled dependencies between runs). Off by
@@ -66,12 +73,15 @@ runs:
6673
6774
# Cache cargo's DOWNLOAD cache — registry index + .crate tarballs + git db. Never
6875
# registry/src: cargo re-extracts it from the tarballs, so caching it is pure duplication
69-
# (it roughly doubles the registry footprint for no benefit).
76+
# (it roughly doubles the registry footprint for no benefit). The cargo home comes from
77+
# CARGO_HOME when set — container images that bake the toolchain elsewhere (e.g.
78+
# /opt/cargo) rely on it, and $HOME/.cargo would cache a directory cargo never touches.
79+
CARGO_DIR="${CARGO_HOME:-$HOME/.cargo}"
7080
{
7181
echo 'paths<<__PATHS__'
72-
echo "$HOME/.cargo/registry/index"
73-
echo "$HOME/.cargo/registry/cache"
74-
echo "$HOME/.cargo/git/db"
82+
echo "$CARGO_DIR/registry/index"
83+
echo "$CARGO_DIR/registry/cache"
84+
echo "$CARGO_DIR/git/db"
7585
if [ "$CACHE_TARGET" = "true" ]; then echo "$TARGET_DIR"; fi
7686
if [ -n "$EXTRA_DIRS" ]; then printf '%s\n' "$EXTRA_DIRS"; fi
7787
echo '__PATHS__'
@@ -80,11 +90,23 @@ runs:
8090
# actions/cache restores here and auto-saves at job end (its post step runs even when nested in a
8191
# composite action). An exact-key save is skipped on a hit, so an unchanged Cargo.lock never grows
8292
# the cache; a changed lock seeds a fresh entry and the stale one is LRU-evicted — no ratchet.
93+
# `save: "false"` swaps in the restore-only variant of the same first-party action.
8394
- name: Cache cargo registry/git (+ target)
95+
if: inputs.save == 'true'
8496
uses: actions/cache@v5
8597
with:
8698
path: ${{ steps.meta.outputs.paths }}
8799
key: ${{ inputs.prefix }}-${{ runner.os }}-rust-${{ steps.meta.outputs.rust }}-${{ hashFiles(inputs.lockfiles) }}
88100
restore-keys: |
89101
${{ inputs.prefix }}-${{ runner.os }}-rust-${{ steps.meta.outputs.rust }}-
90102
${{ inputs.prefix }}-${{ runner.os }}-rust-
103+
104+
- name: Restore cargo registry/git (+ target) without saving
105+
if: inputs.save != 'true'
106+
uses: actions/cache/restore@v5
107+
with:
108+
path: ${{ steps.meta.outputs.paths }}
109+
key: ${{ inputs.prefix }}-${{ runner.os }}-rust-${{ steps.meta.outputs.rust }}-${{ hashFiles(inputs.lockfiles) }}
110+
restore-keys: |
111+
${{ inputs.prefix }}-${{ runner.os }}-rust-${{ steps.meta.outputs.rust }}-
112+
${{ inputs.prefix }}-${{ runner.os }}-rust-

‎.github/workflows/selftest.yml‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,64 @@ jobs:
417417
prefix: passthrough
418418
- name: explicit CARGO_INCREMENTAL is preserved
419419
run: test "$CARGO_INCREMENTAL" = "1"
420+
421+
# rust-cache must resolve the cargo home from CARGO_HOME (container images bake the
422+
# toolchain outside $HOME/.cargo): seed a sentinel under a custom CARGO_HOME, let the
423+
# post step save it, and prove a second job restores it into the same custom location.
424+
selftest-cache-cargo-home-save:
425+
name: rust-cache honors CARGO_HOME (save)
426+
runs-on: ubuntu-latest
427+
env:
428+
CARGO_HOME: ${{ runner.temp }}/cargo-home
429+
steps:
430+
- uses: actions/checkout@v6
431+
- name: seed a sentinel in the custom cargo home
432+
run: |
433+
mkdir -p "$CARGO_HOME/registry/index" "$CARGO_HOME/registry/cache" "$CARGO_HOME/git/db"
434+
echo "${{ github.run_id }}" > "$CARGO_HOME/registry/cache/selftest-sentinel"
435+
- uses: ./.github/actions/rust-cache
436+
with:
437+
prefix: cargohome-${{ github.run_id }}
438+
439+
selftest-cache-cargo-home-restore:
440+
name: rust-cache honors CARGO_HOME (restore)
441+
needs: selftest-cache-cargo-home-save
442+
runs-on: ubuntu-latest
443+
env:
444+
CARGO_HOME: ${{ runner.temp }}/cargo-home
445+
steps:
446+
- uses: actions/checkout@v6
447+
- uses: ./.github/actions/rust-cache
448+
with:
449+
prefix: cargohome-${{ github.run_id }}
450+
save: "false"
451+
- name: sentinel came back into the custom cargo home
452+
run: test "$(cat "$CARGO_HOME/registry/cache/selftest-sentinel")" = "${{ github.run_id }}"
453+
454+
# save:"false" must not create a cache entry: run restore-only under a unique prefix,
455+
# then assert no entry with that prefix exists.
456+
selftest-cache-no-save:
457+
name: rust-cache save:false saves nothing
458+
runs-on: ubuntu-latest
459+
steps:
460+
- uses: actions/checkout@v6
461+
- uses: ./.github/actions/rust-cache
462+
with:
463+
prefix: nosave-${{ github.run_id }}
464+
save: "false"
465+
466+
selftest-cache-no-save-assert:
467+
name: rust-cache save:false left no entry
468+
needs: selftest-cache-no-save
469+
runs-on: ubuntu-latest
470+
permissions:
471+
contents: read
472+
actions: read
473+
steps:
474+
- name: no cache entry exists for the nosave prefix
475+
env:
476+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
477+
run: |
478+
COUNT=$(gh cache list --repo ${{ github.repository }} --limit 100 --json key \
479+
--jq '[.[] | select(.key | startswith("nosave-${{ github.run_id }}-"))] | length')
480+
test "$COUNT" -eq 0

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,19 @@ A crate with `publish = false` is validated for tag/version coherence only, so t
5353
Caches cargo for CI and **defaults** incremental compilation off — pure cost in CI (no edit→recompile loop), and the main thing that balloons `target/`.
5454
An explicit `CARGO_INCREMENTAL` (set at the workflow/job level) is obeyed — for the occasional variant build that is faster with it on — and flows through to the Docker actions via their `CARGO_.*` forwarding. The action imposes no build profile; set `CARGO_*` vars to tune the build.
5555
Caches the cargo download cache (registry index + `.crate` tarballs + git db) via the first-party `actions/cache`; optionally caches `target/` too, kept bounded by the `Cargo.lock` key (no restore-key ratchet).
56+
The cargo home is resolved from `CARGO_HOME` when set — container images that bake the toolchain outside `$HOME/.cargo` (e.g. `/opt/cargo`) are cached correctly.
5657
Place it before your build/test steps.
5758

5859
```yaml
5960
- uses: gronke/rust-ci/.github/actions/rust-cache@main
6061
with:
6162
prefix: build # cache family — use a distinct one per job
6263
cache-target: "true" # also cache target/ (off by default)
64+
# save: "false" # restore-only, for jobs consuming a warmup-maintained cache
6365
```
6466

67+
`save: "false"` restores without writing a cache at job end — for PR jobs that read a cache a default-branch warmup job maintains, where per-branch saves would only burn quota and job minutes.
68+
6569
### `build-image`
6670

6771
Builds the toolchain image — `rust:<version>` plus clippy, rustfmt, and jq — locally and loads it into the Docker daemon, so the Docker actions below run against a local `rust-ci:latest` tag with no registry.

0 commit comments

Comments
 (0)