fix(hooks): scope install traps to subshells to prevent RETURN trap leak

trap '...' RETURN inside a function is NOT local to that function in bash
— it persists in the calling scope and fires on every subsequent function
return. After install_shellcheck set the trap, it fired again when
ensure_tool returned with $tmp_dir unbound, causing nounset abort.

Fix: change install functions from {} to () (subshell bodies) and use
trap EXIT instead of RETURN. The trap is now scoped to the subshell and
cannot leak to callers.

Also fixes double _os() call in install_jq and removes redundant local
declarations (subshells don't need them).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TP4EGbBg3XMcyF28Lx78XJ
This commit is contained in:
2026-06-20 22:16:49 +00:00
parent 7323aec740
commit 8f5e4eeaa5

View File

@@ -39,49 +39,44 @@ _arch() {
esac
}
install_shellcheck() {
local os arch tarball url tmp_dir
install_shellcheck() (
os="$(_os)"
arch="$(_arch)"
tarball="shellcheck-v${SHELLCHECK_VERSION}.${os}.${arch}.tar.xz"
url="https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/${tarball}"
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' RETURN
trap 'rm -rf "$tmp_dir"' EXIT
echo "Installing shellcheck v${SHELLCHECK_VERSION}..."
curl -fsSL "$url" -o "$tmp_dir/$tarball"
tar -xJf "$tmp_dir/$tarball" -C "$tmp_dir" --strip-components=1
install -m 755 "$tmp_dir/shellcheck" "$TOOL_INSTALL_DIR/shellcheck"
echo "Installed: $TOOL_INSTALL_DIR/shellcheck"
}
)
install_jq() {
local os arch binary url tmp_dir
install_jq() (
os="$(_os)"
[[ "$os" == "darwin" ]] && os="macos"
arch="$(_arch | sed 's/x86_64/amd64/; s/aarch64/arm64/')"
[[ "$(_os)" == "darwin" ]] && os="macos"
binary="jq-${os}-${arch}"
url="https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/${binary}"
url="https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-${os}-${arch}"
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' RETURN
trap 'rm -rf "$tmp_dir"' EXIT
echo "Installing jq v${JQ_VERSION}..."
curl -fsSL "$url" -o "$tmp_dir/jq"
install -m 755 "$tmp_dir/jq" "$TOOL_INSTALL_DIR/jq"
echo "Installed: $TOOL_INSTALL_DIR/jq"
}
)
install_yq() {
local os arch binary url tmp_dir
install_yq() (
os="$(_os)"
arch="$(_arch | sed 's/x86_64/amd64/; s/aarch64/arm64/')"
binary="yq_${os}_${arch}"
url="https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/${binary}"
url="https://github.com/mikefarah/yq/releases/download/v${YQ_VERSION}/yq_${os}_${arch}"
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' RETURN
trap 'rm -rf "$tmp_dir"' EXIT
echo "Installing yq v${YQ_VERSION}..."
curl -fsSL "$url" -o "$tmp_dir/yq"
install -m 755 "$tmp_dir/yq" "$TOOL_INSTALL_DIR/yq"
echo "Installed: $TOOL_INSTALL_DIR/yq"
}
)
ensure_tool() {
local tool="$1"