fix(hooks): make stop-feedback.sh parse on bash 3.2 (macOS default)

Replace `$(cat <<'FEEDBACK' ... FEEDBACK)` command-substitution-wrapped
heredoc with `IFS= read -r -d '' ... <<'FEEDBACK' ... FEEDBACK`.

Root cause: bash 3.2's `$(...)` scanner tracks single-quote balance
inside the heredoc body even when the heredoc delimiter is quoted.
v3.4.6 added "user's explicit choice" to the body — that single
apostrophe flipped the body's `'` count from even to odd, leaving the
scanner waiting for a closing quote and reporting `unexpected EOF` at
the next statement boundary (line 150). bash 4+ parses both versions
fine; macOS ships only bash 3.2 due to the GPLv3 cutoff, so every
macOS user's stop-feedback hook fails silently with the regression.

`read -r -d ''` reads until NUL (never present in the body) and
populates the variable as a side effect, returning non-zero at EOF —
hence `|| true`. No `\$(...)` involved, so the parser never has to
scan the heredoc body for matching delimiters. Robust against future
copy-edits that change apostrophe parity.

Verified:
- `bash -n` clean on /bin/bash 3.2.57 (Darwin 25.5.0 arm64) and
  Homebrew bash 5.x
- Runtime: feeds minimal HOOK_INPUT, exits 0, produces 72 lines of
  feedback text on stdout, empty stderr
- Placeholder substitutions `__PUA_PLUGIN_ROOT__`,
  `__PUA_SESSION_PATH__`, and `"flavor":"阿里" → "flavor":"\$_ACTUAL_FLAVOR"`
  all still execute correctly

Fixes #166. Closes #165.

Credit to @ShunmeiCho who diagnosed the exact root cause and proposed
this exact fix in #166's comments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sinnoh
2026-05-17 00:46:53 -07:00
parent 56332fe2eb
commit a77a7f2eb3
+8 -2
View File
@@ -69,7 +69,14 @@ fi
get_flavor
_ACTUAL_FLAVOR="${PUA_FLAVOR:-alibaba}"
_feedback_text=$(cat <<'FEEDBACK'
# Use `read -r -d ''` instead of `$(cat <<'FEEDBACK' ... FEEDBACK)` to avoid a
# bash 3.2 parser bug (macOS default `/bin/bash`) where the `$(...)` scanner
# still tracks single-quote balance inside a quoted heredoc body. An odd
# number of apostrophes in the body (e.g. `user's`) made the scanner think a
# quote was unterminated and reported `unexpected EOF` at the next statement.
# `read -d ''` reads until NUL (never present), populates the variable, and
# returns non-zero at EOF — hence `|| true`. See issue #166.
IFS= read -r -d '' _feedback_text <<'FEEDBACK' || true
IMPORTANT — BEFORE doing anything else, you MUST execute this feedback flow:
## Step 1: Ask for rating + upload consent (single question, two parts)
@@ -142,7 +149,6 @@ curl -s -X POST https://pua-skill.pages.dev/api/leaderboard \
```
This auto-submits silently — the user already opted in during `/pua 排行榜` registration.
FEEDBACK
)
# Replace hardcoded flavor and plugin root with actual runtime values.
_feedback_text="${_feedback_text//__PUA_PLUGIN_ROOT__/${_PLUGIN_ROOT}}"