name: CI on: push: branches: - main pull_request: jobs: web-typecheck: runs-on: ubuntu-latest defaults: run: working-directory: web steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install --frozen-lockfile - name: Typecheck run: bun tsc --noEmit ui-tests: runs-on: self-hosted concurrency: group: self-hosted-build cancel-in-progress: false steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive - name: Select Xcode run: | set -euo pipefail if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then XCODE_DIR="/Applications/Xcode.app/Contents/Developer" else XCODE_APP="$(ls -d /Applications/Xcode*.app 2>/dev/null | head -n 1 || true)" if [ -n "$XCODE_APP" ]; then XCODE_DIR="$XCODE_APP/Contents/Developer" else echo "No Xcode.app found under /Applications" >&2 exit 1 fi fi echo "DEVELOPER_DIR=$XCODE_DIR" >> "$GITHUB_ENV" export DEVELOPER_DIR="$XCODE_DIR" xcodebuild -version xcrun --sdk macosx --show-sdk-path - name: Build GhosttyKit.xcframework run: | set -euo pipefail if ! command -v zig >/dev/null 2>&1; then if command -v brew >/dev/null 2>&1; then brew install zig else echo "zig is required to build GhosttyKit.xcframework. Install zig and retry." >&2 exit 1 fi fi (cd ghostty && zig build -Demit-xcframework=true -Demit-macos-app=false -Dxcframework-target=native) rm -rf GhosttyKit.xcframework cp -R ghostty/macos/GhosttyKit.xcframework GhosttyKit.xcframework test -d GhosttyKit.xcframework - name: Clean DerivedData run: | # Remove stale build cache to avoid incremental build errors rm -rf ~/Library/Developer/Xcode/DerivedData/GhosttyTabs-* - name: Run UI tests run: | set -euo pipefail log_file="$(mktemp /tmp/cmux-ui-tests.XXXXXX)" console_user="$(stat -f%Su /dev/console)" console_uid="" if [ "$console_user" != "root" ]; then console_uid="$(id -u "$console_user")" fi run_xcodebuild() { if [ -n "$console_uid" ] && [ "$console_uid" != "0" ]; then /bin/launchctl asuser "$console_uid" xcodebuild \ -project GhosttyTabs.xcodeproj \ -scheme cmux \ -configuration Debug \ -destination 'platform=macOS' \ -only-testing:GhosttyTabsUITests/UpdatePillUITests \ test else xcodebuild \ -project GhosttyTabs.xcodeproj \ -scheme cmux \ -configuration Debug \ -destination 'platform=macOS' \ -only-testing:GhosttyTabsUITests/UpdatePillUITests \ test fi } if ssh -o BatchMode=yes -o ConnectTimeout=5 cmux-vm 'true' >/dev/null 2>&1; then echo "Running UI tests on cmux-vm..." rsync -a --delete \ --exclude build \ --exclude .git \ --exclude "GhosttyTabs.xcodeproj/project.xcworkspace" \ ./ cmux-vm:/Users/cmux/GhosttyTabs/ ssh cmux-vm 'cd /Users/cmux/GhosttyTabs && xcodebuild -project GhosttyTabs.xcodeproj -scheme cmux -configuration Debug -destination "platform=macOS" -only-testing:GhosttyTabsUITests/UpdatePillUITests test' exit 0 fi # macOS UI tests require an interactive Aqua session. On self-hosted runners it is # easy to end up with a runner that's online but has no foreground-capable GUI. if [ "$console_user" = "root" ] || [ -z "$console_uid" ] || [ "$console_uid" = "0" ]; then echo "No console user session detected (/dev/console is root). Skipping UI tests on this runner." rm -f "$log_file" exit 0 fi if ! /bin/launchctl print "gui/$console_uid" >/dev/null 2>&1; then echo "No gui/$console_uid launchd domain; UI tests require an Aqua session. Skipping UI tests." rm -f "$log_file" exit 0 fi if ! pgrep -x WindowServer >/dev/null 2>&1; then echo "WindowServer is not running; UI tests require a GUI session. Skipping UI tests." rm -f "$log_file" exit 0 fi if ! /usr/bin/osascript -e 'tell application "Finder" to activate' >/dev/null 2>&1; then echo "Runner cannot activate GUI apps (Finder activate failed). Skipping UI tests." rm -f "$log_file" exit 0 fi set +e run_xcodebuild 2>&1 | tee "$log_file" status=${PIPESTATUS[0]} set -e if [ "$status" -ne 0 ] && grep -qE "Failed to initialize for UI testing|Timed out while enabling automation mode|Failed to activate application" "$log_file"; then echo "UI automation flake detected. Retrying once after a short delay..." pkill -x "cmux DEV" >/dev/null 2>&1 || true killall Dock >/dev/null 2>&1 || true sleep 5 set +e run_xcodebuild 2>&1 | tee -a "$log_file" status=${PIPESTATUS[0]} set -e fi rm -f "$log_file" exit "$status"