Merge bugfix/macos-installer into develop
This commit is contained in:
+93
-5
@@ -16,6 +16,16 @@ err() { echo -e "${RED}✗${NC} $1"; }
|
|||||||
|
|
||||||
REPO="$(cd "$(dirname "$0")" && pwd)"
|
REPO="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
|
# In-place file edit that works on both GNU and BSD sed: `sed -i` needs a backup
|
||||||
|
# suffix argument on macOS and forbids one on Linux, so go through a temp file
|
||||||
|
# instead. Writing back with `cat >` (not `mv`) keeps the original mode/owner.
|
||||||
|
_sed_inplace() {
|
||||||
|
local expr="$1" file="$2" tmp
|
||||||
|
tmp="$(mktemp)" || return 1
|
||||||
|
sed "$expr" "$file" > "$tmp" && cat "$tmp" > "$file"
|
||||||
|
rm -f "$tmp"
|
||||||
|
}
|
||||||
|
|
||||||
# Log to file for post-mortem debugging (terminal output unchanged)
|
# Log to file for post-mortem debugging (terminal output unchanged)
|
||||||
LOG_FILE="$REPO/install-$(date +%Y%m%d-%H%M%S).log"
|
LOG_FILE="$REPO/install-$(date +%Y%m%d-%H%M%S).log"
|
||||||
if touch "$LOG_FILE" 2>/dev/null; then
|
if touch "$LOG_FILE" 2>/dev/null; then
|
||||||
@@ -291,6 +301,67 @@ fi
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
# Portable `timeout`: GNU coreutils' timeout is absent from a stock macOS, and
|
||||||
|
# this has to run on a machine where nothing is installed yet. Returns 124 when
|
||||||
|
# the deadline is hit, otherwise the command's own exit status.
|
||||||
|
_run_with_timeout() {
|
||||||
|
local secs="$1" waited=0 pid
|
||||||
|
shift
|
||||||
|
"$@" &
|
||||||
|
pid=$!
|
||||||
|
while kill -0 "$pid" 2>/dev/null; do
|
||||||
|
if [ "$waited" -ge "$secs" ]; then
|
||||||
|
# Park the shell's own stderr: bash announces a signal-killed job on ITS
|
||||||
|
# stderr, so redirecting kill/wait alone does not suppress it — and that
|
||||||
|
# line in the install log reads like a real error.
|
||||||
|
exec 3>&2 2>/dev/null
|
||||||
|
kill -TERM "$pid" 2>/dev/null || true
|
||||||
|
# Playwright downloads in a child process that survives a TERM aimed at
|
||||||
|
# its parent; left alone it keeps holding the browser-cache lock.
|
||||||
|
pkill -f oopDownloadBrowserMain 2>/dev/null || true
|
||||||
|
wait "$pid" 2>/dev/null || true
|
||||||
|
exec 2>&3 3>&-
|
||||||
|
return 124
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
waited=$((waited + 5))
|
||||||
|
done
|
||||||
|
wait "$pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate gstack's node_modules at the locked versions so `bunx playwright`
|
||||||
|
# resolves the LOCAL Playwright. Without it bunx pulls the latest from npm,
|
||||||
|
# which wants a different browser revision than the one gstack imports.
|
||||||
|
_gstack_bun_install() {
|
||||||
|
( cd "$GSTACK_DIR" && { bun install --frozen-lockfile >/dev/null 2>&1 ||
|
||||||
|
bun install >/dev/null 2>&1; } )
|
||||||
|
}
|
||||||
|
|
||||||
|
_gstack_pw_install() { ( cd "$GSTACK_DIR" && bunx playwright install chromium ); }
|
||||||
|
|
||||||
|
# Install gstack's Chromium here instead of leaving it to ./setup, so the
|
||||||
|
# download can be bounded. A Playwright older than the running Node deadlocks
|
||||||
|
# mid-extraction — both processes idle, no error, no progress, forever — which
|
||||||
|
# silently stalls the whole installer. On a timeout, bump Playwright (gstack
|
||||||
|
# declares "playwright": "^1.x", so a minor bump is within its own range) and
|
||||||
|
# retry once. A second failure warns rather than aborts: gstack is OFF by
|
||||||
|
# default and only its browser (/browse, /qa, screenshots) depends on this.
|
||||||
|
gstack_install_browser_guarded() {
|
||||||
|
[ -d "$GSTACK_DIR" ] && command -v bun >/dev/null 2>&1 || return 0
|
||||||
|
_gstack_bun_install || return 0
|
||||||
|
if _run_with_timeout "$GSTACK_BROWSER_TIMEOUT" _gstack_pw_install; then
|
||||||
|
ok "gstack Chromium ready"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
warn "Chromium install stalled >${GSTACK_BROWSER_TIMEOUT}s — bumping gstack's Playwright, retrying"
|
||||||
|
( cd "$GSTACK_DIR" && bun add playwright@latest >/dev/null 2>&1 ) || true
|
||||||
|
if _run_with_timeout "$GSTACK_BROWSER_TIMEOUT" _gstack_pw_install; then
|
||||||
|
ok "gstack Chromium ready after Playwright bump (./setup rebuilds browse)"
|
||||||
|
else
|
||||||
|
warn "gstack Chromium unavailable — /browse, /qa and screenshots stay off"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# gstack pins Playwright (1.58.x) which only ships browser builds for
|
# gstack pins Playwright (1.58.x) which only ships browser builds for
|
||||||
# ubuntu<=24.04. On a newer distro the browser install fails ("does not
|
# ubuntu<=24.04. On a newer distro the browser install fails ("does not
|
||||||
# support chromium on ubuntuXX.04"). Bump gstack's Playwright to a version
|
# support chromium on ubuntuXX.04"). Bump gstack's Playwright to a version
|
||||||
@@ -307,7 +378,7 @@ gstack_bump_playwright_if_unsupported() {
|
|||||||
[ -n "$ostag" ] || return 0 # only the known Ubuntu case
|
[ -n "$ostag" ] || return 0 # only the known Ubuntu case
|
||||||
pwlib="$GSTACK_DIR/node_modules/playwright-core/lib"
|
pwlib="$GSTACK_DIR/node_modules/playwright-core/lib"
|
||||||
# populate node_modules at the pinned version so we can read its support list
|
# populate node_modules at the pinned version so we can read its support list
|
||||||
( cd "$GSTACK_DIR" && { bun install --frozen-lockfile >/dev/null 2>&1 || bun install >/dev/null 2>&1; } ) || return 0
|
_gstack_bun_install || return 0
|
||||||
if grep -rqs "$ostag" "$pwlib" 2>/dev/null; then
|
if grep -rqs "$ostag" "$pwlib" 2>/dev/null; then
|
||||||
return 0 # pinned Playwright already supports this OS
|
return 0 # pinned Playwright already supports this OS
|
||||||
fi
|
fi
|
||||||
@@ -337,6 +408,10 @@ echo ""
|
|||||||
# git add skills-external/gstack && git commit -m "chore: update gstack"
|
# git add skills-external/gstack && git commit -m "chore: update gstack"
|
||||||
|
|
||||||
GSTACK_DIR="$REPO/skills-external/gstack"
|
GSTACK_DIR="$REPO/skills-external/gstack"
|
||||||
|
# Deadline for gstack's Chromium download+extract. Generous on purpose: a real
|
||||||
|
# install runs 1-3 min, overshooting only delays the fallback, and the failure
|
||||||
|
# it catches would otherwise hang the installer forever.
|
||||||
|
GSTACK_BROWSER_TIMEOUT=900
|
||||||
|
|
||||||
if [ ! -d "$GSTACK_DIR/.git" ] && [ ! -f "$GSTACK_DIR/.git" ]; then
|
if [ ! -d "$GSTACK_DIR/.git" ] && [ ! -f "$GSTACK_DIR/.git" ]; then
|
||||||
info "Initializing GStack submodule..."
|
info "Initializing GStack submodule..."
|
||||||
@@ -369,6 +444,10 @@ if [ -d "$GSTACK_DIR" ]; then
|
|||||||
# chromium" fail). Non-fatal if it can't — gstack is OFF by default.
|
# chromium" fail). Non-fatal if it can't — gstack is OFF by default.
|
||||||
gstack_bump_playwright_if_unsupported
|
gstack_bump_playwright_if_unsupported
|
||||||
|
|
||||||
|
# Then fetch the browser under a deadline. ./setup would do it itself, but
|
||||||
|
# unbounded — and this is the step that hangs when Playwright trails Node.
|
||||||
|
gstack_install_browser_guarded
|
||||||
|
|
||||||
info "Running GStack setup..."
|
info "Running GStack setup..."
|
||||||
_gstack_setup_ok=0
|
_gstack_setup_ok=0
|
||||||
if [ -x "$GSTACK_DIR/setup" ]; then
|
if [ -x "$GSTACK_DIR/setup" ]; then
|
||||||
@@ -993,14 +1072,23 @@ fi
|
|||||||
# `claude --effort max` alias (the alias would even override settings.json).
|
# `claude --effort max` alias (the alias would even override settings.json).
|
||||||
EFFORT_CLEANED=0
|
EFFORT_CLEANED=0
|
||||||
if grep -qF 'export CLAUDE_EFFORT=max' "$SHELL_PROFILE" 2>/dev/null; then
|
if grep -qF 'export CLAUDE_EFFORT=max' "$SHELL_PROFILE" 2>/dev/null; then
|
||||||
sed -i '/export CLAUDE_EFFORT=max/d' "$SHELL_PROFILE"; EFFORT_CLEANED=1
|
_sed_inplace '/export CLAUDE_EFFORT=max/d' "$SHELL_PROFILE"; EFFORT_CLEANED=1
|
||||||
fi
|
fi
|
||||||
if grep -qF "alias claude='claude --effort max'" "$SHELL_PROFILE" 2>/dev/null; then
|
if grep -qF "alias claude='claude --effort max'" "$SHELL_PROFILE" 2>/dev/null; then
|
||||||
sed -i "\#alias claude='claude --effort max'#d" "$SHELL_PROFILE"; EFFORT_CLEANED=1
|
_sed_inplace "\#alias claude='claude --effort max'#d" "$SHELL_PROFILE"; EFFORT_CLEANED=1
|
||||||
fi
|
fi
|
||||||
if [ "$EFFORT_CLEANED" -eq 1 ]; then
|
if [ "$EFFORT_CLEANED" -eq 1 ]; then
|
||||||
# Remove orphaned comment lines left before the deleted entries
|
# Drop the header comment left stranded above a deleted entry (the marker
|
||||||
sed -i '/^# Claude Code — added by install-plugins.sh$/{ N; /^\n$/d; }' "$SHELL_PROFILE"
|
# followed by a blank line, or by end-of-file). awk, not sed: the previous
|
||||||
|
# `{N; /^\n$/d;}` could never match — after N the pattern space starts with
|
||||||
|
# '#', so the ^\n$ anchor pair never applied, and it was a silent no-op.
|
||||||
|
_tmp_profile="$(mktemp)"
|
||||||
|
awk -v marker='# Claude Code — added by install-plugins.sh' '
|
||||||
|
$0 == marker { stranded = 1; next }
|
||||||
|
stranded { stranded = 0; if ($0 == "") next; print marker }
|
||||||
|
{ print }
|
||||||
|
' "$SHELL_PROFILE" > "$_tmp_profile" && cat "$_tmp_profile" > "$SHELL_PROFILE"
|
||||||
|
rm -f "$_tmp_profile"
|
||||||
info "Removed obsolete effort alias/env from $SHELL_PROFILE (effort set in settings.json)"
|
info "Removed obsolete effort alias/env from $SHELL_PROFILE (effort set in settings.json)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user