Skip to content

ci: build Apple lanes with Xcode 27.1 - #1266

Draft
AnthonyRonning wants to merge 4 commits into
research/ios-scene-lifecyclefrom
ci/xcode-27.1
Draft

AnthonyRonning wants to merge 4 commits into
research/ios-scene-lifecyclefrom
ci/xcode-27.1

Conversation

@AnthonyRonning

@AnthonyRonning AnthonyRonning commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Why

Move every Apple lane to Xcode 27.1 (27A9275), the release that adds the iOS 27.1 SDK and iPhone Duo support. App Store Connect requires the iOS 27 SDK from April 2027. Top of the stack: it needs PR #1264 (Tauri 2.12) and PR #1265 (UIScene adoption), because iOS 27 SDK builds abort at launch without scenes.

What

  • scripts/ci/apple-toolchain.json pins Xcode 27.1 / 27A9275. This flows into select-xcode.py, the root Nix shell and the cache keys.
  • Runners move to the Xcode 27 images.
    • blacksmith-6vcpu-macos-26 → blacksmith-6vcpu-macos-27 for builders.
    • GitHub macos-26 → xcode-27 for verifiers, signing, TestFlight and Agent CI. GitHub's macos-26 image has no Xcode 27.
  • select-xcode.py also considers suffixed installs such as Xcode_27.1_Release_Candidate.app. That is where GitHub's xcode-27 image keeps 27A9275 today, and where its default is still 27.0. Selection still requires the exact pinned build and still skips betas. Tests cover both cases.
  • Agent CI's macOS jobs (agent-ci desktop/provider, store performance) now select the pinned Xcode too, instead of the runner default.
  • The workflow lint now rejects macos-14/macos-26 labels. The actionlint config lists blacksmith-6vcpu-macos-27 and xcode-27, which the pinned actionlint doesn't know yet.
  • The Agent dev shell links Rust with Xcode's clang too, not nixpkgs' clang wrapper. Its ld64 can't read the macOS 27 SDK's .tbd stubs (they list the new arm64e.x1 architecture), so every Rust build script failed to link with _strerror_r undefined.
  • test-canonical-macho.py: Xcode 27's ld sorts local absolute symbols, so the two fixtures came out identical. The test now swaps the entries to recreate the unsorted variant older linkers emitted, and still checks that the canonical hash strips them.
  • Research and Agent Cargo.toml: release builds no longer strip build scripts and proc-macros ([profile.release.build-override] strip = false). The macOS release builds failed with can't find crate for zerofrom_derive, because dyld refused to load the proc-macro ("mis-aligned LINKEDIT string pool").
    • rustc strips Apple outputs with its bundled rust-objcopy, which doesn't pad __LINKEDIT entries. For images with chained fixups (deployment target 12+, so the desktop builds' 13.4 and the Agent's 15.0), that can leave the string table only 4-byte aligned.
    • macOS 27's dyld rejects such dylibs only when they're linked against the macOS 27 SDK. The same bytes stamped sdk 26.5 load fine, which is why this never showed up on Xcode 26.5.
    • Executables aren't checked, so shipped binaries are unaffected and keep their current strip settings. Xcode's own strip keeps the table aligned.
    • Upstream fix: llvm/llvm-project#203680 makes llvm-objcopy align __LINKEDIT entries. Drop the setting once the pinned Rust ships an objcopy with it.
  • The Agent's macOS release binary now builds on GitHub's xcode-27 image instead of Blacksmith, as it did before PR #1119. gpui compiles Metal shaders, and Blacksmith's macOS 27 image has no Metal Toolchain for Xcode 27.1. xcodebuild -downloadComponent MetalToolchain fails there on every attempt ("Failed fetching catalog"), while GitHub's image ships the component.
    • The dev build took 40 min on GitHub's runner, against 12–15 min on Blacksmith, so Agent macOS builds are slower until the job moves back.
  • The iOS ONNX Runtime build targets iOS 16.0 (the app's minimum) instead of 13.0. Xcode 27 only builds for deployment targets 15.0 to 27.1, so the 13.0 configure step fails. The ONNX hash pins move to 27A9275, from a local build on Xcode 27.1.
  • Docs updated: repository-workflows, the store-bench label, and the StoreKit harness commands, which pinned the removed Xcode 26.6.

Validation

  • CI passes on this PR: the Research builds for every platform, Agent CI on xcode-27, and the Agent's macOS release build on xcode-27. One build-macos attempt failed because a Blacksmith VM had not mounted its Xcode 27.1 volume; the rerun passed.
  • python3 -m unittest discover -s scripts/ci: 569 pass. This includes the new selector cases, and the signing-boundary test now expects GitHub-hosted xcode-27.
  • The workflows flake check (actionlint plus the workflow tests) passes. test-canonical-macho.py passes under Xcode 26.5 and 27.1.
  • Agent with Xcode 27.1: cargo build --workspace --all-targets --locked passes in the Agent dev shell, including gpui's Metal shaders and every build-script link. scripts/test_git_metadata.py passes (9 tests).
  • Research macOS release build with Xcode 27.1: cargo build --release --target aarch64-apple-darwin with the desktop script's environment (deployment target 13.4) passes. Without the build-override change it fails exactly as CI did. The shipped maple executable is still stripped.
  • The dyld check, on a macOS 27 host: the failing proc-macro loads after Xcode's strip but not after rust-objcopy. The same dylib loads when stamped sdk 26.5 and is refused when re-stamped sdk 27.0. Stripped executables with the misaligned table still run.
  • iOS ONNX Runtime 1.23.2, rebuilt locally with Xcode 27.1 (27A9275) for iOS 16.0. Every compile used Xcode 27.1, with no 26.5 paths.
    • Device library e4d28818…, simulator library 0b670a0f…, xcframework 93cbb699…. The manifest hash and tree hash agree.
    • verify_ios_onnxruntime_manifest passes with the new pins.
    • Local builds reproduced CI's 17F42 pins before, so CI should reproduce these.
  • Full stack built locally with Xcode 27.1: debug and release simulator apps, DTSDKName iphonesimulator27.1, LC_BUILD_VERSION sdk 27.1. The release link is the proof for the public @_cdecl entry points in PR #1265.
  • iPhone Duo simulator (Xcode 27.1, iOS 27.1): full screen on both displays.
    • The debug app survived closed → open → rotate → closed in landscape → partially open → cold launch while open → closed with no crash. The release app survived open, rotate and cold launch while open with no crash.
    • The WebView resizes live and switches between phone (382 pt outer, 669 pt inner portrait) and desktop (867 pt inner landscape) layouts without reloading.
    • Cold and warm deep links are delivered. The same holds on iPhone 17 Pro / iOS 26.5 and iPhone 18 Pro / iOS 27.0.
  • Not exercisable before merge: signing, TestFlight upload and the master-only release lanes. The first master run is the real check on the new runners.

Heads-up

  • The native Swift app branch (PR #1152, Mark's iOS rewrite) has its own workflows. When it rebases, the new label lint flags any macos-26 runners there.
  • Local machines need Xcode 27.1 selected globally (sudo xcode-select -s /Applications/Xcode_27.1.app). tauri ios build runs xcodebuild with a replaced environment, so it ignores DEVELOPER_DIR. CI already switches globally.
  • To move the Agent build back to Blacksmith, their macos-27 image needs the Metal Toolchain for Xcode 27.1 (27A266a) preinstalled, or access to Apple's MobileAsset catalog.
  • Agent builds also need 27.1's own Metal Toolchain: xcodebuild -downloadComponent MetalToolchain with 27.1 selected. The component is installed per Xcode version, so the one installed for 26.5 doesn't count.

🤖 Generated with Claude Code

@AnthonyRonning AnthonyRonning changed the title ci/xcode 27.1 ci: build Apple lanes with Xcode 27.1 Oct 25, 2026
AnthonyRonning and others added 3 commits October 25, 2026 00:39
GitHub's xcode-27 image keeps Xcode 27.1 (27A9275) at
/Applications/Xcode_27.1_Release_Candidate.app, and its default is 27.0. Also
consider Xcode_<version>_*.app after the exact names; selection still
requires the pinned build and still skips beta installs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Xcode 27.1 (27A9275) brings the iOS 27.1 SDK and iPhone Duo support, and
App Store Connect requires the iOS 27 SDK from April 2027.

- apple-toolchain.json pins 27.1 / 27A9275 for the selector, the Nix shell
  and the cache keys.
- Builders move to blacksmith-6vcpu-macos-27 and GitHub-hosted jobs to
  xcode-27; the macos-26 images have no Xcode 27. Signing stays on
  GitHub-hosted runners.
- Agent CI's macOS jobs select the pinned Xcode instead of the runner
  default (27.0 on the xcode-27 image); the workflow lint now rejects
  macos-14 and macos-26 labels.
- The iOS ONNX Runtime build targets iOS 16.0 like the app: Xcode 27 only
  builds for deployment targets 15.0 and later. Its hash pins move to
  27A9275 from a local Xcode 27.1 build, and the dead 17F42 defaults go.
- Docs: runner labels and the pin; the StoreKit harness commands no longer
  select the removed Xcode 26.6. Drops the stale project.pbxproj.backup,
  which still targeted iOS 13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- actionlint: allow the blacksmith-6vcpu-macos-27 and xcode-27 labels,
  which the pinned actionlint predates.
- test-canonical-macho: Xcode 27's ld sorts local absolute symbols, so
  both fixtures came out in the same order. Swap the entries to recreate
  the input-order variant older linkers emitted.
- Agent shell: link Rust with Xcode's clang as well. nixpkgs' ld64
  cannot read the macOS 27 SDK's .tbd stubs (they list arm64e.x1), so
  build scripts failed to link (_strerror_r undefined).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AnthonyRonning
AnthonyRonning force-pushed the ci/xcode-27.1 branch 2 times, most recently from 69c8143 to 8cd7644 Compare October 25, 2026 01:21
- Research and Agent: stop stripping build scripts and proc-macros in
  release builds. rustc strips Apple outputs with rust-objcopy, which
  doesn't pad __LINKEDIT entries, so chained-fixup images (deployment
  target 12+) can end up with a 4-byte-aligned string table. macOS 27's
  dyld refuses such dylibs once they're linked against the macOS 27 SDK,
  so rustc could not load zerofrom-derive ("can't find crate").
  Executables aren't checked, so shipped binaries keep their current
  strip settings.
- Agent desktop build: build the macOS binary on GitHub's xcode-27
  image. gpui compiles Metal shaders, and Blacksmith's macOS 27 image has
  no Metal Toolchain for Xcode 27.1 and cannot download it (the
  MobileAsset catalog fetch fails on every attempt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@AnthonyRonning

Copy link
Copy Markdown
Contributor Author

Heads-up for whoever picks this up: the Agent's macOS builds don't need to move to GitHub's xcode-27 runner. Blacksmith's macOS 27 image works once the Metal Toolchain is imported.

Evidence from the experiment in #1308 (draft, based on this branch, not for merge), run 40415895366:

  • Export: on GitHub xcode-27, xcodebuild -downloadComponent metalToolchain -exportPath … produced MetalToolchain-27A266a.exportedBundle (865 MB) in 1 s.
  • Import: on blacksmith-6vcpu-macos-27, the artifact download took about 47 s. xcodebuild -importComponent metalToolchain -importPath <bundle> then took 13 s, and xcrun metal compiled a probe shader (Apple metal 32023.921).
  • Full build: a cold cargo build --workspace --all-targets --locked of the Agent, with compiled Metal shaders, took 12m44s, plus 54 s for the Nix shell. That compares with the ~40 min on xcode-27 noted in this PR.
  • Check lanes can skip Metal entirely: with no toolchain on the runner, cargo clippy -p maple-agent-app --all-targets --locked -F gpui-platform/runtime_shaders -- -D warnings ran cold in 3m51s on Blacksmith macOS 27.

Suggested shape when this rebases:

  • Keep the unsigned Agent builders on blacksmith-6vcpu-macos-27.
  • Get the bundle from a short export job on xcode-27, or cache the exported bundle on Blacksmith keyed by the toolchain build (27A266a). Import it before building.
  • Signing, attest and verify jobs stay GitHub-hosted, as today.

The workflow is .github/workflows/macos27-blacksmith-experiment.yml on exp/macos27-blacksmith-metal.

Not urgent: CI is being rewritten in a single PR first, and this PR should rebase after that lands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant