fix(portability): make test green on macOS, GNU-only idioms replaced
The suite and seven libs assumed a GNU userland: `cmd | grep -q` under pipefail (grep exits at the first match, the producer takes SIGPIPE, rc 141 → 15 false "merged into" FAILs in gitflow-test), `sed -i` with no suffix, `wc -l` padding compared as a string, `stat -c`, `touch -d`, `realpath -m` (gstack-links refusal never fired), bare `timeout` off the sanitized PATH (design gate READY BUT UNVERIFIED), `grep -oP` (update-all emptied the plugin list). Thirteen suites were red on this machine. Portable forms on the native userland of both OS: producer captured out of the pipeline, `sed -i.bak` in tests and a temp-sibling `sed_profile` on the user dotfile, `tr -d ' '`, python3 perms, `touch -t`, a `realpath -m` emulation that refuses `..`, perl `alarm` for the 15 s bound, `sed -n` token extraction. Regression tests: gstack-links T4b, doctrine-citers Alphabet/Alpha flip, profile-set-managed T18 (failing CLI no longer aborts `set`), new portability-census suite over the tracked shell files. Linux run deferred (see TODO).
This commit is contained in:
+21
-7
@@ -70,6 +70,7 @@ ensure_claude_on_path() {
|
||||
for cand in \
|
||||
"$HOME/.claude/local/claude" \
|
||||
"$HOME/.local/bin/claude" \
|
||||
/opt/homebrew/bin/claude \
|
||||
/usr/local/bin/claude; do
|
||||
[ -x "$cand" ] && { PATH="$(dirname "$cand"):$PATH"; return; }
|
||||
done
|
||||
@@ -94,6 +95,7 @@ ensure_21st_on_path() {
|
||||
local cand
|
||||
for cand in \
|
||||
"$HOME/.local/bin/21st" \
|
||||
/opt/homebrew/bin/21st \
|
||||
/usr/local/bin/21st; do
|
||||
[ -x "$cand" ] && { PATH="$(dirname "$cand"):$PATH"; return; }
|
||||
done
|
||||
@@ -113,7 +115,8 @@ ensure_21st_on_path
|
||||
# out; the FIRST LINE of stdout does. A token env, when already exported by
|
||||
# the user's shell profile, wins without a CLI call (never requested here:
|
||||
# tool calls don't share a shell, and a secret doesn't belong in a comment
|
||||
# or the transcript). `timeout 15` bounds a hung CLI; stdin is closed so a
|
||||
# or the transcript). A perl `alarm` of 15 s bounds a hung CLI (GNU
|
||||
# `timeout` is absent from the macOS system PATH); stdin is closed so a
|
||||
# CLI that reads stdin can't eat the gate's own `read` loop; stderr never
|
||||
# enters the match (stdout only). Echoes: in | out | unknown:<diagnostic>.
|
||||
twentyfirst_auth_state() {
|
||||
@@ -122,11 +125,13 @@ twentyfirst_auth_state() {
|
||||
return
|
||||
fi
|
||||
local line rc
|
||||
if line="$(timeout 15 21st whoami 2>/dev/null </dev/null | head -1)"; then
|
||||
if line="$(perl -e 'alarm shift; exec @ARGV' 15 21st whoami \
|
||||
2>/dev/null </dev/null)"; then
|
||||
rc=0
|
||||
else
|
||||
rc=$?
|
||||
fi
|
||||
line="${line%%$'\n'*}" # first line, without head(1) in a pipeline
|
||||
if [ "$rc" -eq 0 ]; then
|
||||
case "$line" in
|
||||
"Logged in as "*) echo in; return ;;
|
||||
@@ -160,14 +165,22 @@ tool_active() {
|
||||
;;
|
||||
plugin)
|
||||
if ! command -v "$CLAUDE_BIN" >/dev/null 2>&1; then echo unknown; return; fi
|
||||
if "$CLAUDE_BIN" plugin list 2>/dev/null \
|
||||
| awk -v p="^[[:space:]]*❯ ${name}@" '$0 ~ p {f=1; next} f && /Status:/ {print; exit}' \
|
||||
| grep -q "✔ enabled"
|
||||
# capture first, then match: an early-exit awk/grep -q in a pipe
|
||||
# SIGPIPEs the producer (rc 141 under pipefail on macOS)
|
||||
local plist
|
||||
plist="$("$CLAUDE_BIN" plugin list 2>/dev/null)" || true
|
||||
if grep -q "✔ enabled" < <(awk -v p="^[[:space:]]*❯ ${name}@" \
|
||||
'$0 ~ p {f=1; next} f && /Status:/ {print; exit}' <<<"$plist")
|
||||
then echo active; else echo inactive; fi
|
||||
;;
|
||||
mcp)
|
||||
if ! command -v "$CLAUDE_BIN" >/dev/null 2>&1; then echo unknown; return; fi
|
||||
if "$CLAUDE_BIN" mcp list 2>/dev/null | grep -q "^${name}"; then echo active; else echo inactive; fi
|
||||
if grep -q "^${name}" \
|
||||
<<<"$("$CLAUDE_BIN" mcp list 2>/dev/null)"; then
|
||||
echo active
|
||||
else
|
||||
echo inactive
|
||||
fi
|
||||
;;
|
||||
cli)
|
||||
command -v "$name" >/dev/null 2>&1 || { echo inactive; return; }
|
||||
@@ -222,7 +235,8 @@ print_unverified() {
|
||||
echo " also unverified (claude CLI unreachable): ${unverified[*]}"
|
||||
fi
|
||||
local entry name diag
|
||||
for entry in "${unverified_cli[@]}"; do
|
||||
# bash 3.2 (macOS /bin/bash) errors on an empty array under set -u
|
||||
for entry in ${unverified_cli[@]+"${unverified_cli[@]}"}; do
|
||||
name="${entry%% (*}"
|
||||
diag="${entry#*\(}"; diag="${diag%\)}"
|
||||
echo " $name could not answer: $diag —" \
|
||||
|
||||
Reference in New Issue
Block a user