From 4cafa75d70b7b83cc5488e5aaec8f790d964ab95 Mon Sep 17 00:00:00 2001 From: Asen Lekov Date: Mon, 19 Oct 2026 23:39:37 +0300 Subject: [PATCH] fix(pre-job): exit 0 under the runner's bash -e when the bun cache has no @scope folder The runner runs the JOB_STARTED hook under bash -e. With no @scope folder in the bun cache, the unmatched glob reached find as a literal path, find exited 1, and every job on the runner failed before its first step. Glob with nullglob, end each sweep command in || true and the script in exit 0, and test the hook under bash -e. Co-Authored-By: Claude Opus 5.5 --- pre-job.sh | 21 +++++++++++++++------ tests/pre-job.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100755 tests/pre-job.sh diff --git a/pre-job.sh b/pre-job.sh index a94b941..82178bb 100755 --- a/pre-job.sh +++ b/pre-job.sh @@ -11,8 +11,10 @@ # cache trees: the slot caches are the warm cache. Without a slot (the image # run outside compose) there is no marker, so it always sweeps. # -# Intentionally not using set -e: individual prune failures should not -# prevent the job from starting. +# The runner runs this hook under `bash -e`, whatever the shebang says, and a +# non-zero exit fails the job before its first step. So every sweep command +# ends in `|| true` and the script ends in `exit 0`: a failed prune must never +# stop the job from starting. # # NOTE: this is only half the fix. Workflows that restore ~/.bun/install/cache # via actions/cache with restore-keys can re-import a poisoned entry from the @@ -42,13 +44,20 @@ echo "[pre-job] Previous job did not finish; sweeping incomplete cache entries.. # for ..." and it looks like a dependency problem rather than a runner # problem. Only those two levels: the cache holds hundreds of thousands of # files, and a file inside a package that happens to end in .tmp is content. +# nullglob: with no @scope folder the pattern would reach find as a literal +# path, and find exits 1 on it. bun_cache="$HOME/.bun/install/cache" -find "$bun_cache" "$bun_cache"/@*/ -mindepth 1 -maxdepth 1 \( -name '*.tmp' -o -name '*.part' \) \ - -exec rm -rf {} + 2>/dev/null +if [ -d "$bun_cache" ]; then + shopt -s nullglob + find "$bun_cache" "$bun_cache"/@*/ -mindepth 1 -maxdepth 1 \( -name '*.tmp' -o -name '*.part' \) \ + -exec rm -rf {} + 2>/dev/null || true + shopt -u nullglob +fi # Gradle: leftover lock / journal / daemon state from a killed compile. # Keep modules-2, build-cache-*, and transforms-*. -rm -f "$HOME"/.gradle/caches/modules-2/modules-2.lock 2>/dev/null -rm -rf "$HOME"/.gradle/caches/journal-* "$HOME"/.gradle/daemon/ 2>/dev/null +rm -f "$HOME"/.gradle/caches/modules-2/modules-2.lock 2>/dev/null || true +rm -rf "$HOME"/.gradle/caches/journal-* "$HOME"/.gradle/daemon/ 2>/dev/null || true echo "[pre-job] Sweep complete." +exit 0 diff --git a/tests/pre-job.sh b/tests/pre-job.sh new file mode 100755 index 0000000..f31d07b --- /dev/null +++ b/tests/pre-job.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# The JOB_STARTED hook runs under `bash -e`, as the runner invokes it, and must +# exit 0 whatever the caches look like: no bun cache, a bun cache with no +# @scope folder, and one with leftovers to sweep. A non-zero exit fails the job +# before its first step. +# bash tests/pre-job.sh +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=tests/harness.sh +source "$here/harness.sh" + +hook() { env -u RUNNER_SLOT_DIR HOME="$1" bash -e "$root/pre-job.sh" >/dev/null 2>&1; } + +home="$work/empty" +mkdir -p "$home" +hook "$home" || fail "exit $? with no bun cache" + +home="$work/noscope" +mkdir -p "$home/.bun/install/cache/pkg" +hook "$home" || fail "exit $? with a bun cache and no @scope folder" + +home="$work/leftovers" +cache="$home/.bun/install/cache" +mkdir -p "$cache/pkg" "$cache/@scope/pkg" "$cache/.tmp" +touch "$cache/x.tmp" "$cache/y.part" "$cache/@scope/z.tmp" "$cache/pkg/keep.tmp" +hook "$home" || fail "exit $? with leftovers to sweep" +gone "$cache/x.tmp" +gone "$cache/y.part" +gone "$cache/@scope/z.tmp" +kept "$cache/pkg/keep.tmp" + +finish "pre-job exits 0 under bash -e and sweeps only partial extractions"