From 8da11c441594cdc0648035fa44d2ffde01f61ba6 Mon Sep 17 00:00:00 2001 From: arzafran Date: Thu, 9 Jul 2026 18:50:40 -0300 Subject: [PATCH] perf(release): reuse CI's GhosttyKit cache and build the app arm64-only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-ship lane rebuilt GhosttyKit from source on every ship (~14 min) because the prebuilt xcframework release has never actually published — the cross-repo upload silently exits 0 when its token is empty, so there are zero releases on the ghostty fork despite the pinned checksums. Instead of depending on that broken pipeline, release.yml now restores the same xcframework cache CI already builds (same ghostty-SHA key, same commit via workflow_run), and only falls back to the download/source-build on a cache miss. Also drop the Intel slice: the shipped Mac app and the GhosttyKit build are now arm64-only (native), halving the compile when it does run. Remote-daemon server binaries stay cross-platform. The ranlib-refresh workaround now matches whichever macos slice exists so it keeps running on the arm64 path. --- .github/workflows/build-ghosttykit.yml | 2 +- .github/workflows/release.yml | 18 +++++++---- scripts/ensure-ghosttykit.sh | 19 ++++++++---- tests/test_ci_universal_release_settings.sh | 33 +++++++++++++++++---- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build-ghosttykit.yml b/.github/workflows/build-ghosttykit.yml index 850bab3300f..c1454a8895b 100644 --- a/.github/workflows/build-ghosttykit.yml +++ b/.github/workflows/build-ghosttykit.yml @@ -85,7 +85,7 @@ jobs: export PATH="/usr/local/bin:$PATH" zig version fi - cd ghostty && zig build -Demit-xcframework=true -Demit-macos-app=false -Dxcframework-target=universal -Doptimize=ReleaseFast + cd ghostty && zig build -Demit-xcframework=true -Demit-macos-app=false -Dxcframework-target=native -Doptimize=ReleaseFast - name: Package xcframework if: steps.check-release.outputs.exists == 'false' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d312b557d7a..c44a2fd5b43 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -198,8 +198,16 @@ jobs: fi npm install --global "create-dmg@${CREATE_DMG_VERSION}" - - name: Download pre-built GhosttyKit.xcframework + - name: Cache GhosttyKit.xcframework + id: cache-ghosttykit-release if: steps.guard_release_assets.outputs.skip_all != 'true' + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4 + with: + path: GhosttyKit.xcframework + key: ghosttykit-v2-${{ hashFiles('.gitmodules', 'ghostty') }} + + - name: Download pre-built GhosttyKit.xcframework + if: steps.guard_release_assets.outputs.skip_all != 'true' && steps.cache-ghosttykit-release.outputs.cache-hit != 'true' run: | ./scripts/download-prebuilt-ghosttykit.sh @@ -236,7 +244,7 @@ jobs: xcodebuild -scheme programa -configuration Release -derivedDataPath build-universal \ -destination 'generic/platform=macOS' \ -clonedSourcePackagesDirPath .spm-cache \ - ARCHS="arm64 x86_64" \ + ARCHS="arm64" \ ONLY_ACTIVE_ARCH=NO \ CODE_SIGNING_ALLOWED=NO build @@ -253,9 +261,9 @@ jobs: echo "App binary architectures: $APP_ARCHS" echo "CLI binary architectures: $CLI_ARCHS" echo "Ghostty helper architectures: $HELPER_ARCHS" - [[ "$APP_ARCHS" == *arm64* && "$APP_ARCHS" == *x86_64* ]] - [[ "$CLI_ARCHS" == *arm64* && "$CLI_ARCHS" == *x86_64* ]] - [[ "$HELPER_ARCHS" == *arm64* && "$HELPER_ARCHS" == *x86_64* ]] + [[ "$APP_ARCHS" == "arm64" ]] + [[ "$CLI_ARCHS" == "arm64" ]] + [[ "$HELPER_ARCHS" == "arm64" ]] - name: Build remote daemon release assets and inject manifest if: steps.guard_release_assets.outputs.skip_all != 'true' diff --git a/scripts/ensure-ghosttykit.sh b/scripts/ensure-ghosttykit.sh index 8e9b13bf8ce..2d2299eb79b 100755 --- a/scripts/ensure-ghosttykit.sh +++ b/scripts/ensure-ghosttykit.sh @@ -120,7 +120,7 @@ else echo "==> Building GhosttyKit.xcframework (this may take a few minutes)..." ( cd ghostty - zig build -Demit-xcframework=true -Demit-macos-app=false -Dxcframework-target=universal -Doptimize=ReleaseFast + zig build -Demit-xcframework=true -Demit-macos-app=false -Dxcframework-target=native -Doptimize=ReleaseFast ) echo "$GHOSTTY_KEY" > "$LOCAL_KEY_STAMP" echo "$GHOSTTY_SHA" > "$LEGACY_LOCAL_SHA_STAMP" @@ -140,10 +140,19 @@ else echo "==> Cached GhosttyKit.xcframework at $CACHE_XCFRAMEWORK" fi -MACOS_ARCHIVE="$CACHE_XCFRAMEWORK/macos-arm64_x86_64/libghostty.a" -if [[ -f "$MACOS_ARCHIVE" ]]; then - # Xcode 26 can fail to resolve symbols from Ghostty's universal static archive - # until its ranlib index is refreshed after reuse or copy. +# The macos slice dir is named by its arch(s): macos-arm64 (native, arm64-only) or +# macos-arm64_x86_64 (universal) — match whichever exists. A non-matching glob leaves +# the literal pattern, which the -f guard rejects, so this is safe under `set -e`. +MACOS_ARCHIVE="" +for _macos_archive in "$CACHE_XCFRAMEWORK"/macos-*/libghostty.a; do + if [[ -f "$_macos_archive" ]]; then + MACOS_ARCHIVE="$_macos_archive" + break + fi +done +if [[ -n "$MACOS_ARCHIVE" ]]; then + # Xcode 26 can fail to resolve symbols from Ghostty's static archive until its + # ranlib index is refreshed after reuse or copy. echo "==> Refreshing libghostty archive index..." if ! command -v xcrun >/dev/null 2>&1; then echo "error: xcrun is required to refresh libghostty archive index." >&2 diff --git a/tests/test_ci_universal_release_settings.sh b/tests/test_ci_universal_release_settings.sh index c374c6fbf93..a4b94ed5faa 100644 --- a/tests/test_ci_universal_release_settings.sh +++ b/tests/test_ci_universal_release_settings.sh @@ -1,15 +1,38 @@ #!/usr/bin/env bash -# Regression test for universal GhosttyKit and Release build settings. +# Regression test for arm64-only GhosttyKit and Release build settings. set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" for file in \ "$ROOT_DIR/.github/workflows/build-ghosttykit.yml" \ - "$ROOT_DIR/scripts/setup.sh" + "$ROOT_DIR/scripts/ensure-ghosttykit.sh" do - if ! grep -Fq -- '-Dxcframework-target=universal' "$file"; then - echo "FAIL: $file must build GhosttyKit with -Dxcframework-target=universal" + if ! grep -Fq -- '-Dxcframework-target=native' "$file"; then + echo "FAIL: $file must build GhosttyKit with -Dxcframework-target=native (arm64-only)" + exit 1 + fi + if grep -Fq -- '-Dxcframework-target=universal' "$file"; then + echo "FAIL: $file must not build GhosttyKit as universal" + exit 1 + fi +done + +RELEASE_YML="$ROOT_DIR/.github/workflows/release.yml" + +if ! grep -Fq 'ARCHS="arm64"' "$RELEASE_YML"; then + echo "FAIL: release.yml must build the Release app with ARCHS=\"arm64\" only" + exit 1 +fi + +if grep -Fq 'ARCHS="arm64 x86_64"' "$RELEASE_YML"; then + echo "FAIL: release.yml must not build the Release app as universal (arm64 x86_64)" + exit 1 +fi + +for var in APP_ARCHS CLI_ARCHS HELPER_ARCHS; do + if ! grep -Fq "[[ \"\$${var}\" == \"arm64\" ]]" "$RELEASE_YML"; then + echo "FAIL: release.yml must verify \$${var} is exactly arm64 (single-arch)" exit 1 fi done @@ -25,4 +48,4 @@ if ! awk ' exit 1 fi -echo "PASS: GhosttyKit builds universal and Release configs disable ONLY_ACTIVE_ARCH" +echo "PASS: GhosttyKit builds arm64-only and Release configs build/verify arm64-only"