perf(ci): verified compiler cache for test and smoke legs, local and CI

ccache with CCACHE_COMPILERCHECK=content everywhere: every cache entry
is keyed on the CONTENT of the compiler binary plus the fully
preprocessed translation unit, so a hit is provably the identical
compilation - a stale, foreign, or corrupted cache can only MISS, never
return wrong output. No CCACHE_BASEDIR and no path rewriting: debug
info and sanitizer report paths stay exact.

Locally scripts/env.sh routes compilers through ccache's masquerade
directories when present (opt-out CBM_NO_CCACHE=1) - $CC keeps its
plain name, so verify_compiler, make, and link lines are untouched.

CI caches are strictly per-ref by policy on top of GitHub's own branch
scoping: keys embed github.ref, so no base-branch fallback - a new PR
builds cold once and only its own pushes warm it. Cached: the four
test jobs and the pr-smoke matrix. Release builds in _build.yml stay
deliberately uncached.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-07-18 20:51:56 +02:00
parent 7d6cdb23ef
commit 129d8f7524
3 changed files with 135 additions and 4 deletions
+83 -3
View File
@@ -81,12 +81,39 @@ jobs:
- name: Install deps (Ubuntu)
if: startsWith(matrix.os, 'ubuntu')
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
- name: Install ccache (macOS)
if: startsWith(matrix.os, 'macos')
run: command -v ccache >/dev/null 2>&1 || brew install ccache
# Verified compiler cache: CCACHE_COMPILERCHECK=content keys every entry
# on the compiler-binary CONTENT plus the fully preprocessed input, so a
# hit is provably the identical compilation — a stale or foreign cache
# can only miss, never return wrong output (see scripts/env.sh).
# Keys embed github.ref on top of GitHub's own branch scoping: caches are
# STRICTLY per-ref by policy — no base-branch fallback, a new PR builds
# cold once and only its own pushes warm it.
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-test-${{ matrix.os }}-${{ matrix.cc }}-${{ github.ref }}-
- name: Test
run: scripts/test.sh CC=${{ matrix.cc }} CXX=${{ matrix.cxx }}
env:
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
- name: ccache stats
if: always()
run: ccache -s || true
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
test-tsan:
runs-on: ubuntu-latest
@@ -95,10 +122,33 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- name: Install deps (Ubuntu)
run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev
run: sudo apt-get update && sudo apt-get install -y clang zlib1g-dev ccache
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-tsan-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-tsan-${{ github.ref }}-
- name: ThreadSanitizer tests
run: make -f Makefile.cbm test-tsan CC=clang CXX=clang++
# This job calls make directly (no env.sh), so route the compilers
# through ccache's Debian masquerade dir; update-ccache-symlinks in the
# ccache postinst has already linked the clang installed above.
run: |
export PATH=/usr/lib/ccache:$PATH
export CCACHE_COMPILERCHECK=content
make -f Makefile.cbm test-tsan CC=clang CXX=clang++
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
- name: ccache stats
if: always()
run: ccache -s || true
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
test-windows:
needs: setup-matrix
@@ -119,9 +169,18 @@ jobs:
mingw-w64-clang-${{ matrix.pkg }}-clang
mingw-w64-clang-${{ matrix.pkg }}-compiler-rt
mingw-w64-clang-${{ matrix.pkg }}-zlib
mingw-w64-clang-${{ matrix.pkg }}-ccache
make
git
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-test-${{ matrix.os }}-${{ matrix.msystem }}-${{ github.ref }}-
- name: Test
shell: msys2 {0}
# AddressSanitizer is unavailable on native ARM64 Windows (LLVM ships no
@@ -133,6 +192,15 @@ jobs:
run: scripts/test.sh CC=clang CXX=clang++ ${{ matrix.os == 'windows-11-arm' && 'SANITIZE=' || '' }}
env:
CBM_SKIP_PERF: ${{ inputs.skip_perf && '1' || '' }}
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
- name: ccache stats
if: always()
shell: msys2 {0}
run: ccache -s || true
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
# Windows product-surface regression guards. Distinct from test-windows above
# (the sanitizer C suite): these drive a real product binary + embedded HTTP UI
@@ -155,6 +223,7 @@ jobs:
install: >-
mingw-w64-clang-x86_64-clang
mingw-w64-clang-x86_64-zlib
mingw-w64-clang-x86_64-ccache
make
git
@@ -162,11 +231,22 @@ jobs:
with:
node-version: "22"
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-guards-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-guards-${{ github.ref }}-
- name: Build product binary with embedded UI
shell: msys2 {0}
# --with-ui builds the frontend (npm) and embeds it, so the drive-picker
# guard's HTTP UI is available. Functional gate only (no sanitizers).
run: scripts/build.sh --with-ui CC=clang CXX=clang++
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1500M
- name: Windows regression guards (#636/#357, #618, #548, #423/#20)
shell: pwsh
+25 -1
View File
@@ -82,7 +82,11 @@ jobs:
- name: Install deps (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev
run: sudo apt-get update && sudo apt-get install -y zlib1g-dev ccache
- name: Install ccache (macOS)
if: matrix.os == 'macos-14'
run: command -v ccache >/dev/null 2>&1 || brew install ccache
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
if: matrix.os == 'windows-latest'
@@ -93,14 +97,28 @@ jobs:
mingw-w64-clang-x86_64-clang
mingw-w64-clang-x86_64-zlib
mingw-w64-clang-x86_64-python3
mingw-w64-clang-x86_64-ccache
make
coreutils
# Verified compiler cache — content-keyed, stale hits impossible by
# construction (see scripts/env.sh).
- name: Compiler cache (content-verified)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ${{ github.workspace }}/.ccache
key: ccache-smoke-${{ matrix.os }}-${{ github.ref }}-${{ github.sha }}
restore-keys: |
ccache-smoke-${{ matrix.os }}-${{ github.ref }}-
- name: Build prod + smoke (Ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
scripts/build.sh CC=gcc CXX=g++
scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
- name: Build prod + smoke (macOS)
if: matrix.os == 'macos-14'
@@ -108,6 +126,9 @@ jobs:
scripts/build.sh CC=cc CXX=c++
codesign --sign - --force build/c/codebase-memory-mcp
scripts/smoke-test.sh "$(pwd)/build/c/codebase-memory-mcp"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
- name: Build prod + smoke (Windows)
if: matrix.os == 'windows-latest'
@@ -117,6 +138,9 @@ jobs:
BIN="$(pwd)/build/c/codebase-memory-mcp"
[ -f "${BIN}.exe" ] && BIN="${BIN}.exe"
scripts/smoke-test.sh "$BIN"
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1000M
ci-ok:
# The one required context (besides dco) — fails unless every PR stage
+27
View File
@@ -98,6 +98,33 @@ if [[ -z "${CC:-}" ]]; then
fi
fi
# ── Verified compiler cache (ccache, opt-out CBM_NO_CCACHE=1) ──
# Activated through ccache's masquerade directories so $CC keeps its plain
# name everywhere (verify_compiler, make, link lines are untouched).
# Zero-staleness guarantee: CCACHE_COMPILERCHECK=content keys every entry on
# the CONTENT of the compiler binary plus the fully preprocessed translation
# unit, so a cache hit is provably the identical compilation — a stale or
# foreign cache can only MISS, never return wrong output. No CCACHE_BASEDIR
# and no path rewriting: debug-info and sanitizer report paths stay exact.
if [[ "${CBM_NO_CCACHE:-0}" != "1" ]] && command -v ccache >/dev/null 2>&1; then
for _cbm_ccache_masq in \
/usr/lib/ccache \
/opt/homebrew/opt/ccache/libexec \
/usr/local/opt/ccache/libexec \
/clang64/lib/ccache/bin \
/clangarm64/lib/ccache/bin; do
if [[ -d "$_cbm_ccache_masq" ]]; then
case ":$PATH:" in
*":$_cbm_ccache_masq:"*) ;;
*) PATH="$_cbm_ccache_masq:$PATH" ;;
esac
fi
done
unset _cbm_ccache_masq
export PATH
export CCACHE_COMPILERCHECK=content
fi
# ── Print environment summary ──────────────────────────────────
print_env() {
local context="$1"