* Cache Swift packages across CI runs
Add actions/cache for the SPM cloned source packages directory so
subsequent runs skip fetching Sparkle, sentry-cocoa, swift-markdown-ui,
posthog-ios, and NetworkImage from GitHub each time.
- nightly/release: replace the no-op SwiftPM cache step with
actions/cache + -clonedSourcePackagesDirPath on xcodebuild
- ci/ci-macos-compat/test-e2e: add actions/cache before the existing
resolve step, stop deleting the cache dir each run
* Include runner in test-e2e cache key
Consistent with ci-macos-compat.yml which uses matrix.os in the key.
* Migrate all workflows from self-hosted Mac Mini to Depot runners
Move CI, nightly, and release workflows to depot-macos-latest. Replace
zig GhosttyKit builds with pre-built xcframework downloads. Add virtual
display for CI UI tests. Remove concurrency groups (ephemeral VMs don't
need them).
* Add per-test timeout to CI UI tests to prevent hangs on Depot
SidebarResizeUITests hangs on headless Depot runners due to mouse drag
simulation issues. Adding -maximum-test-execution-time-allowance 120
(matching test-depot.yml) ensures individual tests timeout after 2 min
instead of blocking the entire run.
* Skip SidebarResizeUITests in CI on Depot runners
Mouse drag simulation hangs on headless Depot runners even with a
virtual display. The per-test timeout doesn't prevent the hang either.
Skip this test class in CI; it still runs fine on local machines.
* Handle XCTExpectFailure in CI UI tests (exit 65 with 0 unexpected)
xcodebuild exits 65 even when all failures use XCTExpectFailure. Add
the same expected-failure handling from the unit test step so browser
focus tests (which are expected to fail on headless runners) don't
break CI.
* Add virtual display for headless Depot runners
Depot macOS runners have no physical display, causing XCUITests to fail
with "Failed to activate application (current state: Running Background)".
This adds a small ObjC tool that creates a virtual display using the
private CGVirtualDisplay API before tests run.
* Split self-hosted concurrency groups per workflow
CI, nightly, and release all shared `self-hosted-build`, so the hourly
nightly cancelled in-progress CI runs. Now each workflow has its own
group (self-hosted-ci, self-hosted-nightly, self-hosted-release).
CI also gets cancel-in-progress: true so rapid pushes cancel stale runs.
After rm -rf of the SPM cache dir, recreate it as an empty directory
so binary target downloads (e.g. Sentry.xcframework.zip) don't hit
"already exists in file system" errors from stale artifacts on the
self-hosted runner.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Org policy now requires actions pinned to immutable SHAs instead of
mutable version tags. Pin actions/checkout, actions/github-script,
softprops/action-gh-release, and oven-sh/setup-bun across all workflows.
* Upgrade Sentry: tracing, breadcrumbs, dSYM upload
- Enhanced Sentry SDK init with performance tracing (10% sample),
explicit app hang timeout, stack trace attachment, and HTTP
failure capture
- Added breadcrumbs for key user actions: workspace switch/create/close,
split creation, command palette open/close, app focus — these give
context to hang/crash reports
- Added dSYM upload step to nightly and release CI workflows so hang
stacks are fully symbolicated (requires SENTRY_AUTH_TOKEN secret)
- Created SentryHelper.swift with lightweight breadcrumb helper
Closes https://github.com/manaflow-ai/cmux/issues/365
* Remove command palette breadcrumbs
Not useful for hang diagnosis — keep only workspace/tab/split/focus
breadcrumbs that correlate with heavy operations.
Remove -Dxcframework-target=native from CI and release workflows,
defaulting to universal (matching upstream ghostty). The native target
produces a macos-arm64 xcframework slice that causes Xcode to link the
final binary differently (~70KB more __text), resulting in menubar and
right-click lag on M1 Max. The arm64 static libraries are byte-for-byte
identical between native and universal builds - the difference is purely
in how Xcode resolves the xcframework slice.
The -Dxcframework-target flag controls platform slices (native=macOS
only, universal=macOS+iOS), not CPU microarchitecture. Removing it
caused CI to attempt iOS builds which fail due to missing Metal
iOS toolchain on the runner.
* Fix zsh git branch refresh race after cwd change
* Clarify intentional duplicate cwd check in git refresh path
* Add Metal Toolchain download step to CI and release workflows
Fixes build failure when compiling Metal shaders for iOS xcframework
targets — the self-hosted runner needs `xcodebuild -downloadComponent
MetalToolchain` installed before `xcrun metal` can run.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The self-hosted runner (M4 Mac Mini) was building GhosttyKit with
-Dxcframework-target=native, producing M4-tuned binaries. This caused
menubar and right-click lag on M1 machines. Dropping the flag defaults
to universal, which works well across all Apple Silicon chips.
Hardened runtime's library validation was verifying every dylib on load,
causing noticeable UI lag. Add entitlements file with
disable-library-validation to fix while keeping notarization support.
hdiutil produces a basic layout. npm create-dmg (sindresorhus) gives
large centered icons with proper spacing. The blank Applications icon
is a macOS 26 rendering issue that affects both approaches.
create-dmg (brew) uses AppleScript/Finder for icon positioning which
times out on headless CI. Switch to hdiutil with Applications symlink
for reliable CI builds.
Switch from npm create-dmg (sindresorhus) to brew create-dmg
(create-dmg/create-dmg) which properly renders the Applications
folder symlink icon with explicit layout control.
* Fix zsh ZDOTDIR wrapper + log parsing with -- messages
* Fix CI race condition: serialize self-hosted builds with concurrency group
Two workflows racing on the same self-hosted runner caused DerivedData
corruption (release's rm -rf nuked DerivedData while CI was building).
Add shared concurrency group and scope DerivedData cleanup to project.
Root cause: INFOPLIST_KEY_ build setting prefix only works for Apple-recognized
keys (CF*, NS*, LS*), not custom keys like SUPublicEDKey. The key was never
being added to Info.plist, so generate_appcast silently skipped EdDSA signing
(no public key in app = nothing to match against).
Fix:
- Derive public key from private key at build time using CryptoKit
- Use PlistBuddy to inject SUPublicEDKey and SUFeedURL after build
- Add sign_update fallback in appcast script if generate_appcast skips signing
- Add base64 padding normalization for key handling
SPARKLE_PUBLIC_KEY was set as a shell env var but Xcode needs it passed
as a build setting argument. Without SUPublicEDKey in Info.plist, Sparkle
silently ignores updates.