Skip to content

Commit b0501d6

Browse files
committed
unbreak macOS Apple Silicon install, atomic Linux install, real version smoke test
Three independently-motivated bug fixes in scripts/install.sh: 1. macOS Apple Silicon: nightly Mach-O is SIGKILLed at exec The lightpanda-aarch64-macos asset is killed by the kernel even with an ad-hoc / linker signature; ad-hoc re-signing locally does not help. Route ONLY Apple Silicon through the upstream Homebrew tap (lightpanda-io/browser/lightpanda), which builds from source. Intel macOS keeps the GitHub Release Mach-O path — there's no evidence the SIGKILL applies to x86_64 and switching working users to a brew dependency unprompted would be over-aggressive. 2. Failed reruns destroy existing working binary SKILL.md documents reruns as the update path, but curl -L -o "$INSTALL_DIR/$BINARY_NAME" overwrites the install target during download. Atomic install via mktemp + verify + smoke-test + mv preserves the existing binary on any failure. 3. --version smoke test silently accepts broken binaries Lightpanda's CLI has no --version flag; running it logs '$msg=exit err=UnknownCommand' and exits 1. The current 2>/dev/null + --help fallback masks this, and the head -1 pipe at the end of the test means it succeeds on broken binaries that produce no output. Use the 'version' subcommand and capture stderr to surface real diagnostics like 'GLIBC_2.32 not found' on failure. Other adjacent changes — happy to split into follow-up PRs if you prefer narrower scope: - set -euo pipefail (was set -e); pipefail catches a failing curl in curl | jq that would otherwise be hidden by jq exiting 0 on empty input. - chmod 0755 instead of chmod a+x — mktemp creates 0600 so a+x yields 0711 (owner-only readable). Only relevant because of the atomic install change. - Optional $GITHUB_TOKEN raises the API quota from 60->5000/hr. - PATH-shadow check warns when an older lightpanda is earlier in PATH than the freshly installed one. - SKILL.md Install section gains a per-OS callout describing the Apple-Silicon-via-brew vs Intel-+-Linux-via-release split.
1 parent bc562f3 commit b0501d6

2 files changed

Lines changed: 141 additions & 31 deletions

File tree

