From 7282faecd868ba386c9b34f4d99948b41e7c7607 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 8 Jul 2026 16:58:02 -0400 Subject: [PATCH 1/5] build.sh: translate workspace bind mounts for a shared Docker daemon (opt-in) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When build.sh runs against a per-node *shared* Docker daemon (rehosting CI's shared-docker runners) rather than a per-pod dind, the daemon does not share this runner's mount namespace. Its `-v "$PWD":/app` and `-v "$CACHE_HOST_DIR":/tmp/build` bind sources live under the runner's per-pod /home/runner/_work, which the shared daemon cannot see, so /app mounts empty and the build dies with: bash: /app/_in_container_build.sh: No such file or directory (exit 127) Add rewrite_mount(): when PENGUIN_HOST_MOUNT_FROM/PENGUIN_HOST_MOUNT_TO are BOTH set (exported by the shared-daemon runner; the same mechanism penguin's wrapper uses), rewrite a bind source under _FROM to the daemon-visible _TO path. When they're unset — every local build and every GitHub-hosted runner — it returns the path unchanged, so behaviour is byte-for-byte identical to before (pure opt-in). Applies to the workspace + cache binds only; the kernel-source cache (under /home/runner/_shared, passed via --extra-docker-opts) is outside _FROM and left as-is — the shared daemon mirrors that node-shared path directly (rehosting/kube#15). --- build.sh | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index 6ae8e11..7987966 100755 --- a/build.sh +++ b/build.sh @@ -127,8 +127,38 @@ else CACHE_HOST_DIR="$CACHE_DIR" fi +# Bind-mount source translation for a shared Docker daemon (opt-in, pure +# no-op otherwise). +# +# Normally dockerd shares this script's mount namespace, so a bind-mount +# source path ("$PWD", the cache dir) means the same thing to the daemon as +# to us. Under a shared per-node daemon (e.g. rehosting CI's shared-docker +# runners) it does NOT: the daemon can't see this runner's per-pod workspace +# under /home/runner/_work, so "-v $PWD:/app" would mount an empty dir and the +# build would fail with "/app/_in_container_build.sh: No such file". The runner +# exports PENGUIN_HOST_MOUNT_FROM / PENGUIN_HOST_MOUNT_TO (the same mechanism +# penguin's wrapper uses) giving the daemon-visible location of that workspace. +# +# rewrite_mount() rewrites a bind source ONLY when BOTH env vars are set and +# the path is under _FROM. When they're unset — every local build and every +# GitHub-hosted runner — it returns the path unchanged, so behaviour is +# identical to before. (Sources outside _FROM, e.g. the kernel-source cache +# under /home/runner/_shared, are left as-is; the shared daemon mirrors that +# node-shared path directly.) +rewrite_mount() { + local path="$1" + if [[ -n "$PENGUIN_HOST_MOUNT_FROM" && -n "$PENGUIN_HOST_MOUNT_TO" \ + && "$path" == "$PENGUIN_HOST_MOUNT_FROM"* ]]; then + printf '%s' "${PENGUIN_HOST_MOUNT_TO}${path#"$PENGUIN_HOST_MOUNT_FROM"}" + else + printf '%s' "$path" + fi +} +CACHE_MOUNT_SRC="$(rewrite_mount "$CACHE_HOST_DIR")" +APP_MOUNT_SRC="$(rewrite_mount "$PWD")" + if $CLEAR_CACHE; then - docker run --rm -v "$CACHE_HOST_DIR":/tmp/build -v "$PWD":/app pandare/kernel_builder /bin/bash -c "rm -rf /tmp/build/*" + docker run --rm -v "$CACHE_MOUNT_SRC":/tmp/build -v "$APP_MOUNT_SRC":/app pandare/kernel_builder /bin/bash -c "rm -rf /tmp/build/*" exit fi @@ -141,8 +171,8 @@ fi mkdir -p "$CACHE_HOST_DIR" docker run $INTERACTIVE \ - --rm -v "$CACHE_HOST_DIR":/tmp/build \ - -v "$PWD":/app \ + --rm -v "$CACHE_MOUNT_SRC":/tmp/build \ + -v "$APP_MOUNT_SRC":/app \ -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" \ $EXTRA_DOCKER_OPTS \ "$IMAGE" \ From 732cc3d32b1a8790fe803e81ec8220a1e6440808 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 8 Jul 2026 17:09:08 -0400 Subject: [PATCH 2/5] ci: build kernel_builder FROM all-arch embedded-toolchains:latest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build image was built per-target (FROM embedded-toolchains:${TARGET}), which was a pull-cost optimization. On the shared per-node Docker daemon that backfires: 13 matrix jobs build+tag one mutable rehosting/linux_builder :latest on the shared image store, so a job can `docker run` an image built for another target and fail with its cross-compiler missing (e.g. /opt/cross/mips64el-linux-musl/bin/...-gcc: No such file). Build FROM the all-arch embedded-toolchains:latest instead. The shared daemon loads that base once per node and reuses it warm, so all-toolchains is cheap now — and every built image is target-agnostic, so the shared :latest tag can't hand a job an image lacking its toolchain. Kernel target is still selected per-job via build.sh --targets. Build cache collapses to a single shared ref (was per-target). --- .github/workflows/build.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0930a95..d89d2e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -194,13 +194,23 @@ jobs: push: false tags: | rehosting/linux_builder:latest + # TARGET=latest builds FROM the all-arch embedded-toolchains:latest + # instead of a per-target base. Per-target was a pull-cost optimization; + # on the shared per-node Docker daemon the base is loaded once per node + # and reused warm, so pulling all toolchains is cheap. It also makes the + # built image target-agnostic, so concurrent matrix jobs sharing the + # daemon's `:latest` tag can't hand a job an image missing its + # cross-compiler (every image now has every toolchain). The kernel build + # target is still selected per-job via `build.sh --targets`. build-args: | REGISTRY=${{ secrets.REHOSTING_ARC_REGISTRY }}/proxy - TARGET=${{ matrix.target_version }} + TARGET=latest + # Single shared build cache now that the image is target-agnostic + # (was per-target: ${matrix.target_version}_cache). cache-from: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:${{ matrix.target_version }}_cache,mode=max + type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max cache-to: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:${{ matrix.target_version }}_cache,mode=max + type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max outputs: type=docker - name: Build Kernel for ${{ matrix.target_version }} run: | From d5374c69120894d0bcbd3ff2cc61cb56b2588c0f Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Wed, 8 Jul 2026 18:03:41 -0400 Subject: [PATCH 3/5] ci: build the kernel_builder image once, matrix jobs pull it Previously every matrix build job ran its own docker buildx build of the kernel_builder image. Since the image went target-agnostic (TARGET=latest bundles every cross-toolchain), that rebuilt the SAME multi-GB image ~13x per run. Worse, setup-buildx-action's docker-container driver keeps its own cache store separate from the shared per-node Docker daemon, so the daemon's warm-image reuse never covered the buildx FROM base -- each job re-pulled the full all-arch base -- and 13 jobs racing cache-to on one :all_cache ref meant the registry cache never accumulated cleanly. Add a dedicated build-image job that builds the all-arch image once, pushes it to Harbor under an immutable per-commit tag, and is the sole writer of the :all_cache registry cache. The build matrix now needs [prebuild, build-image] and simply docker-pulls that tag (warm on the shared daemon after the first job lands on a node) then runs build.sh --image . Immutable tag also sidesteps the shared-daemon mutable-:latest race (cf. penguin #893). --- .github/workflows/build.yml | 130 ++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d89d2e7..a7115b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -74,8 +74,77 @@ jobs: echo "Found valid targets: $TARGETS_OUTPUT" echo "Found valid versions: $VERSIONS_OUTPUT" + # Build the kernel_builder image exactly once per run, push it to Harbor + # under an immutable per-commit tag, and let every matrix build job pull it. + # + # Why a dedicated job (instead of building inside each matrix job): + # * The image is now target-agnostic (TARGET=latest bundles every + # cross-toolchain), so all 13 build jobs would otherwise rebuild the SAME + # multi-GB image. setup-buildx-action's docker-container driver keeps its + # own cache store, separate from the shared per-node Docker daemon, so the + # daemon's warm-image reuse does NOT cover a buildx `FROM` base -- every + # job re-pulled the full all-arch base. Building once here fixes that. + # * A single writer to the `:all_cache` registry cache means the cache + # actually accumulates run-to-run instead of 13 jobs racing to overwrite + # one ref. + # * An immutable `:` tag (vs a mutable `:latest`) is safe on the shared + # daemon's single image store -- concurrent jobs/PRs can't clobber each + # other's tag (same lesson as rehosting/penguin #893). + build-image: + runs-on: rehosting-arc + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v4 + + - name: Trust Harbor's self-signed certificate + run: | + echo "Fetching certificate from ${{ secrets.REHOSTING_ARC_REGISTRY }}" + openssl s_client -showcerts -connect ${{ secrets.REHOSTING_ARC_REGISTRY }}:443 < /dev/null 2>/dev/null | openssl x509 -outform PEM | sudo tee /usr/local/share/ca-certificates/harbor.crt > /dev/null + sudo update-ca-certificates + + - name: Log in to Rehosting Arc Registry + uses: docker/login-action@v3 + with: + registry: ${{secrets.REHOSTING_ARC_REGISTRY}} + username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} + password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + # See the note in the (removed) per-job build step / penguin c35bedc5: + # don't pin moby/buildkit:master -- it regressed on the kernel-5.4 + # runners (can't mask /proc/acpi). Default pinned-stable buildkit + the + # insecure Harbor registry config. + driver-opts: | + network=host + buildkitd-config-inline: | + [registry."${{ secrets.REHOSTING_ARC_REGISTRY }}"] + insecure = true + http = true + + - name: Build and push kernel_builder image + uses: docker/build-push-action@v6 + with: + context: . + push: true + # Immutable per-commit tag -- matrix jobs pull this exact ref. + tags: | + ${{ secrets.REHOSTING_ARC_REGISTRY }}/rehosting/linux_builder:${{ github.sha }} + # TARGET=latest builds FROM the all-arch embedded-toolchains:latest so + # the single built image carries every cross-toolchain and one image + # serves every matrix target. + build-args: | + REGISTRY=${{ secrets.REHOSTING_ARC_REGISTRY }}/proxy + TARGET=latest + # Single shared build cache, written by this one job (no matrix race). + cache-from: | + type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max + cache-to: | + type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max + build: - needs: prebuild + needs: [prebuild, build-image] runs-on: rehosting-arc if: github.event.pull_request.draft == false @@ -83,6 +152,11 @@ jobs: matrix: target_version: ${{ fromJSON(needs.prebuild.outputs.targets) }} + env: + # The image built once by build-image; pulled (warm on the shared daemon + # after the first job lands on a node) instead of rebuilt per target. + KERNEL_BUILDER_IMAGE: ${{ secrets.REHOSTING_ARC_REGISTRY }}/rehosting/linux_builder:${{ github.sha }} + steps: - uses: actions/checkout@v4 with: @@ -170,48 +244,15 @@ jobs: username: ${{ secrets.REHOSTING_ARC_REGISTRY_USER }} password: ${{ secrets.REHOSTING_ARC_REGISTRY_PASSWORD }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - # Do NOT pin image=moby/buildkit:master here. A recent master - # regressed on the self-hosted runners (kernel 5.4): - # runc run failed: ... can't mask dir "/proc/acpi": mount ... - # MS_RDONLY ... invalid argument - # which fails the first RUN that needs container init. Letting buildx - # use its default pinned-stable buildkit avoids it. network=host and - # the registry config are kept. (Mirrors rehosting/penguin c35bedc5.) - driver-opts: | - network=host - buildkitd-config-inline: | - [registry."${{ secrets.REHOSTING_ARC_REGISTRY }}"] - insecure = true - http = true + - name: Pull kernel_builder image + run: | + set -eux + # Built once by the build-image job. On the shared per-node Docker + # daemon the first matrix job to land on a node pulls the layers; every + # later job on that node finds them already present (warm reuse). No + # per-job buildx build anymore. + docker pull "$KERNEL_BUILDER_IMAGE" - - name: Build kernel_builder docker image - uses: docker/build-push-action@v6 - with: - context: . - push: false - tags: | - rehosting/linux_builder:latest - # TARGET=latest builds FROM the all-arch embedded-toolchains:latest - # instead of a per-target base. Per-target was a pull-cost optimization; - # on the shared per-node Docker daemon the base is loaded once per node - # and reused warm, so pulling all toolchains is cheap. It also makes the - # built image target-agnostic, so concurrent matrix jobs sharing the - # daemon's `:latest` tag can't hand a job an image missing its - # cross-compiler (every image now has every toolchain). The kernel build - # target is still selected per-job via `build.sh --targets`. - build-args: | - REGISTRY=${{ secrets.REHOSTING_ARC_REGISTRY }}/proxy - TARGET=latest - # Single shared build cache now that the image is target-agnostic - # (was per-target: ${matrix.target_version}_cache). - cache-from: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max - cache-to: | - type=registry,ref=${{secrets.REHOSTING_ARC_REGISTRY}}/rehosting/linux_builder:all_cache,mode=max - outputs: type=docker - name: Build Kernel for ${{ matrix.target_version }} run: | set -eux @@ -228,8 +269,9 @@ jobs: VERSIONS=$(echo "$VERSIONS_JSON" | jq -r '.[]' | xargs) fi - # Mount the stable source directory instead of the run-specific one - ./build.sh --targets "$TARGET" ${VERSIONS:+--versions "$VERSIONS"} --extra-docker-opts "-v $SOURCES_DIR:/app/linux" + # Use the prebuilt image (--image) and mount the stable source + # directory instead of the run-specific one. + ./build.sh --image "$KERNEL_BUILDER_IMAGE" --targets "$TARGET" ${VERSIONS:+--versions "$VERSIONS"} --extra-docker-opts "-v $SOURCES_DIR:/app/linux" # Stage per-target outputs in the workspace; they are handed to the # aggregate job via workflow artifacts (below) instead of a shared From 3b8eb624a1fdfc1ba80b16ed18f15e8773864809 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 9 Jul 2026 19:06:17 -0400 Subject: [PATCH 4/5] ci: cache kernel compiles with a node-shared ccache The matrix build jobs recompiled every kernel from scratch each run (~15-20 min/target): builds are out-of-tree into /tmp/build, which build.sh backs with $PWD/cache -- the per-job workspace, wiped every run -- so the incremental tree never survived. The base image already ships ccache. Enable ccache on a persistent, node-shared dir: - _in_container_build.sh: when CCACHE_DIR is set, route the kernel/modules/perf compiles through CC="ccache gcc". When unset, CC_PREFIX is empty and each CC= expands to exactly the kernel default ($(CROSS_COMPILE)gcc), so the behaviour is byte-for-byte unchanged for local/other callers. Sets sane ccache knobs (15G, compress, time_macros sloppiness) and prints start/end stats for observability. - build.yml: mount /home/runner/_shared/linux_builder_ccache (same node-shared mount the kernel sources use, mirrored into the shared daemon by kube#15) at /ccache and set CCACHE_DIR. One dir shared by every target/version on the node; ccache is content-keyed + internally locked, so cross-target/cross-job sharing is safe and correct across config and source bumps. First run warms the cache (cold, same speed as today); subsequent runs on a node should drop to a mostly-cache-hit rebuild. --- .github/workflows/build.yml | 16 ++++++++++++++-- _in_container_build.sh | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a7115b3..bfa55af 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -269,9 +269,21 @@ jobs: VERSIONS=$(echo "$VERSIONS_JSON" | jq -r '.[]' | xargs) fi + # Node-shared, persistent compiler cache. Lives under the same + # /home/runner/_shared mount the kernel sources use (mirrored into the + # shared Docker daemon by kube#15), so mounting it into the build + # container needs no extra plumbing. One dir shared by every + # target/version on the node -- ccache is content-keyed and dedups, so + # a warm cache turns the ~15-20 min from-scratch compile into a mostly- + # cache-hit rebuild. _in_container_build.sh enables ccache iff CCACHE_DIR + # is set (no-op otherwise). + CCACHE_HOST_DIR="/home/runner/_shared/linux_builder_ccache" + mkdir -p "$CCACHE_HOST_DIR" + # Use the prebuilt image (--image) and mount the stable source - # directory instead of the run-specific one. - ./build.sh --image "$KERNEL_BUILDER_IMAGE" --targets "$TARGET" ${VERSIONS:+--versions "$VERSIONS"} --extra-docker-opts "-v $SOURCES_DIR:/app/linux" + # directory instead of the run-specific one, plus the shared ccache. + ./build.sh --image "$KERNEL_BUILDER_IMAGE" --targets "$TARGET" ${VERSIONS:+--versions "$VERSIONS"} \ + --extra-docker-opts "-v $SOURCES_DIR:/app/linux -v $CCACHE_HOST_DIR:/ccache -e CCACHE_DIR=/ccache -e CCACHE_MAXSIZE=15G" # Stage per-target outputs in the workspace; they are handed to the # aggregate job via workflow artifacts (below) instead of a shared diff --git a/_in_container_build.sh b/_in_container_build.sh index 426f29e..4735c70 100755 --- a/_in_container_build.sh +++ b/_in_container_build.sh @@ -36,6 +36,29 @@ echo "No strip: $NO_STRIP" echo "menuconfig: $MENU_CONFIG" echo "diffdefconfig: $DIFFDEFCONFIG" +# Optional compiler cache. Enabled purely by the presence of CCACHE_DIR (set by +# the caller, e.g. CI mounts a node-shared persistent dir). When unset, CC_PREFIX +# stays empty and every `CC=` below expands to exactly the kernel default +# ($(CROSS_COMPILE)gcc), so behaviour is identical to before. ccache is content- +# keyed, so it survives `mrproper`/clean and stays correct across config and +# source bumps; concurrent build jobs sharing one CCACHE_DIR are safe (ccache +# locks internally). The container paths ($O=/tmp/build/..., src /app/linux) are +# stable across runs, which is what lets objects hit. +CC_PREFIX="" +if [ -n "${CCACHE_DIR:-}" ]; then + mkdir -p "$CCACHE_DIR" + export CCACHE_DIR + export CCACHE_MAXSIZE="${CCACHE_MAXSIZE:-15G}" + export CCACHE_COMPRESS=1 + # __DATE__/__TIME__ and header mtimes churn across a fresh checkout; treat + # them as sloppy so unchanged translation units still hit. + export CCACHE_SLOPPINESS="${CCACHE_SLOPPINESS:-time_macros,include_file_mtime,include_file_ctime,file_stat_matches}" + ccache -M "$CCACHE_MAXSIZE" >/dev/null 2>&1 || true + CC_PREFIX="ccache " + echo "ccache enabled: dir=$CCACHE_DIR maxsize=$CCACHE_MAXSIZE" + ccache -s 2>/dev/null | sed 's/^/ ccache(start): /' || true +fi + # Array to keep track of child processes declare -a pids @@ -159,13 +182,13 @@ for TARGET in $TARGETS; do /app/linux/${VERSION}/scripts/diffconfig /tmp/original_config /tmp/build/${VERSION}/${TARGET}/.config exit fi - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ $BUILD_TARGETS -j$(nproc) + make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ $BUILD_TARGETS -j$(nproc) # Always run modules_prepare to ensure headers and Module.symvers are generated - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ modules_prepare + make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ modules_prepare # Build modules to ensure Module.symvers is generated - make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) O=/tmp/build/${VERSION}/${TARGET}/ modules -j$(nproc) + make -C /app/linux/$VERSION ARCH=${short_arch} CROSS_COMPILE=$(get_cc $TARGET $VERSION) CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" O=/tmp/build/${VERSION}/${TARGET}/ modules -j$(nproc) # Prepare and completely clean the output directory for perf PERF_OUTDIR="/tmp/build/${VERSION}/${TARGET}/tools/perf/" @@ -184,6 +207,7 @@ for TARGET in $TARGETS; do make -C /app/linux/$VERSION/tools/perf \ ARCH=${short_arch} \ CROSS_COMPILE=$(get_cc $TARGET $VERSION) \ + CC="${CC_PREFIX}$(get_cc $TARGET $VERSION)gcc" \ LD="$PERF_LD" \ OUTPUT="$PERF_OUTDIR" \ LDFLAGS="-static" \ @@ -371,5 +395,10 @@ if [ "$KERNEL_DEVEL" = "true" ]; then exit 0 fi +if [ -n "${CCACHE_DIR:-}" ]; then + echo "ccache stats after build:" + ccache -s 2>/dev/null | sed 's/^/ ccache(end): /' || true +fi + # Ensure cache can be read/written by host chmod -R o+rw /tmp/build From 2c5e0dc801a1170de761307a4f1b05d43896d9c6 Mon Sep 17 00:00:00 2001 From: Luke Craig Date: Thu, 9 Jul 2026 19:33:44 -0400 Subject: [PATCH 5/5] ci: print ccache stats from the EXIT trap The end-of-run ccache summary sat after the kernel-devel `exit 0`, which is the default CI path, so it never printed. Move it into the EXIT trap (renamed fix_ownership -> on_exit) so it fires on every exit path. Observability only; ccache itself was already warming during the compiles. --- _in_container_build.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/_in_container_build.sh b/_in_container_build.sh index 4735c70..759c0ad 100755 --- a/_in_container_build.sh +++ b/_in_container_build.sh @@ -6,7 +6,14 @@ set -eu # root-owned on the host. Runs as root inside the container via an EXIT trap so # it covers every exit path (including the early kernel-devel exit). Gated on a # non-root HOST_UID, so CI (which may run as root / not set these) is unaffected. -fix_ownership() { +on_exit() { + # ccache summary on every exit path (there are several early exits: + # config-only, menuconfig, diffdefconfig, kernel-devel). No-op unless ccache + # is enabled. + if [ -n "${CCACHE_DIR:-}" ]; then + echo "ccache stats after build:" + ccache -s 2>/dev/null | sed 's/^/ ccache(end): /' || true + fi if [ -n "${HOST_UID:-}" ] && [ "${HOST_UID}" != "0" ]; then chown -R "${HOST_UID}:${HOST_GID:-$HOST_UID}" \ /tmp/build \ @@ -14,7 +21,7 @@ fix_ownership() { /app/kernel-devel-all.tar.gz 2>/dev/null || true fi } -trap fix_ownership EXIT +trap on_exit EXIT # We want to build linux for each of our targets and versions using the config files. Linux is in /app/linux/[version] # while our configs are at configs/[version]/[arch]. We need to set the ARCH and CROSS_COMPILE variables @@ -395,10 +402,5 @@ if [ "$KERNEL_DEVEL" = "true" ]; then exit 0 fi -if [ -n "${CCACHE_DIR:-}" ]; then - echo "ccache stats after build:" - ccache -s 2>/dev/null | sed 's/^/ ccache(end): /' || true -fi - # Ensure cache can be read/written by host chmod -R o+rw /tmp/build