openclaw/scripts/pre-commit/resolve-node.sh

30 lines
1.2 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Resolve the newest nvm-managed Node when it is not already in PATH.
# Source this file; do not execute it directly.
#
# Cross-platform: avoids GNU-only `sort -V` (not supported by BSD sort on
# macOS). Instead, each semver component is zero-padded to five digits so
# that a plain lexicographic sort correctly selects the highest semantic
# version (e.g. v22.x wins over v18.x).
if ! command -v node >/dev/null 2>&1; then
_nvm_node=$(
for _p in "$HOME/.nvm/versions/node"/*/bin/node; do
[[ -x "$_p" ]] || continue
_ver="${_p%/bin/node}"; _ver="${_ver##*/v}"
IFS=. read -r _ma _mi _pa <<< "$_ver"
fix(file-lock,git-hooks): PID reuse detection, null-payload race, prerelease sort Three independent fixes bundled here because they came from the same review pass. ── 1. Record lock owner identity beyond PID (file-lock) ────────────── Stale-lock detection used only isPidAlive(), but PIDs are reusable. On systems with small PID namespaces (containers, rapid restarts) a crashed writer's PID can be reassigned to an unrelated live process, causing isStaleLock to return false and the lock to appear held indefinitely. Fix: record the process start time (field 22 from /proc/{pid}/stat) alongside pid and createdAt. On Linux, if the current holder's startTime differs from the stored value the PID was recycled and the lock is reclaimed immediately. On other platforms startTime is omitted and the existing createdAt age-check (a reused PID inherits the old timestamp, exceeding staleMs) remains as the fallback. ── 2. Restore mtime fallback for null/unparseable payloads (file-lock) ─ The previous fix treated null payload as immediately stale. But the lock file is created (empty) by open('wx') before writeFile fills in the JSON. A live writer still in that window has an empty file; marking it stale immediately allows a second process to steal the lock and both to enter fn() concurrently. Fix: when payload is null, fall back to the file's mtime. A file younger than staleMs may belong to a live writer and is left alone; a file older than staleMs was definitely orphaned and is reclaimed. A new test asserts that a freshly-created empty lock (recent mtime) is NOT treated as stale. ── 3. Strip prerelease suffix before printf '%05d' (resolve-node.sh) ── When an nvm install has a prerelease directory name (e.g. v22.0.0-rc.1/bin/node), splitting on '.' leaves _pa as '0-rc.1'. printf '%05d' then fails because '0-rc.1' is not an integer, and set -euo pipefail aborts the hook before lint/format can run — the opposite of what the nvm fallback is meant to achieve. Fix: strip the longest non-digit suffix from each component before printf: '0-rc.1' → '0', '14' → '14' (no-op for normal releases). Uses POSIX parameter expansion so it works on both GNU bash and macOS bash 3.x.
2026-03-15 07:46:31 +00:00
# Strip any non-numeric suffix so prerelease tags (e.g. "0-rc.1", "0-nightly")
# do not make printf '%05d' fail under set -euo pipefail.
# ${var%%pattern} removes the longest suffix matching pattern, so
# "0-rc.1" → "0" and "14" → "14" (no-op when already numeric-only).
_ma="${_ma%%[^0-9]*}"
_mi="${_mi%%[^0-9]*}"
_pa="${_pa%%[^0-9]*}"
printf '%05d%05d%05d\t%s\n' "${_ma:-0}" "${_mi:-0}" "${_pa:-0}" "$_p"
done | sort | tail -1 | cut -f2-
)
if [[ -x "$_nvm_node" ]]; then
export PATH="$(dirname "$_nvm_node"):$PATH"
fi
unset _nvm_node _p _ver _ma _mi _pa
fi