#!/usr/bin/env bash
set -euo pipefail
APPLICATIONS_DIR="${CMUX_XCODE_APPLICATIONS_DIR:-/Applications}"
REQUIRED_SDK_MAJOR="${CMUX_CI_REQUIRED_MACOS_SDK_MAJOR:-}"
sdk_major() {
local v="$1" maj
maj="${v%%.*}"
case "$maj" in ''|*[!0-9]*) return 1 ;; esac
printf '%s' "$maj"
}
validate_required_sdk() {
local selected_dir="$1" sdk_version="$2" actual_major
[ -n "$REQUIRED_SDK_MAJOR" ] || return 0
case "$REQUIRED_SDK_MAJOR" in ''|*[!0-9]*)
echo "CMUX_CI_REQUIRED_MACOS_SDK_MAJOR must be numeric, got: $REQUIRED_SDK_MAJOR" >&2
exit 1
;;
esac
if ! actual_major="$(sdk_major "$sdk_version")"; then
echo "Could not parse macOS SDK version for $selected_dir: $sdk_version" >&2
exit 1
fi
if [ "$actual_major" != "$REQUIRED_SDK_MAJOR" ]; then
echo "Selected Xcode at $selected_dir has macOS SDK $sdk_version; required major is $REQUIRED_SDK_MAJOR" >&2
exit 1
fi
}
select_developer_dir() {
local selected_dir="$1" sdk_version="$2" label="$3"
validate_required_sdk "$selected_dir" "$sdk_version"
echo "$label (DEVELOPER_DIR): $selected_dir (macOS SDK $sdk_version)"
if [ -n "${GITHUB_ENV:-}" ]; then
echo "DEVELOPER_DIR=$selected_dir" >> "$GITHUB_ENV"
fi
export DEVELOPER_DIR="$selected_dir"
if xcode-select -s "$selected_dir" 2>/dev/null; then
echo "xcode-select default -> $selected_dir"
elif command -v sudo >/dev/null 2>&1 && sudo -n xcode-select -s "$selected_dir" 2>/dev/null; then
echo "xcode-select default (via sudo) -> $selected_dir"
else
echo "WARN: could not switch xcode-select default to $selected_dir (continuing; DEVELOPER_DIR is still set for steps that honor it)" >&2
fi
xcodebuild -version
xcrun --sdk macosx --show-sdk-path
}
PINNED_DEVELOPER_DIR="${CMUX_CI_DEVELOPER_DIR:-}"
if [ -z "$PINNED_DEVELOPER_DIR" ] && [ -n "${CMUX_CI_XCODE_APP:-}" ]; then
PINNED_DEVELOPER_DIR="${CMUX_CI_XCODE_APP%/}/Contents/Developer"
fi
if [ -n "$PINNED_DEVELOPER_DIR" ]; then
if [ ! -d "$PINNED_DEVELOPER_DIR" ]; then
echo "Pinned Xcode developer dir does not exist: $PINNED_DEVELOPER_DIR" >&2
exit 1
fi
PINNED_SDK_VER="$(DEVELOPER_DIR="$PINNED_DEVELOPER_DIR" xcrun --sdk macosx --show-sdk-version 2>/dev/null || true)"
if [ -z "$PINNED_SDK_VER" ]; then
echo "Pinned Xcode developer dir has no usable macOS SDK: $PINNED_DEVELOPER_DIR" >&2
exit 1
fi
select_developer_dir "$PINNED_DEVELOPER_DIR" "$PINNED_SDK_VER" "Selected pinned Xcode"
exit 0
fi
if [ -n "$REQUIRED_SDK_MAJOR" ]; then
case "$REQUIRED_SDK_MAJOR" in ''|*[!0-9]*)
echo "CMUX_CI_REQUIRED_MACOS_SDK_MAJOR must be numeric, got: $REQUIRED_SDK_MAJOR" >&2
exit 1
;;
esac
fi
sdk_rank() {
local v="$1" maj min
maj="${v%%.*}"
min="${v#*.}"
[ "$min" = "$v" ] && min=0
min="${min%%.*}"
case "$maj" in ''|*[!0-9]*) return 1 ;; esac
case "$min" in ''|*[!0-9]*) min=0 ;; esac
printf '%d' "$(( maj * 1000 + min ))"
}
BEST_DIR=""
BEST_VER=""
BEST_RANK=-1
BETA_DIR=""
BETA_VER=""
BETA_RANK=-1
while IFS= read -r app; do
[ -n "$app" ] || continue
dev="$app/Contents/Developer"
[ -d "$dev" ] || continue
sdk_ver="$(DEVELOPER_DIR="$dev" xcrun --sdk macosx --show-sdk-version 2>/dev/null || true)"
[ -n "$sdk_ver" ] || continue
if [ -n "$REQUIRED_SDK_MAJOR" ]; then
if ! actual_major="$(sdk_major "$sdk_ver")"; then
echo "Ignoring $app with unparsable macOS SDK version: $sdk_ver" >&2
continue
fi
if [ "$actual_major" != "$REQUIRED_SDK_MAJOR" ]; then
echo "Skipping $app -> macOS SDK $sdk_ver; required major is $REQUIRED_SDK_MAJOR"
continue
fi
fi
if ! rank="$(sdk_rank "$sdk_ver")"; then
echo "Ignoring $app with unparsable macOS SDK version: $sdk_ver" >&2
continue
fi
case "$(basename "$app")" in
*[Bb]eta*)
echo "Found $app -> macOS SDK $sdk_ver (rank $rank, beta)"
if [ "$rank" -ge "$BETA_RANK" ]; then
BETA_DIR="$dev"
BETA_VER="$sdk_ver"
BETA_RANK="$rank"
fi
continue
;;
esac
echo "Found $app -> macOS SDK $sdk_ver (rank $rank)"
if [ "$rank" -ge "$BEST_RANK" ]; then
BEST_DIR="$dev"
BEST_VER="$sdk_ver"
BEST_RANK="$rank"
fi
done < <(find "$APPLICATIONS_DIR" -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null | sort)
if [ -z "$BEST_DIR" ] && [ -n "$BETA_DIR" ]; then
echo "No stable Xcode found; falling back to beta: $BETA_DIR" >&2
BEST_DIR="$BETA_DIR"
BEST_VER="$BETA_VER"
BEST_RANK="$BETA_RANK"
fi
if [ -z "$BEST_DIR" ]; then
if [ -n "$REQUIRED_SDK_MAJOR" ]; then
echo "No Xcode.app found under $APPLICATIONS_DIR with macOS SDK major $REQUIRED_SDK_MAJOR" >&2
exit 1
fi
echo "No Xcode.app found under $APPLICATIONS_DIR" >&2
exit 1
fi
select_developer_dir "$BEST_DIR" "$BEST_VER" "Selected Xcode"