From 11743496778adc56d9c672373142da7440b66caa Mon Sep 17 00:00:00 2001 From: Aziz Albahar Date: Sun, 31 May 2026 20:47:31 -0500 Subject: [PATCH 1/2] Nightly: build app with Xcode 26 SDK so Tahoe gets Liquid Glass Nightly was building the whole app with the runner's default Xcode 16.4 (macOS 15.5 SDK). On macOS 26 a binary linked against a pre-26 SDK is forced into the legacy non-Liquid-Glass appearance, so the Settings window (and everything else) rendered with old chrome. This regressed in #5022, which moved nightly off the macOS 26 runner to fix the universal Ghostty helper link. Keep the macOS 15 host (zig 0.15.2 can only cross-link the x86_64 helper slice against a pre-26 SDK) but build the Swift app with the macOS 26 Xcode that already ships on the runner image, matching the team's .xcode-version pin. The universal helper is built separately with the older Xcode and injected into the bundle before signing; the existing architecture-verification step still guards universality. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/nightly.yml | 96 +++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7124719a8e09..66ad23ec5c14 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -100,11 +100,15 @@ jobs: build-sign-notarize-nightly: needs: decide if: needs.decide.outputs.should_build == 'true' - # Keep publishing builds on macOS 15 until Zig can link the real universal - # Ghostty CLI helper on macOS 26. ci-macos-compat.yml covers macOS 26 with - # CMUX_SKIP_ZIG_BUILD=1, but nightly needs the real universal helper. + # Run on the macOS 15 host because zig 0.15.2 can cross-link the universal + # (arm64 + x86_64) Ghostty CLI helper only against a pre-26 SDK. The Swift + # app is built with the macOS 26 Xcode on this same runner so it adopts + # Liquid Glass on Tahoe; the helper is built separately with the older Xcode + # and injected before signing (see Select Xcode + the helper build/inject + # steps). Building the whole app with Xcode 16 (#5022) shipped a macOS 15 SDK + # binary that Tahoe forced into the legacy non-Liquid-Glass appearance. runs-on: ${{ vars.MACOS_RUNNER_15 || 'warp-macos-15-arm64-6x' }} - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout build ref uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -137,26 +141,46 @@ jobs: if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' run: | set -euo pipefail - if [ -d "/Applications/Xcode.app/Contents/Developer" ]; then - XCODE_DIR="/Applications/Xcode.app/Contents/Developer" - else - XCODE_APP="$( - find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null \ - | sort \ - | tail -n 1 \ - || true - )" - if [ -n "$XCODE_APP" ]; then - XCODE_DIR="$XCODE_APP/Contents/Developer" + # The app must link against the macOS 26 SDK so it adopts Liquid Glass + # on Tahoe (the team is pinned to Xcode 26 via .xcode-version). The zig + # Ghostty CLI helper must link against a pre-26 SDK because zig 0.15.2 + # cannot cross-link x86_64 against the macOS 26 SDK. Both Xcodes ship on + # the macOS 15 runner image, so pick one of each by SDK major. + APP_DEVELOPER_DIR="" + APP_SDK_MAJOR="" + HELPER_DEVELOPER_DIR="" + HELPER_SDK_MAJOR="" + 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 + major="${sdk_ver%%.*}" + echo "Found $app -> macOS SDK $sdk_ver" + if [ "$major" -ge 26 ]; then + APP_DEVELOPER_DIR="$dev" + APP_SDK_MAJOR="$major" else - echo "No Xcode.app found under /Applications" >&2 - exit 1 + HELPER_DEVELOPER_DIR="$dev" + HELPER_SDK_MAJOR="$major" fi + done < <(find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null | sort) + + if [ -z "$APP_DEVELOPER_DIR" ]; then + echo "No Xcode with the macOS 26+ SDK found; the app would ship without Liquid Glass on Tahoe." >&2 + exit 1 + fi + if [ -z "$HELPER_DEVELOPER_DIR" ]; then + echo "No pre-26 Xcode found to cross-link the universal Ghostty CLI helper (zig 0.15.2 cannot link x86_64 against the macOS 26 SDK)." >&2 + exit 1 fi - echo "DEVELOPER_DIR=$XCODE_DIR" >> "$GITHUB_ENV" - export DEVELOPER_DIR="$XCODE_DIR" - xcodebuild -version - xcrun --sdk macosx --show-sdk-path + + echo "App build Xcode (DEVELOPER_DIR): $APP_DEVELOPER_DIR (macOS SDK $APP_SDK_MAJOR)" + echo "Helper build Xcode (HELPER_DEVELOPER_DIR): $HELPER_DEVELOPER_DIR (macOS SDK $HELPER_SDK_MAJOR)" + DEVELOPER_DIR="$APP_DEVELOPER_DIR" xcodebuild -version + echo "DEVELOPER_DIR=$APP_DEVELOPER_DIR" >> "$GITHUB_ENV" + echo "HELPER_DEVELOPER_DIR=$HELPER_DEVELOPER_DIR" >> "$GITHUB_ENV" - name: Install build deps if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' @@ -165,6 +189,19 @@ jobs: ./scripts/install-zig-ci.sh npm install --global "create-dmg@${CREATE_DMG_VERSION}" + - name: Build universal Ghostty CLI helper + if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' + env: + # zig 0.15.2 can cross-link the x86_64 slice only against a pre-26 SDK. + DEVELOPER_DIR: ${{ env.HELPER_DEVELOPER_DIR }} + run: | + set -euo pipefail + ./scripts/build-ghostty-cli-helper.sh --universal --output /tmp/cmux-ghostty-helper-universal + ARCHS_OUT="$(lipo -archs /tmp/cmux-ghostty-helper-universal)" + echo "Universal Ghostty CLI helper architectures: $ARCHS_OUT" + case " $ARCHS_OUT " in *" arm64 "*) ;; *) echo "helper missing arm64 slice" >&2; exit 1 ;; esac + case " $ARCHS_OUT " in *" x86_64 "*) ;; *) echo "helper missing x86_64 slice" >&2; exit 1 ;; esac + - name: Download pre-built GhosttyKit.xcframework if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' run: | @@ -199,6 +236,11 @@ jobs: - name: Build universal nightly app (Release) if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' + env: + # Skip the in-Xcode zig helper build; it would fail to cross-link + # x86_64 against the macOS 26 SDK. The real universal helper is built + # by the "Build universal Ghostty CLI helper" step and injected below. + CMUX_SKIP_ZIG_BUILD: "1" run: | xcodebuild -scheme cmux -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ @@ -207,6 +249,18 @@ jobs: ONLY_ACTIVE_ARCH=NO \ CODE_SIGNING_ALLOWED=NO ASSETCATALOG_COMPILER_APPICON_NAME=AppIcon-Nightly build + - name: Inject universal Ghostty CLI helper + if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' + run: | + set -euo pipefail + DEST="build-universal/Build/Products/Release/cmux.app/Contents/Resources/bin/ghostty" + if [ ! -e "$DEST" ]; then + echo "Expected Ghostty helper slot missing at $DEST" >&2 + exit 1 + fi + install -m 755 /tmp/cmux-ghostty-helper-universal "$DEST" + echo "Injected Ghostty CLI helper architectures: $(lipo -archs "$DEST")" + - name: Run bundled Ghostty theme picker helper regression if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' run: | From 6ed048f0d522a0f56d499f83ffde765372ed14c1 Mon Sep 17 00:00:00 2001 From: Aziz Albahar Date: Sun, 31 May 2026 21:10:27 -0500 Subject: [PATCH 2/2] Address review: numeric SDK comparison + robust helper inject - Select Xcode by numeric SDK rank instead of lexicographic sort, so 26.10 ranks above 26.3 and the newest SDK in each slot (>=26 for the app, <26 for the zig helper) is chosen deterministically. - Inject step now mkdir -p's the bin directory and installs, instead of erroring when the slot is absent; the build phase still writes the stub when CMUX_SKIP_ZIG_BUILD=1. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/nightly.yml | 49 ++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 66ad23ec5c14..8a60c85d3456 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -145,27 +145,38 @@ jobs: # on Tahoe (the team is pinned to Xcode 26 via .xcode-version). The zig # Ghostty CLI helper must link against a pre-26 SDK because zig 0.15.2 # cannot cross-link x86_64 against the macOS 26 SDK. Both Xcodes ship on - # the macOS 15 runner image, so pick one of each by SDK major. - APP_DEVELOPER_DIR="" - APP_SDK_MAJOR="" - HELPER_DEVELOPER_DIR="" - HELPER_SDK_MAJOR="" + # the macOS 15 runner image, so pick the newest of each by SDK version. + # Compare SDK versions numerically (not lexicographically) so e.g. + # 26.10 sorts above 26.3. + sdk_rank() { + # "26.2" -> 26002 ; "26" -> 26000 + local v="$1" maj min + maj="${v%%.*}" + min="${v#*.}" + [ "$min" = "$v" ] && min=0 + min="${min%%.*}" + printf '%d' "$(( maj * 1000 + min ))" + } + APP_DEVELOPER_DIR=""; APP_SDK_VER=""; APP_RANK=-1 + HELPER_DEVELOPER_DIR=""; HELPER_SDK_VER=""; HELPER_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 - major="${sdk_ver%%.*}" - echo "Found $app -> macOS SDK $sdk_ver" - if [ "$major" -ge 26 ]; then - APP_DEVELOPER_DIR="$dev" - APP_SDK_MAJOR="$major" + rank="$(sdk_rank "$sdk_ver")" + echo "Found $app -> macOS SDK $sdk_ver (rank $rank)" + if [ "${sdk_ver%%.*}" -ge 26 ]; then + if [ "$rank" -gt "$APP_RANK" ]; then + APP_DEVELOPER_DIR="$dev"; APP_SDK_VER="$sdk_ver"; APP_RANK="$rank" + fi else - HELPER_DEVELOPER_DIR="$dev" - HELPER_SDK_MAJOR="$major" + if [ "$rank" -gt "$HELPER_RANK" ]; then + HELPER_DEVELOPER_DIR="$dev"; HELPER_SDK_VER="$sdk_ver"; HELPER_RANK="$rank" + fi fi - done < <(find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null | sort) + done < <(find /Applications -maxdepth 1 -name 'Xcode*.app' -print 2>/dev/null) if [ -z "$APP_DEVELOPER_DIR" ]; then echo "No Xcode with the macOS 26+ SDK found; the app would ship without Liquid Glass on Tahoe." >&2 @@ -176,8 +187,8 @@ jobs: exit 1 fi - echo "App build Xcode (DEVELOPER_DIR): $APP_DEVELOPER_DIR (macOS SDK $APP_SDK_MAJOR)" - echo "Helper build Xcode (HELPER_DEVELOPER_DIR): $HELPER_DEVELOPER_DIR (macOS SDK $HELPER_SDK_MAJOR)" + echo "App build Xcode (DEVELOPER_DIR): $APP_DEVELOPER_DIR (macOS SDK $APP_SDK_VER)" + echo "Helper build Xcode (HELPER_DEVELOPER_DIR): $HELPER_DEVELOPER_DIR (macOS SDK $HELPER_SDK_VER)" DEVELOPER_DIR="$APP_DEVELOPER_DIR" xcodebuild -version echo "DEVELOPER_DIR=$APP_DEVELOPER_DIR" >> "$GITHUB_ENV" echo "HELPER_DEVELOPER_DIR=$HELPER_DEVELOPER_DIR" >> "$GITHUB_ENV" @@ -253,11 +264,13 @@ jobs: if: needs.decide.outputs.should_publish != 'true' || steps.current_head_prebuild.outputs.still_current == 'true' run: | set -euo pipefail - DEST="build-universal/Build/Products/Release/cmux.app/Contents/Resources/bin/ghostty" - if [ ! -e "$DEST" ]; then - echo "Expected Ghostty helper slot missing at $DEST" >&2 + APP_DIR="build-universal/Build/Products/Release/cmux.app" + if [ ! -d "$APP_DIR" ]; then + echo "Built app not found at $APP_DIR" >&2 exit 1 fi + DEST="$APP_DIR/Contents/Resources/bin/ghostty" + mkdir -p "$(dirname "$DEST")" install -m 755 /tmp/cmux-ghostty-helper-universal "$DEST" echo "Injected Ghostty CLI helper architectures: $(lipo -archs "$DEST")"