From ee47c2dfcd7dab5d4d8f937a0395bdaf463d267b Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:00:31 +0000 Subject: [PATCH 1/6] chore(ci): measure remote cache hits --- .github/mise-cache-qualification.toml | 2 +- .github/workflows/ci.yml | 41 +++++++++++---------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/.github/mise-cache-qualification.toml b/.github/mise-cache-qualification.toml index 3d6a6eceb..f9606f76b 100644 --- a/.github/mise-cache-qualification.toml +++ b/.github/mise-cache-qualification.toml @@ -4,4 +4,4 @@ experimental = true [tasks."cache:qualify"] dir = "{{ env.GITHUB_WORKSPACE }}" run = "cargo build --workspace" -rust_cache = { verify = true } +rust_cache = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4d1f8e5d..acba70578 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: retention-days: 1 if-no-files-found: error - # Qualify mise's experimental Rust action cache against the production + # Exercise mise's experimental Rust action cache against the production # service without replacing Namespace's cache yet. Pull requests can read # jdx/aube but must not write. Pull requests start with empty local state and # must restore entries previously seeded by main; main can seed a cold cache. @@ -138,42 +138,35 @@ jobs: exit 1 fi fi - - name: Qualify Rust action cache + - name: Exercise Rust action cache run: | set -o pipefail log="$RUNNER_TEMP/mise-cache-qualification.log" mise run cache:qualify 2>&1 | tee "$log" - if [ "$GITHUB_EVENT_NAME" = pull_request ]; then - remote_summary=$(grep -Eo \ - 'Action cache: [0-9]+ hits, [0-9]+ misses, [0-9]+ prefetched; [^,]+ downloaded,' \ - "$log" | tail -n 1 || true) - if [[ ! "$remote_summary" =~ ^Action\ cache:\ [0-9]+\ hits,\ [0-9]+\ misses,\ ([0-9]+)\ prefetched\;\ (.+)\ downloaded,$ ]]; then - echo "::error::mise did not report Rust remote action-cache results" - exit 1 - fi - prefetched=${BASH_REMATCH[1]} - downloaded=${BASH_REMATCH[2]} - if (( prefetched == 0 )) || [[ "$downloaded" == "0 B" ]]; then - echo "::error::Rust remote action cache reported $prefetched prefetched actions and $downloaded downloaded" - exit 1 - fi - else + if [ "$GITHUB_EVENT_NAME" != pull_request ]; then cargo clean --target-dir "$CARGO_TARGET_DIR" mise run cache:qualify 2>&1 | tee -a "$log" fi summary=$(grep -Eo \ - 'Action cache qualification: [0-9]+ verified, [0-9]+ diverged' \ + 'Action cache: [0-9]+ hits, [0-9]+ misses, [0-9]+ prefetched; [^,]+ downloaded,' \ "$log" | tail -n 1 || true) - if [[ ! "$summary" =~ ^Action\ cache\ qualification:\ ([0-9]+)\ verified,\ ([0-9]+)\ diverged$ ]]; then - echo "::error::mise did not report Rust action-cache qualification results" + if [[ ! "$summary" =~ ^Action\ cache:\ ([0-9]+)\ hits,\ [0-9]+\ misses,\ ([0-9]+)\ prefetched\;\ (.+)\ downloaded,$ ]]; then + echo "::error::mise did not report Rust action-cache results" exit 1 fi - verified=${BASH_REMATCH[1]} - diverged=${BASH_REMATCH[2]} - if (( verified == 0 || diverged != 0 )); then - echo "::error::Rust action-cache qualification reported $verified verified and $diverged diverged actions" + hits=${BASH_REMATCH[1]} + prefetched=${BASH_REMATCH[2]} + downloaded=${BASH_REMATCH[3]} + if (( hits == 0 )); then + echo "::error::Rust action cache reported no hits" exit 1 fi + if [ "$GITHUB_EVENT_NAME" = pull_request ]; then + if (( prefetched == 0 )) || [[ "$downloaded" == "0 B" ]]; then + echo "::error::Rust remote action cache reported $prefetched prefetched actions and $downloaded downloaded" + exit 1 + fi + fi if grep -Eiq 'remote cache .* failed|mise rustc cache warning' \ "$log"; then echo "::error::mise reported a Rust action-cache warning" From 08b19ebd66139311fbaabbf2d2bfa856c368685a Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:33:24 +0000 Subject: [PATCH 2/6] ci: compare GitHub Actions Rust cache --- .github/workflows/ci.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acba70578..0d60f3b6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,6 +173,32 @@ jobs: exit 1 fi + # Hosted-runner control for the common archive-cache setup. The first PR run + # seeds an isolated pull-request cache; rerunning the job measures the warm + # restore without relying on Namespace's attached NVMe cache volume. + github-cache: + name: GitHub Actions Rust cache + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + env: + RUSTFLAGS: "-D warnings" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: jdx/mise-action@7e36c90d9ab29c415a2384db3006f3ec8a8cc654 # v4.2.4 + - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] PR entries are scoped to the PR and consumed only by this unprivileged job + with: + path: | + ~/.cargo/registry/index + ~/.cargo/registry/cache + ~/.cargo/git/db + target + key: aube-rust-${{ runner.os }}-v1-${{ hashFiles('Cargo.lock') }} + - run: mise run build + # Linux-only test + lint + render + docs:build. Matches the # `linux / test` step in .buildkite/pipeline.sh. Split from `build` so # lint failures don't block the BATS jobs from starting against a @@ -435,6 +461,7 @@ jobs: needs: - build - cache-qualification + - github-cache - test-linux - bats - bats-serial From 521f13a7ec790b380f3fa58443134e2b7bc9a03c Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:39:29 +0000 Subject: [PATCH 3/6] ci: report GitHub cache qualification --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d60f3b6e..a702a6764 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,7 +189,8 @@ jobs: with: persist-credentials: false - uses: jdx/mise-action@7e36c90d9ab29c415a2384db3006f3ec8a8cc654 # v4.2.4 - - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] PR entries are scoped to the PR and consumed only by this unprivileged job + - id: rust-cache + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] PR entries are scoped to the PR and consumed only by this unprivileged job with: path: | ~/.cargo/registry/index @@ -197,7 +198,15 @@ jobs: ~/.cargo/git/db target key: aube-rust-${{ runner.os }}-v1-${{ hashFiles('Cargo.lock') }} - - run: mise run build + - name: Build with GitHub Actions cache + env: + CACHE_HIT: ${{ steps.rust-cache.outputs.cache-hit }} + run: | + start=$SECONDS + mise run build + elapsed=$((SECONDS - start)) + printf '### GitHub Actions Rust cache\n\n- Exact cache hit: %s\n- Build: %ss\n' \ + "$CACHE_HIT" "$elapsed" >> "$GITHUB_STEP_SUMMARY" # Linux-only test + lint + render + docs:build. Matches the # `linux / test` step in .buildkite/pipeline.sh. Split from `build` so From fc2dd43fca4533ff4b8aa83053bb3e9f94e7e0f0 Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Mon, 17 Aug 2026 18:11:41 +0000 Subject: [PATCH 4/6] ci: publish mise cache phase metrics --- .github/workflows/ci.yml | 80 +++++++++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a702a6764..0fad1f052 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -142,28 +142,44 @@ jobs: run: | set -o pipefail log="$RUNNER_TEMP/mise-cache-qualification.log" - mise run cache:qualify 2>&1 | tee "$log" + first_report="$RUNNER_TEMP/mise-cache-stats-remote.json" + reports=("remote:$first_report") + MISE_TASK_CACHE_STATS_REPORT="$first_report" \ + mise run cache:qualify 2>&1 | tee "$log" + qualification_report=$first_report if [ "$GITHUB_EVENT_NAME" != pull_request ]; then cargo clean --target-dir "$CARGO_TARGET_DIR" - mise run cache:qualify 2>&1 | tee -a "$log" + local_report="$RUNNER_TEMP/mise-cache-stats-local.json" + reports+=("local-after-clean:$local_report") + MISE_TASK_CACHE_STATS_REPORT="$local_report" \ + mise run cache:qualify 2>&1 | tee -a "$log" + qualification_report=$local_report fi - summary=$(grep -Eo \ - 'Action cache: [0-9]+ hits, [0-9]+ misses, [0-9]+ prefetched; [^,]+ downloaded,' \ - "$log" | tail -n 1 || true) - if [[ ! "$summary" =~ ^Action\ cache:\ ([0-9]+)\ hits,\ [0-9]+\ misses,\ ([0-9]+)\ prefetched\;\ (.+)\ downloaded,$ ]]; then - echo "::error::mise did not report Rust action-cache results" - exit 1 - fi - hits=${BASH_REMATCH[1]} - prefetched=${BASH_REMATCH[2]} - downloaded=${BASH_REMATCH[3]} + + for entry in "${reports[@]}"; do + report=${entry#*:} + if ! jq -e ' + .version == 1 and + (.session_duration_ns | numbers) and + (.hits | numbers) and + (.remote_blob_requests | numbers) and + (.materialization_duration_ns | numbers) + ' "$report" >/dev/null; then + echo "::error::mise did not write a valid Rust action-cache report to $report" + exit 1 + fi + done + + hits=$(jq -r '.hits' "$qualification_report") if (( hits == 0 )); then echo "::error::Rust action cache reported no hits" exit 1 fi if [ "$GITHUB_EVENT_NAME" = pull_request ]; then - if (( prefetched == 0 )) || [[ "$downloaded" == "0 B" ]]; then - echo "::error::Rust remote action cache reported $prefetched prefetched actions and $downloaded downloaded" + prefetched=$(jq -r '.prefetched_actions' "$qualification_report") + downloaded=$(jq -r '.downloaded_bytes' "$qualification_report") + if (( prefetched == 0 || downloaded == 0 )); then + echo "::error::Rust remote action cache reported $prefetched prefetched actions and $downloaded downloaded bytes" exit 1 fi fi @@ -173,6 +189,42 @@ jobs: exit 1 fi + { + echo '### mise Rust action cache' + echo + echo '| Pass | Hits | Misses | Avoided compiles | Prefetched | Blob requests | Downloaded | Restored |' + echo '| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |' + for entry in "${reports[@]}"; do + label=${entry%%:*} + report=${entry#*:} + jq -r --arg label "$label" ' + def mib($value): + (((($value / 1048576) * 100) | round) / 100 | tostring) + " MiB"; + "| \($label) | \(.hits) | \(.misses) | \(.compiler_invocations_avoided) | \(.prefetched_actions) | \(.remote_blob_requests) | \(mib(.downloaded_bytes)) | \(mib(.restored_output_bytes)) |" + ' "$report" + done + echo + echo '| Pass | Session | Prefetch | Manifest lookup | Action lookup | Blob transfer | CAS write | Materialization |' + echo '| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |' + for entry in "${reports[@]}"; do + label=${entry%%:*} + report=${entry#*:} + jq -r --arg label "$label" ' + def seconds($value): + (((($value / 1000000000) * 100) | round) / 100 | tostring) + "s"; + "| \($label) | \(seconds(.session_duration_ns)) | \(seconds(.prefetch_duration_ns)) | \(seconds(.remote_manifest_lookup_duration_ns)) (\(.remote_manifest_lookups)) | \(seconds(.remote_action_lookup_duration_ns)) (\(.remote_action_lookups)) | \(seconds(.remote_blob_transfer_duration_ns)) | \(seconds(.local_cas_write_duration_ns)) | \(seconds(.materialization_duration_ns)) |" + ' "$report" + done + } >> "$GITHUB_STEP_SUMMARY" + - name: Upload Rust action cache reports + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: mise-cache-stats-${{ github.run_id }}-${{ github.run_attempt }} + path: ${{ runner.temp }}/mise-cache-stats-*.json + retention-days: 30 + if-no-files-found: warn + # Hosted-runner control for the common archive-cache setup. The first PR run # seeds an isolated pull-request cache; rerunning the job measures the warm # restore without relying on Namespace's attached NVMe cache volume. From 44e96530cae6ab242fdd0421fd6e0fbacab1c61c Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:22:30 +0000 Subject: [PATCH 5/6] ci: qualify cache with mise 2026.8.9 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fad1f052..712208129 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: persist-credentials: false - uses: jdx/mise-action@7e36c90d9ab29c415a2384db3006f3ec8a8cc654 # v4.2.4 with: - version: 2026.8.8 + version: 2026.8.9 install: false cache: false - name: Qualify production cache authorization From b9f15c708f4e1e8846becd32cb17c00c683ae694 Mon Sep 17 00:00:00 2001 From: default <216188+jdx@users.noreply.github.com> Date: Wed, 19 Aug 2026 21:36:10 +0000 Subject: [PATCH 6/6] ci: isolate GitHub cache control keys --- .github/workflows/ci.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 712208129..abc34d032 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -241,7 +241,13 @@ jobs: with: persist-credentials: false - uses: jdx/mise-action@7e36c90d9ab29c415a2384db3006f3ec8a8cc654 # v4.2.4 - - id: rust-cache + with: + cache: false + - name: Capture Rust cache key inputs + run: | + mkdir -p .github/cache-keys + rustc -Vv > .github/cache-keys/rustc-version + - id: cargo-cache uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 # zizmor: ignore[cache-poisoning] PR entries are scoped to the PR and consumed only by this unprivileged job with: path: | @@ -249,10 +255,10 @@ jobs: ~/.cargo/registry/cache ~/.cargo/git/db target - key: aube-rust-${{ runner.os }}-v1-${{ hashFiles('Cargo.lock') }} + key: aube-rust-${{ runner.os }}-${{ github.event.pull_request.number || github.ref_name }}-v1-${{ hashFiles('Cargo.lock', 'Cargo.toml', '.github/cache-keys/rustc-version') }} - name: Build with GitHub Actions cache env: - CACHE_HIT: ${{ steps.rust-cache.outputs.cache-hit }} + CACHE_HIT: ${{ steps.cargo-cache.outputs.cache-hit }} run: | start=$SECONDS mise run build