Cache the crates, not the 30 GB of build output

The same leak we just closed on JellyTau, fed harder from here. `target/`
is 30 GB locally (25G debug, 3.1G android, 2.9G release) and was cached
under two keys across five jobs, on the runner both repos share.

- Cache registry/index, registry/cache and git/db only. registry/src is
  left out as well: 155 MB of .crate tarballs beats 1.1 GB extracted, and
  cargo re-extracts it for free. Verified that `cargo fetch` unpacks, so
  sync-android-sources.sh — which reads btleplug's Java backend out of
  registry/src before any build has run — still finds its sources.
- Collapse cargo-host and cargo-android into one cargo-registry key. The
  split only existed to keep the two `target` dirs off each other; with
  target uncached, registry contents are target-independent. This also
  ends a silent miss: build-release's `test` and `build-linux` jobs shared
  cargo-host, so the test job claimed the key and build-linux's cache was
  never saved.
- CARGO_INCREMENTAL=0. Never reused between runs, and the bulk of the
  25 GB debug dir.
- npm: cache ~/.npm instead of ui/node_modules. `npm ci` deletes
  node_modules before installing, so that entry was restored and thrown
  away unread.
- Installer artifact retention 30d -> 7d; the tagged release carries them.

Rust jobs now compile cold every run. sccache with a hard size cap is the
way back if that starts to hurt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 12:11:11 +02:00
co-authored by Claude Opus 5
parent 3143e7e51c
commit 5a89433746
2 changed files with 93 additions and 30 deletions
+40 -12
View File
@@ -16,6 +16,8 @@ on:
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
# Incremental state is never reused between CI runs -- pure disk cost.
CARGO_INCREMENTAL: 0
jobs:
test:
@@ -31,18 +33,33 @@ jobs:
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
# Registry only -- never `target`. That directory is ~30 GB locally
# (25G debug, 3.1G android, 2.9G release) and was cached under two
# keys, on a runner that JellyTau's builds share -- the same leak that
# filled its disk in August 2026. registry/src is left out too: cargo
# re-extracts it for free from registry/cache (155 MB of .crate
# tarballs vs 1.1 GB extracted).
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-host-${{ hashFiles('**/Cargo.lock') }}
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# One shared key across every job. The host/android split existed to
# stop the two `target` dirs clobbering each other; with target no
# longer cached, registry contents are target-independent and every
# job wants the same crates. First job to finish saves; the rest
# restore.
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-host-
${{ runner.os }}-cargo-registry-
- name: Cache Node dependencies
- name: Cache npm downloads
uses: actions/cache@v3
with:
path: ui/node_modules
# npm's download cache, not ui/node_modules: `npm ci` deletes
# node_modules before it installs, so the old cache was restored and
# then immediately thrown away. ~/.npm is what actually makes the
# reinstall fast.
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('ui/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
@@ -100,13 +117,24 @@ jobs:
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
# Registry only -- never `target`. That directory is ~30 GB locally
# (25G debug, 3.1G android, 2.9G release) and was cached under two
# keys, on a runner that JellyTau's builds share -- the same leak that
# filled its disk in August 2026. registry/src is left out too: cargo
# re-extracts it for free from registry/cache (155 MB of .crate
# tarballs vs 1.1 GB extracted).
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-android-${{ hashFiles('**/Cargo.lock') }}
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# One shared key across every job. The host/android split existed to
# stop the two `target` dirs clobbering each other; with target no
# longer cached, registry contents are target-independent and every
# job wants the same crates. First job to finish saves; the rest
# restore.
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-android-
${{ runner.os }}-cargo-registry-
# Cheap invariants that a compiler cannot see. Both failures are silent:
# the app builds, installs, launches, and then finds no trainer.
+53 -18
View File
@@ -13,6 +13,8 @@ on:
env:
RUST_BACKTRACE: 1
CARGO_TERM_COLOR: always
# Incremental state is never reused between CI runs -- pure disk cost.
CARGO_INCREMENTAL: 0
jobs:
test:
@@ -27,13 +29,24 @@ jobs:
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
# Registry only -- never `target`. That directory is ~30 GB locally
# (25G debug, 3.1G android, 2.9G release) and was cached under two
# keys, on a runner that JellyTau's builds share -- the same leak that
# filled its disk in August 2026. registry/src is left out too: cargo
# re-extracts it for free from registry/cache (155 MB of .crate
# tarballs vs 1.1 GB extracted).
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-host-${{ hashFiles('**/Cargo.lock') }}
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# One shared key across every job. The host/android split existed to
# stop the two `target` dirs clobbering each other; with target no
# longer cached, registry contents are target-independent and every
# job wants the same crates. First job to finish saves; the rest
# restore.
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-host-
${{ runner.os }}-cargo-registry-
- name: Install frontend dependencies
run: npm --prefix ui ci
@@ -60,13 +73,24 @@ jobs:
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
# Registry only -- never `target`. That directory is ~30 GB locally
# (25G debug, 3.1G android, 2.9G release) and was cached under two
# keys, on a runner that JellyTau's builds share -- the same leak that
# filled its disk in August 2026. registry/src is left out too: cargo
# re-extracts it for free from registry/cache (155 MB of .crate
# tarballs vs 1.1 GB extracted).
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-host-${{ hashFiles('**/Cargo.lock') }}
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# One shared key across every job. The host/android split existed to
# stop the two `target` dirs clobbering each other; with target no
# longer cached, registry contents are target-independent and every
# job wants the same crates. First job to finish saves; the rest
# restore.
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-host-
${{ runner.os }}-cargo-registry-
- name: Install frontend dependencies
run: npm --prefix ui ci
@@ -93,7 +117,7 @@ jobs:
with:
name: bikecontrol-linux
path: dist/linux/
retention-days: 30
retention-days: 7
build-arch:
name: Build Arch package
@@ -127,7 +151,7 @@ jobs:
with:
name: bikecontrol-arch
path: dist/arch/
retention-days: 30
retention-days: 7
build-android:
name: Build Android APK
@@ -147,13 +171,24 @@ jobs:
- name: Cache Rust dependencies
uses: actions/cache@v3
with:
# Registry only -- never `target`. That directory is ~30 GB locally
# (25G debug, 3.1G android, 2.9G release) and was cached under two
# keys, on a runner that JellyTau's builds share -- the same leak that
# filled its disk in August 2026. registry/src is left out too: cargo
# re-extracts it for free from registry/cache (155 MB of .crate
# tarballs vs 1.1 GB extracted).
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-android-${{ hashFiles('**/Cargo.lock') }}
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
# One shared key across every job. The host/android split existed to
# stop the two `target` dirs clobbering each other; with target no
# longer cached, registry contents are target-independent and every
# job wants the same crates. First job to finish saves; the rest
# restore.
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-android-
${{ runner.os }}-cargo-registry-
- name: Install frontend dependencies
run: npm --prefix ui ci
@@ -210,7 +245,7 @@ jobs:
with:
name: bikecontrol-android
path: dist/android/
retention-days: 30
retention-days: 7
create-release:
name: Create release