Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions scripts/ensure-ghosttykit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,62 @@ echo "==> Ghostty build key: $GHOSTTY_KEY"

LOCK_TIMEOUT=300
LOCK_START=$SECONDS
while ! mkdir "$LOCK_DIR" 2>/dev/null; do
LOCK_MKDIR_ERR=""
LOCK_ACQUIRED=0
cleanup_lock() {
if [[ "$LOCK_ACQUIRED" == "1" ]]; then
rmdir "$LOCK_DIR" >/dev/null 2>&1 || true
fi
}
trap cleanup_lock EXIT
print_sanitized_lock_error() {
[[ -n "$LOCK_MKDIR_ERR" ]] || return 0

local line
while IFS= read -r line; do
line="${line//$LOCK_DIR/<GhosttyKit cache lock>}"
line="${line//$CACHE_ROOT/<GhosttyKit cache>}"
if [[ -n "${HOME:-}" ]]; then
line="${line//$HOME/~}"
fi
printf ' %s\n' "$line" >&2
done <<< "$LOCK_MKDIR_ERR"
}
lock_mkdir_failed_because_contention() {
if [[ -d "$LOCK_DIR" ]]; then
return 0
fi
[[ ! -e "$LOCK_DIR" && "$LOCK_MKDIR_ERR" =~ [Ff]ile[[:space:]]exists ]]
}
while true; do
if LOCK_MKDIR_ERR="$(mkdir "$LOCK_DIR" 2>&1)"; then
LOCK_ACQUIRED=1
break
fi
if ! lock_mkdir_failed_because_contention; then
echo "error: could not create the GhosttyKit cache lock." >&2
echo "The underlying mkdir command failed while creating the lock directory." >&2
print_sanitized_lock_error
echo "Check that the GhosttyKit cache directory is writable." >&2
exit 1
Comment on lines +151 to +155

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 mkdir error output captured but never printed

$LOCK_MKDIR_ERR is redirected into a temp file on every mkdir call (lines 127 and 131), but when the permission-error path fires the file's contents are never written to stderr before exit 1. The EXIT trap then deletes it. The user sees only a static "Check that the GhosttyKit cache directory is writable" hint and never the actual mkdir diagnostic (e.g. mkdir: /path/.lock: Permission denied, or a "read-only filesystem" error). The whole point of introducing $LOCK_MKDIR_ERR was to surface this detail, so its absence here is a functional gap.

Suggested change
echo "error: could not create the GhosttyKit cache lock." >&2
echo "The underlying mkdir command failed while creating the lock directory." >&2
echo "Check that the GhosttyKit cache directory is writable." >&2
exit 1
echo "error: could not create the GhosttyKit cache lock." >&2
echo "The underlying mkdir command failed while creating the lock directory." >&2
if [[ -s "$LOCK_MKDIR_ERR" ]]; then
cat "$LOCK_MKDIR_ERR" >&2
fi
echo "Check that $CMUX_GHOSTTYKIT_CACHE_DIR (or its parent directory) is writable." >&2
exit 1

fi
if (( SECONDS - LOCK_START > LOCK_TIMEOUT )); then
echo "==> Lock stale (>${LOCK_TIMEOUT}s), removing and retrying..."
rmdir "$LOCK_DIR" 2>/dev/null || rm -rf "$LOCK_DIR"
if ! rmdir "$LOCK_DIR" 2>/dev/null; then
# Another waiter may have won the race and removed the stale lock.
# Retry the acquisition in that case; report any genuine removal error.
if [[ ! -e "$LOCK_DIR" ]]; then
continue
fi
echo "error: the stale GhosttyKit cache lock could not be removed automatically." >&2
echo "Check for another setup process, then remove the stale .lock directory under the GhosttyKit cache." >&2
exit 1
fi
continue
fi
echo "==> Waiting for GhosttyKit cache lock for $GHOSTTY_KEY..."
sleep 1
done
trap 'rmdir "$LOCK_DIR" >/dev/null 2>&1 || true' EXIT

try_fetch_prebuilt_xcframework() {
# Only attempt when Ghostty submodule is clean — dirty trees won't match any
Expand Down
15 changes: 15 additions & 0 deletions scripts/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ PROJECT_DIR="$(dirname "$SCRIPT_DIR")"

cd "$PROJECT_DIR"

# Check full Xcode before anything else. On a Command Line Tools-only machine,
# or before the Xcode license is accepted, `xcodebuild -version` fails, and the
# later steps would fail with less direct errors.
echo "==> Checking for Xcode..."
if ! xcodebuild -version >/dev/null 2>&1; then
echo "Error: full Xcode is required, but xcodebuild could not run."
echo "Active developer directory: $(xcode-select -p 2>/dev/null || echo 'unset')"
echo "Install Xcode from the App Store and select it:"
echo " sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer"
echo "If Xcode is already selected, open it once to finish setup, or accept the license:"
echo " sudo xcodebuild -license accept"
echo "Run 'xcodebuild -version' for details."
exit 1
fi

# Xcode 26 ships the Metal compiler as a separately downloaded component rather
# than inside Xcode.app. Without it the app target fails partway through the
# build ("cannot execute tool 'metal' due to missing Metal Toolchain"), after
Expand Down
Loading