SKILL.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ Prefer the built-in Web Search tool when it is available and sufficient for your
2525
bash scripts/install.sh
2626
```
2727

28-
Lightpanda is available on Linux and macOS only. Windows is supported via WSL2.
28+
Lightpanda is available on Linux and macOS only. Windows is supported via WSL2. The script picks the right method per OS:
29+
- **macOS, Apple Silicon** — installs via the upstream Homebrew tap (`brew install lightpanda-io/browser/lightpanda`). Requires Homebrew. The nightly Mach-O binaries from GitHub Releases get SIGKILLed by the kernel at exec on Apple Silicon despite shipping with an ad-hoc / linker signature, so the script delegates to the tap, which builds from source.
30+
- **macOS Intel + Linux** — downloads the nightly binary from GitHub Releases, verifies its SHA-256, smoke-tests it, and atomically installs to `$HOME/.local/bin/lightpanda` (override with `LIGHTPANDA_DIR=...`). On rerun, the existing binary is left in place if any step fails.
2931

3032
The binary is a nightly build that evolves quickly. If you encounter crashes or issues, run `scripts/install.sh` again to update to the latest version (max once per day).
3133

scripts/install.sh

Lines changed: 138 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
11
#!/bin/bash
22
# Lightpanda Setup Script
3-
# Run once to install Lightpanda browser
3+
# Run to install or update the Lightpanda browser binary.
4+
#
5+
# macOS arm64 — installs via the upstream Homebrew tap. The nightly
6+
# Mach-O is SIGKILLed at exec on Apple Silicon despite
7+
# shipping with an ad-hoc / linker signature, so we
8+
# delegate to brew which builds from source.
9+
# macOS x86_64,
10+
# Linux — downloads the nightly asset, verifies its SHA-256,
11+
# smoke-tests it, and atomically `mv`s it into
12+
# $LIGHTPANDA_DIR (default $HOME/.local/bin). The
13+
# existing binary is left in place if any step fails —
14+
# this is the documented update path.
15+
16+
# `pipefail` propagates a failing curl in `curl | jq` (jq exits 0 on
17+
# empty input). `-u` turns variable typos into hard errors.
18+
set -euo pipefail
419

5-
set -e
6-
7-
INSTALL_DIR="${LIGHTPANDA_DIR:-$HOME/.local/bin}"
820
BINARY_NAME="lightpanda"
921

1022
echo "=== Lightpanda Setup ==="
11-
echo "Install directory: $INSTALL_DIR"
1223

13-
# Detect OS and architecture
1424
OS="$(uname -s)"
1525
ARCH="$(uname -m)"
1626

@@ -32,10 +42,70 @@ case "$OS" in
3242
Darwin)
3343
case "$ARCH" in
3444
x86_64)
45+
# Intel Macs: the Mach-O release asset runs fine, so
46+
# use the same download/verify/atomic-mv path as Linux.
3547
DOWNLOAD_URL="https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-x86_64-macos"
3648
;;
3749
arm64|aarch64)
38-
DOWNLOAD_URL="https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-aarch64-macos"
50+
# Apple Silicon: the nightly Mach-O is SIGKILLed at exec
51+
# by the kernel even with an ad-hoc / linker signature
52+
# (ad-hoc re-signing locally does not help). Route
53+
# through the upstream Homebrew tap, which builds from
54+
# source and produces a binary that runs.
55+
if ! command -v brew &> /dev/null; then
56+
echo "ERROR: Apple Silicon install requires Homebrew (https://brew.sh)."
57+
echo "Install Homebrew, then re-run this script. Or install Lightpanda directly:"
58+
echo " brew install lightpanda-io/browser/lightpanda"
59+
exit 1
60+
fi
61+
echo "Detected: $OS $ARCH — installing via Homebrew tap (lightpanda-io/browser/lightpanda)."
62+
# `install` is idempotent on an up-to-date install but
63+
# does not auto-upgrade an existing one; `upgrade` keeps
64+
# reruns current. Both exit 0 in the up-to-date case
65+
# and non-zero on real errors — let `set -e` propagate.
66+
brew install lightpanda-io/browser/lightpanda
67+
brew upgrade lightpanda-io/browser/lightpanda
68+
# Anchor verification to the brew prefix (general, not
69+
# keg-specific): a stale `~/.local/bin/lightpanda` from
70+
# an earlier curl-on-arm64 attempt could shadow the
71+
# brew binary and make us report success for a broken
72+
# one.
73+
BREW_PREFIX="$(brew --prefix)"
74+
BIN_PATH="$BREW_PREFIX/bin/$BINARY_NAME"
75+
if [ ! -x "$BIN_PATH" ]; then
76+
echo "ERROR: Homebrew install completed but $BIN_PATH is missing."
77+
echo "Try: brew link --overwrite lightpanda"
78+
exit 1
79+
fi
80+
# Hard-fail smoke test — brew can produce a binary that
81+
# crashes at exec (formula regression, missing dep).
82+
# Capture stderr so we can show real diagnostics.
83+
DARWIN_SMOKE_STDERR=$(mktemp)
84+
trap 'rm -f "$DARWIN_SMOKE_STDERR"' EXIT
85+
if VERSION_OUT=$("$BIN_PATH" version 2>"$DARWIN_SMOKE_STDERR") && [ -n "$VERSION_OUT" ]; then
86+
: # ok
87+
else
88+
echo "ERROR: Homebrew-installed lightpanda failed its version smoke test (\`$BIN_PATH version\` returned no output or non-zero exit)."
89+
if [ -s "$DARWIN_SMOKE_STDERR" ]; then
90+
echo " Binary stderr:"
91+
sed 's/^/ /' "$DARWIN_SMOKE_STDERR"
92+
fi
93+
echo " Try: brew reinstall lightpanda-io/browser/lightpanda"
94+
exit 1
95+
fi
96+
echo ""
97+
echo "Lightpanda installed via Homebrew: $VERSION_OUT"
98+
echo "Binary location: $BIN_PATH"
99+
# Warn if something earlier in PATH shadows the brew binary.
100+
PATH_RESOLVED="$(command -v lightpanda 2>/dev/null || true)"
101+
if [ -n "$PATH_RESOLVED" ] && [ "$PATH_RESOLVED" != "$BIN_PATH" ]; then
102+
echo ""
103+
echo "WARNING: another 'lightpanda' is earlier in your PATH:"
104+
echo " $PATH_RESOLVED"
105+
echo "Remove it or reorder PATH so $BREW_PREFIX/bin takes precedence."
106+
fi
107+
echo "Run with: lightpanda serve --host 127.0.0.1 --port 9222"
108+
exit 0
39109
;;
40110
*)
41111
echo "ERROR: Unsupported architecture: $ARCH"
@@ -50,10 +120,12 @@ case "$OS" in
50120
;;
51121
esac
52122

123+
# --- Shared download flow (Linux + Intel macOS) ---
124+
INSTALL_DIR="${LIGHTPANDA_DIR:-$HOME/.local/bin}"
53125
echo "Detected: $OS $ARCH"
126+
echo "Install directory: $INSTALL_DIR"
54127
echo "Download URL: $DOWNLOAD_URL"
55128

56-
# Check for required tools
57129
if ! command -v curl &> /dev/null; then
58130
echo "ERROR: curl not found. Please install curl."
59131
exit 1
@@ -69,65 +141,101 @@ if ! command -v sha256sum &> /dev/null && ! command -v shasum &> /dev/null; then
69141
exit 1
70142
fi
71143

72-
# Create install directory
73144
mkdir -p "$INSTALL_DIR"
74145

75-
# Determine asset name from URL
76146
ASSET_NAME="${DOWNLOAD_URL##*/}"
77147

78-
# Fetch expected SHA256 digest from GitHub release API
148+
# api.github.com unauthenticated quota is 60/hr/IP; collide on shared
149+
# NAT or in CI. $GITHUB_TOKEN raises it to 5000/hr. Branch the curl
150+
# call rather than building an array of header args — `"${arr[@]}"`
151+
# under `set -u` errors on Bash <=4.3 (RHEL 7, old containers).
152+
GITHUB_API_URL="https://api.github.com/repos/lightpanda-io/browser/releases/tags/nightly"
79153
echo "Fetching expected checksum from GitHub API..."
80-
EXPECTED_DIGEST=$(curl -sL "https://api.github.com/repos/lightpanda-io/browser/releases/tags/nightly" \
81-
| jq -r --arg name "$ASSET_NAME" '.assets[] | select(.name == $name) | .digest')
154+
if [ -n "${GITHUB_TOKEN:-}" ]; then
155+
EXPECTED_DIGEST=$(curl -fSL -H "Authorization: Bearer $GITHUB_TOKEN" "$GITHUB_API_URL" \
156+
| jq -r --arg name "$ASSET_NAME" '.assets[] | select(.name == $name) | .digest')
157+
else
158+
EXPECTED_DIGEST=$(curl -fSL "$GITHUB_API_URL" \
159+
| jq -r --arg name "$ASSET_NAME" '.assets[] | select(.name == $name) | .digest')
160+
fi
82161

83162
if [ -z "$EXPECTED_DIGEST" ] || [ "$EXPECTED_DIGEST" = "null" ]; then
84163
echo "ERROR: Could not retrieve checksum for $ASSET_NAME from GitHub API."
85164
exit 1
86165
fi
87166

88-
# Strip the "sha256:" prefix
89167
EXPECTED_SHA256="${EXPECTED_DIGEST#sha256:}"
90168
echo "Expected SHA256: $EXPECTED_SHA256"
91169

92-
# Download binary
93-
echo "Downloading Lightpanda..."
94-
curl -L -o "$INSTALL_DIR/$BINARY_NAME" "$DOWNLOAD_URL"
170+
# Download to a temp file in the same directory, verify, smoke-test,
171+
# THEN atomically `mv` into place. SKILL.md documents reruns as the
172+
# update path, so a failed download must not destroy a working binary.
173+
TMP_BINARY=$(mktemp "$INSTALL_DIR/.${BINARY_NAME}.XXXXXX")
174+
SMOKE_STDERR=$(mktemp)
175+
trap 'rm -f "$TMP_BINARY" "$SMOKE_STDERR"' EXIT
176+
177+
echo "Downloading Lightpanda to $TMP_BINARY..."
178+
curl -fSL -o "$TMP_BINARY" "$DOWNLOAD_URL"
95179

96-
# Verify checksum
97180
echo "Verifying checksum..."
98181
if command -v sha256sum &> /dev/null; then
99-
ACTUAL_SHA256=$(sha256sum "$INSTALL_DIR/$BINARY_NAME" | awk '{print $1}')
182+
ACTUAL_SHA256=$(sha256sum "$TMP_BINARY" | awk '{print $1}')
100183
else
101-
ACTUAL_SHA256=$(shasum -a 256 "$INSTALL_DIR/$BINARY_NAME" | awk '{print $1}')
184+
ACTUAL_SHA256=$(shasum -a 256 "$TMP_BINARY" | awk '{print $1}')
102185
fi
103186

104187
if [ "$ACTUAL_SHA256" != "$EXPECTED_SHA256" ]; then
105188
echo "ERROR: Checksum verification FAILED!"
106189
echo " Expected: $EXPECTED_SHA256"
107190
echo " Actual: $ACTUAL_SHA256"
108-
rm -f "$INSTALL_DIR/$BINARY_NAME"
191+
echo " Existing binary (if any) at $INSTALL_DIR/$BINARY_NAME was NOT replaced."
109192
exit 1
110193
fi
111194

112195
echo "Checksum verified OK."
113-
chmod a+x "$INSTALL_DIR/$BINARY_NAME"
196+
# 0755 (conventional executable mode) rather than `chmod a+x` over
197+
# mktemp's 0600, which would yield 0711 (owner-only readable).
198+
chmod 0755 "$TMP_BINARY"
199+
200+
# Smoke-test BEFORE replacing. Use the `version` subcommand — Lightpanda's
201+
# `--version` flag exits 1 with `UnknownCommand`, so it would falsely
202+
# reject a working binary. Capturing stdout to a variable means the `if`
203+
# exit status is the binary's exit status (no pipe to mask it).
204+
echo "Smoke-testing the downloaded binary..."
205+
if ! VERSION_OUT=$("$TMP_BINARY" version 2>"$SMOKE_STDERR") || [ -z "$VERSION_OUT" ]; then
206+
echo "ERROR: Downloaded binary failed to run (\`version\` subcommand returned no output or non-zero exit)."
207+
echo " This may indicate an incompatible system, missing libc, or a corrupted release asset."
208+
if [ -s "$SMOKE_STDERR" ]; then
209+
echo " Binary stderr:"
210+
sed 's/^/ /' "$SMOKE_STDERR"
211+
fi
212+
echo " Existing binary (if any) at $INSTALL_DIR/$BINARY_NAME was NOT replaced."
213+
exit 1
214+
fi
215+
216+
# `mv` within the same filesystem is atomic.
217+
mv "$TMP_BINARY" "$INSTALL_DIR/$BINARY_NAME"
218+
# Leave the EXIT trap registered so $SMOKE_STDERR still gets cleaned;
219+
# `rm -f` on the moved-away $TMP_BINARY is a silent no-op.
114220

115-
# Check if install directory is in PATH
221+
# Check PATH membership AND shadow (something else earlier in PATH).
116222
if [[ ":$PATH:" != *":$INSTALL_DIR:"* ]]; then
117223
echo ""
118224
echo "WARNING: $INSTALL_DIR is not in your PATH"
119225
echo "Add this to your shell profile (~/.bashrc or ~/.zshrc):"
120226
echo " export PATH=\"\$PATH:$INSTALL_DIR\""
227+
else
228+
PATH_RESOLVED="$(command -v lightpanda 2>/dev/null || true)"
229+
if [ -n "$PATH_RESOLVED" ] && [ "$PATH_RESOLVED" != "$INSTALL_DIR/$BINARY_NAME" ]; then
230+
echo ""
231+
echo "WARNING: another 'lightpanda' is earlier in your PATH:"
232+
echo " $PATH_RESOLVED"
233+
echo "Remove it or reorder PATH so $INSTALL_DIR takes precedence."
234+
fi
121235
fi
122236

123-
# Test installation
124237
echo ""
125-
echo "Testing Lightpanda..."
126-
if "$INSTALL_DIR/$BINARY_NAME" --version 2>/dev/null || "$INSTALL_DIR/$BINARY_NAME" --help 2>/dev/null | head -1; then
127-
echo "Lightpanda installed successfully!"
128-
else
129-
echo "Binary installed at: $INSTALL_DIR/$BINARY_NAME"
130-
fi
238+
echo "Lightpanda installed successfully: ${VERSION_OUT:-OK}"
131239

132240
echo ""
133241
echo "=== Setup Complete ==="

0 commit comments

Comments
 (0)