ci: build macOS releases with Xcode 26

Resolves #7591

This moves our CI to build macOS on Sequoia (macOS 15) with Xcode 26,
including the new macOS 26 beta SDK.

Importantly, this will make our builds on macOS 26 use the new styling.

I've added a new job that ensures we can continue to build with Xcode 16 and
the macOS 15 SDK, as well, although I think that might come to an end
when we switch over to an IconComposer-based icon. I'll verify then. For
now, we continue to support both.

I've also removed our `hasLiquidGlass` check, since this will now always
be true for macOS 26 builds.
pull/7616/head
Mitchell Hashimoto 2025-06-17 13:34:18 -07:00
parent f794693bdc
commit 6d283c012e
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
7 changed files with 56 additions and 58 deletions

View File

@ -94,7 +94,7 @@ jobs:
- name: Build Ghostty.app
run: |
cd macos
sudo xcode-select -s /Applications/Xcode_16.4.app
sudo xcode-select -s /Applications/Xcode_26.0.app
xcodebuild -target Ghostty -configuration Release
# We inject the "build number" as simply the number of commits since HEAD.
@ -246,7 +246,7 @@ jobs:
- name: Build Ghostty.app
run: |
cd macos
sudo xcode-select -s /Applications/Xcode_16.4.app
sudo xcode-select -s /Applications/Xcode_26.0.app
xcodebuild -target Ghostty -configuration Release
# We inject the "build number" as simply the number of commits since HEAD.

View File

@ -173,7 +173,7 @@ jobs:
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.4.app
run: sudo xcode-select -s /Applications/Xcode_26.0.app
# Setup Sparkle
- name: Setup Sparkle
@ -388,7 +388,7 @@ jobs:
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.4.app
run: sudo xcode-select -s /Applications/Xcode_26.0.app
# Setup Sparkle
- name: Setup Sparkle
@ -563,7 +563,7 @@ jobs:
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: XCode Select
run: sudo xcode-select -s /Applications/Xcode_16.4.app
run: sudo xcode-select -s /Applications/Xcode_26.0.app
# Setup Sparkle
- name: Setup Sparkle

View File

@ -18,6 +18,7 @@ jobs:
- build-nix
- build-snap
- build-macos
- build-macos-sequoia-stable
- build-macos-tahoe
- build-macos-matrix
- build-windows
@ -270,6 +271,46 @@ jobs:
ghostty-source.tar.gz
build-macos:
runs-on: namespace-profile-ghostty-macos-sequoia
needs: test
steps:
- name: Checkout code
uses: actions/checkout@v4
# Install Nix and use that to run our tests so our environment matches exactly.
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v16
with:
name: ghostty
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Xcode Select
run: sudo xcode-select -s /Applications/Xcode_26.0.app
- name: get the Zig deps
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
# GhosttyKit is the framework that is built from Zig for our native
# Mac app to access.
- name: Build GhosttyKit
run: nix develop -c zig build --system ${{ steps.deps.outputs.deps }}
# The native app is built with native Xcode tooling. This also does
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
# Nix breaks xcodebuild so this has to be run outside.
- name: Build Ghostty.app
run: cd macos && xcodebuild -target Ghostty
# Build the iOS target without code signing just to verify it works.
- name: Build Ghostty iOS
run: |
cd macos
xcodebuild -target Ghostty-iOS "CODE_SIGNING_ALLOWED=NO"
build-macos-sequoia-stable:
runs-on: namespace-profile-ghostty-macos-sequoia
needs: test
steps:
@ -328,17 +369,6 @@ jobs:
- name: Xcode Select
run: sudo xcode-select -s /Applications/Xcode_26.0.app
# TODO(tahoe):
# https://developer.apple.com/documentation/xcode-release-notes/xcode-26-release-notes#Interface-Builder
# We allow this step to fail because if our image already has
# the workaround in place this will fail.
- name: Xcode 26 Beta 17A5241e Metal Workaround
continue-on-error: true
run: |
xcodebuild -downloadComponent metalToolchain -exportPath /tmp/MyMetalExport/
sed -i '' -e 's/17A5241c/17A5241e/g' /tmp/MyMetalExport/MetalToolchain-17A5241c.exportedBundle/ExportMetadata.plist
xcodebuild -importComponent metalToolchain -importPath /tmp/MyMetalExport/MetalToolchain-17A5241c.exportedBundle
- name: get the Zig deps
id: deps
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
@ -377,7 +407,7 @@ jobs:
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Xcode Select
run: sudo xcode-select -s /Applications/Xcode_16.4.app
run: sudo xcode-select -s /Applications/Xcode_26.0.app
- name: get the Zig deps
id: deps
@ -695,7 +725,7 @@ jobs:
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
- name: Xcode Select
run: sudo xcode-select -s /Applications/Xcode_16.4.app
run: sudo xcode-select -s /Applications/Xcode_26.0.app
- name: get the Zig deps
id: deps

View File

@ -16,7 +16,7 @@ class TerminalController: BaseTerminalController {
case "hidden": "TerminalHiddenTitlebar"
case "transparent": "TerminalTransparentTitlebar"
case "tabs":
if #available(macOS 26.0, *), hasLiquidGlass() {
if #available(macOS 26.0, *) {
"TerminalTabsTitlebarTahoe"
} else {
"TerminalTabsTitlebarVentura"

View File

@ -447,7 +447,7 @@ extension TerminalWindow {
// The padding from the top that the view appears. This was all just manually
// measured based on the OS.
var topPadding: CGFloat {
if #available(macOS 26.0, *), hasLiquidGlass() {
if #available(macOS 26.0, *) {
return viewModel.hasToolbar ? 10 : 5
} else {
return viewModel.hasToolbar ? 9 : 4

View File

@ -45,11 +45,13 @@ class TransparentTitlebarTerminalWindow: TerminalWindow {
override func update() {
super.update()
// On macOS 13 to 15, we need to hide the NSVisualEffectView in order to allow our
// titlebar to be truly transparent.
if !effectViewIsHidden && !hasLiquidGlass() {
hideEffectView()
if #unavailable(macOS 26) {
if !effectViewIsHidden {
hideEffectView()
}
}
}
@ -65,7 +67,7 @@ class TransparentTitlebarTerminalWindow: TerminalWindow {
// references changed (e.g. tabGroup is new).
setupKVO()
if #available(macOS 26.0, *), hasLiquidGlass() {
if #available(macOS 26.0, *) {
syncAppearanceTahoe(surfaceConfig)
} else {
syncAppearanceVentura(surfaceConfig)

View File

@ -8,37 +8,3 @@ func isRunningInXcode() -> Bool {
return false
}
/// True if we have liquid glass available.
func hasLiquidGlass() -> Bool {
// Can't have liquid glass unless we're in macOS 26+
if #unavailable(macOS 26.0) {
return false
}
// If we aren't running SDK 26.0 or later then we definitely
// do not have liquid glass.
guard let sdkName = Bundle.main.infoDictionary?["DTSDKName"] as? String else {
// If we don't have this, we assume we're built against the latest
// since we're on macOS 26+
return true
}
// If the SDK doesn't start with macosx then we just assume we
// have it because we already verified we're on macOS above.
guard sdkName.hasPrefix("macosx") else {
return true
}
// The SDK version must be at least 26
let versionString = String(sdkName.dropFirst("macosx".count))
guard let major = if let dotIndex = versionString.firstIndex(of: ".") {
Int(String(versionString[..<dotIndex]))
} else {
Int(versionString)
} else { return true }
// Note: we could also check for the UIDesignRequiresCompatibility key
// but our project doesn't use it so there's no point.
return major >= 26
}