Skip to content

Use selected Xcode SDK for local builds - #1294

Merged
malpern merged 1 commit into
masterfrom
codex/xcode-sdk-selection
Sep 13, 2026
Merged

malpern merged 1 commit into
masterfrom
codex/xcode-sdk-selection

Conversation

@malpern

@malpern malpern commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • correct the production Xcode 26.6 default path
  • export the selected Xcode SDK so Rust does not resolve Command Line Tools
  • cover the shell contract

Validation

  • 🧪 Running tests via swift test with safety guards...
    ⏱️ Timeout: 240s
    🛣️ Test lane: full
    🔎 Test filter: DeploymentScriptContractTests
    🏗️ Test prebuild: 1 | disable XCTest: 0 | reset module cache: 0
    🧪 SWIFT_TEST=1 | SKIP_EVENT_TAP_TESTS=1
    🪵 KEYPATH_LOG_LEVEL=3 | KEYPATH_TEST_VERBOSE_LOGS=0
    🧹 Refreshing generated /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/.build/debug symlink
    ♻️ Reusing generated module cache: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/.build/ModuleCache.noindex
    🎯 Swift test args: --filter DeploymentScriptContractTests
    📦 Scratch: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/.build | HOME=/private/var/folders/0s/42v7yl697hn0l1cdnx0vrvyr0000gn/T/tmp.EQ4YcqRE1V
    🗂️ Module cache: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/.build/ModuleCache.noindex
    🔨 Building tests...
    [0/1] Planning build
    [1/1] Compiling plugin CompileKeyboardStageMetal
    [2/2] Compiling plugin GenerateManual
    [3/3] Compiling plugin GenerateDoccReference
    Building for debugging...
    [3/18] Write swift-version--112E4EF74740DA65.txt
    Build complete! (2.94s)
    🚀 Launching swift test...
    [0/1] Planning build
    Test Suite 'Selected tests' started at 2026-09-13 09:12:07.749.
    Test Suite 'KeyPathPackageTests.xctest' started at 2026-09-13 09:12:07.750.
    Test Suite 'DeploymentScriptContractTests' started at 2026-09-13 09:12:07.750.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testBuildAndSignSupportsNonDeployingCandidateBuilds]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testBuildAndSignSupportsNonDeployingCandidateBuilds]' passed (0.002 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testCanonicalBuildScriptsUseStableXcodeContract]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testCanonicalBuildScriptsUseStableXcodeContract]' passed (0.000 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testCIRunnerInstallerReadsGitHubJSONRegistration]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testCIRunnerInstallerReadsGitHubJSONRegistration]' passed (0.000 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testInstalledAppDeployScriptsUseCrossWorktreeLock]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testInstalledAppDeployScriptsUseCrossWorktreeLock]' passed (0.000 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testInstalledAppVerifierRequiresPackagedSwiftPMResources]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testInstalledAppVerifierRequiresPackagedSwiftPMResources]' passed (0.000 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testLocalBuildsSharePinnedWorktreeCache]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testLocalBuildsSharePinnedWorktreeCache]' passed (0.001 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testQuickDeployPreservesBuildFailureDiagnostics]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testQuickDeployPreservesBuildFailureDiagnostics]' passed (0.001 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testReleaseBuildReadsVersionMetadataFromPlistPaths]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testReleaseBuildReadsVersionMetadataFromPlistPaths]' passed (0.000 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testSafeTestFailuresAlwaysPrintArtifactPaths]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testSafeTestFailuresAlwaysPrintArtifactPaths]' passed (0.001 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testSwiftPMWorkflowsRequireInteractiveSafeRunner]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testSwiftPMWorkflowsRequireInteractiveSafeRunner]' passed (0.001 seconds).
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testUninstallerPathsTrackAppResources]' started.
    Test Case '-[KeyPathTests.DeploymentScriptContractTests testUninstallerPathsTrackAppResources]' passed (0.002 seconds).
    Test Suite 'DeploymentScriptContractTests' passed at 2026-09-13 09:12:07.759.
    Executed 11 tests, with 0 failures (0 unexpected) in 0.009 (0.010) seconds
    Test Suite 'KeyPathPackageTests.xctest' passed at 2026-09-13 09:12:07.759.
    Executed 11 tests, with 0 failures (0 unexpected) in 0.009 (0.010) seconds
    Test Suite 'Selected tests' passed at 2026-09-13 09:12:07.759.
    Executed 11 tests, with 0 failures (0 unexpected) in 0.009 (0.011) seconds
    􀟈 Test run started.
    􀄵 Testing Library Version: 1902
    􀄵 Target Platform: arm64e-apple-macos14.0
    􁁛 Test run with 0 tests in 0 suites passed after 0.001 seconds.
    ✅ All tests passed (12 passed)
    📊 Runner summary: lane=full exit=0 prebuild=1 disable_xctest=0 reset_module_cache=0 build=4s test=1s total=6s build_log=252 bytes build_log_swift_warnings=0 build_log_module_cache_warnings=0 log=3554 bytes test_log_swift_warnings=0 test_log_module_cache_warnings=0 test_log_app_warnings=0 test_log_app_errors=0
  • ✅ KeyPath.entitlements exists
    ✅ Sources/KeyPathHelper/KeyPathHelper.entitlements exists
    ✅ kanata.entitlements exists
    ✅ Scripts/build-and-sign.sh exists
    ✅ Scripts/release-doctor.sh exists
    ✅ Scripts/verify-release-signing-contract.sh is executable
    ✅ KeyPath.entitlements is a valid plist
    ✅ Sources/KeyPathHelper/KeyPathHelper.entitlements is a valid plist
    ✅ kanata.entitlements is a valid plist
    ✅ KeyPath.entitlements pins com.apple.security.app-sandbox = false
    ✅ KeyPath.entitlements pins com.apple.security.network.client = true
    ✅ KeyPath.entitlements pins com.apple.security.automation.apple-events = true
    ✅ Sources/KeyPathHelper/KeyPathHelper.entitlements pins com.apple.security.app-sandbox = false
    ✅ kanata.entitlements pins com.apple.security.device.hid = true
    ✅ kanata.entitlements pins com.apple.security.device.input-monitoring = true
    ✅ helper entitlements source is explicit
    ✅ helper signing uses stable helper identifier
    ✅ helper signing applies helper entitlements
    ✅ main app entitlements source is explicit
    ✅ main app signing applies app entitlements
    ✅ CLI signing uses helper-trusted stable identifier
    ✅ release signing uses hardened runtime
    ✅ Kanata Engine inner binary is hardened-runtime signed
    ✅ Kanata Engine bundle is hardened-runtime signed
    ✅ Kanata launcher is hardened-runtime signed
    ✅ Kanata host bridge is hardened-runtime signed
    ✅ Kanata simulator is hardened-runtime signed
    ✅ build-and-sign runs installed-app identity verification
    ✅ build-and-sign runs source signing-contract verification
    ✅ build-and-sign refuses to deploy without a stapled notarization ticket
    ✅ build-and-sign deploys the stapled bundle verbatim (no re-sign on deploy)
    ✅ notarize-resume deploys the stapled bundle verbatim (no re-sign on deploy)
    ✅ release-doctor runs source signing-contract verification
    ✅ Release signing contract passed.
    ✅ Sparkle signing identity 'keypath' matches SUPublicEDKey
    🦀 Building Rust artifacts in parallel (kanata, simulator, host bridge)...
    🔬 Building Kanata Simulator from source...
    🦀 Building Kanata from source (with TCC-safe caching)...
    🧩 Building KeyPath Kanata host bridge...
    📁 Kanata source: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/External/kanata
    📁 Build directory: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/build
    📁 Kanata source: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/External/kanata
    📁 Build directory: /Users/malpern/local-code/KeyPath/.worktrees/xcode-sdk-selection/build
    ✅ Using cached simulator binary
    ✅ Using cached kanata binary (TCC identity preserved)
    kanata-sim 0.1.0
    ✅ Cached kanata ready: kanata 1.12.1-prerelease-1
    📊 Size: 3.1M
    🎯 Cache HIT: Host bridge inputs unchanged, verifying existing artifacts
    bridge version: 0.1.0
    default cfg count: 0
    ✅ Host bridge ready
    ✅ All Rust builds complete
    🔐 Building privileged helper...
    🔐 Building KeyPathHelper (privileged helper)...
    1️⃣ Building helper executable...
    [0/1] Planning build
    Building for production...
    [0/3] Write swift-version--112E4EF74740DA65.txt
    [1/2] Linking KeyPathHelper
    Build of product 'KeyPathHelper' complete! (2.50s)
    2️⃣ Signing helper...

✅ Helper build complete: .build/arm64-apple-macosx/release/KeyPathHelper
⏭️ Skipping screenshot regeneration (dev build)
🏗️ Building KeyPath and plugins...
[0/1] Planning build
[1/1] Compiling plugin CompileKeyboardStageMetal
[2/2] Compiling plugin GenerateManual
[3/3] Compiling plugin GenerateDoccReference
Building for production...
[3/12] Write swift-version--112E4EF74740DA65.txt
[4/5] Linking KeyPathHelper
Build complete! (2.17s)
📦 Creating app bundle...
🔌 Assembling Insights.bundle...
✅ Assembled Insights.bundle
📦 Embedding privileged helper (SMAppService layout)...
✅ Verified kanata plist uses Contents/Library/KeyPath/kanata-launcher and omits --debug
✅ Helper embedded: dist/KeyPath.app/Contents/Library/HelperTools/KeyPathHelper
✅ Helper plist embedded: dist/KeyPath.app/Contents/Library/LaunchDaemons/com.keypath.helper.plist
✅ Kanata daemon plist embedded: dist/KeyPath.app/Contents/Library/LaunchDaemons/com.keypath.kanata.plist
✅ Copied app resources
✅ Copied resource bundle: KeyPath_KeyPath.bundle
✅ Copied resource bundle: KeyPath_KeyPathAppKit.bundle
✅ Copied resource bundle: KeyPath_KeyPathInstallationWizard.bundle
🧾 Writing BuildInfo.plist...
✍️ Signing executables...
Applying entitlements from KeyPath.entitlements...
✅ Verifying signatures...
[identity-contract] PASS: Kanata Engine binary exists
[identity-contract] PASS: Kanata daemon shell exists
[identity-contract] PASS: privileged helper exists
[identity-contract] PASS: Kanata daemon plist exists
[identity-contract] PASS: privileged helper plist exists
[identity-contract] PASS: signed Kanata Engine bundle ID is com.keypath.kanata-engine
[identity-contract] PASS: signed Kanata daemon label is com.keypath.kanata
[identity-contract] PASS: signed Kanata daemon BundleProgram is Contents/Library/KeyPath/kanata-launcher
[identity-contract] PASS: signed Kanata daemon associated bundle ID is com.keypath.KeyPath
[identity-contract] PASS: signed helper launchd label is com.keypath.helper
[identity-contract] PASS: signed helper BundleProgram is Contents/Library/HelperTools/KeyPathHelper
[identity-contract] PASS: Kanata Engine.app signing identifier is com.keypath.kanata-engine
[identity-contract] PASS: Kanata Engine.app signed by Developer ID Application: Micah Alpern (X2RKZ5TG99)
[identity-contract] PASS: Kanata Engine.app team identifier is X2RKZ5TG99
[identity-contract] PASS: Kanata Engine.app designated requirement is stable
[identity-contract] PASS: Kanata Engine binary signing identifier is com.keypath.kanata-engine
[identity-contract] PASS: Kanata Engine binary signed by Developer ID Application: Micah Alpern (X2RKZ5TG99)
[identity-contract] PASS: Kanata Engine binary team identifier is X2RKZ5TG99
[identity-contract] PASS: Kanata Engine binary designated requirement is stable
[identity-contract] PASS: KeyPathHelper signing identifier is com.keypath.helper
[identity-contract] PASS: KeyPathHelper signed by Developer ID Application: Micah Alpern (X2RKZ5TG99)
[identity-contract] PASS: KeyPathHelper team identifier is X2RKZ5TG99
[identity-contract] PASS: KeyPathHelper designated requirement is stable
[identity-contract] PASS: kanata-launcher signing identifier is kanata-launcher
[identity-contract] PASS: kanata-launcher signed by Developer ID Application: Micah Alpern (X2RKZ5TG99)
[identity-contract] PASS: kanata-launcher team identifier is X2RKZ5TG99
[identity-contract] PASS: kanata-launcher designated requirement is stable
[identity-contract] all checks passed
⏭️ Skipping notarization (SKIP_NOTARIZE=1)
🎉 Build complete!
📍 Signed app: dist/KeyPath.app

✨ Creating Sparkle update archive...
📦 Creating versioned archive: KeyPath-1.0.2.zip
📝 Generating and signing complete appcast with Sparkle...
Wrote 1 new update, updated 0 existing updates, and removed 0 old updates in appcast.xml
✅ Sparkle archive and signed appcast verified
💿 Creating DMG installer...
✅ DMG created: dist/sparkle/KeyPath-1.0.2.dmg

✅ Sparkle archive created:
📦 Archive: dist/sparkle/KeyPath-1.0.2.zip
📝 Signed appcast: dist/sparkle/appcast.xml
💿 DMG: dist/sparkle/KeyPath-1.0.2.dmg
📂 Deploying to /Applications...
✅ Deployed latest KeyPath to /Applications/KeyPath.app
🚪 Restarting app...
Starting new KeyPath... (production Xcode 26.6; complete signed candidate build)

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-13T16:14:45.186881Z e3e3146 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown

One reliability gap worth addressing before merge:

Scripts/lib/xcode.shSDKROOT is exported without existence validation. The function already validates $DEVELOPER_DIR and fails fast with a clear ❌ Invalid Xcode developer directory message if it's missing. The new SDKROOT derived from it ($DEVELOPER_DIR/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk) gets no equivalent check — if that subpath doesn't exist (e.g. a future Xcode layout change, or a KEYPATH_DEV_XCODE_DEVELOPER_DIR override pointing at a non-standard install), the script will silently export a bogus SDKROOT and downstream cc/rustc invocations will fail with a confusing SDK-resolution error instead of the same clear early failure. Suggest adding a [ -d "$SDKROOT" ] guard alongside the existing developer-dir check.

Minor/unrelated-but-flagging: the default KEYPATH_STABLE_XCODE_DEVELOPER_DIR path changed from Xcode-26.6.0.app to Xcode-26.6.app in the same diff as the SDKROOT fix — worth confirming this rename is intentional here rather than accidentally bundled, since AGENTS.md documents the pin as including the patch version (26.6.0) and notes "an installed 26.6.1 does not satisfy 26.6," so the naming convention for this constant matters.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3e3146e26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Scripts/lib/xcode.sh
# Rust invokes the system compiler through `cc`; without an explicit SDK it
# can resolve Command Line Tools even when DEVELOPER_DIR points at Xcode.
# Keep every compiler on the same selected Xcode SDK.
export SDKROOT="$DEVELOPER_DIR/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Invalidate cached Rust artifacts when selecting the SDK

When a checkout already contains the Rust build caches, this export does not make the packaged artifacts use the selected SDK: build-kanata.sh and build-kanata-simulator.sh hash only sources, while the host-bridge fingerprint records DEVELOPER_DIR, which was already set before this change. Consequently, build-and-sign.sh can report cache hits and ship binaries previously linked through Command Line Tools; include the SDK/compiler identity or a cache-schema version in each fingerprint so this new build input forces one rebuild.

AGENTS.md reference: AGENTS.md:L56-L65

Useful? React with 👍 / 👎.

Comment thread Scripts/lib/xcode.sh

KEYPATH_STABLE_XCODE_VERSION="${KEYPATH_STABLE_XCODE_VERSION:-26.6}"
KEYPATH_STABLE_XCODE_DEVELOPER_DIR="${KEYPATH_STABLE_XCODE_DEVELOPER_DIR:-/Applications/Xcode-26.6.0.app/Contents/Developer}"
KEYPATH_STABLE_XCODE_DEVELOPER_DIR="${KEYPATH_STABLE_XCODE_DEVELOPER_DIR:-/Applications/Xcode-26.6.app/Contents/Developer}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the documented stable Xcode path

Changing the default to Xcode-26.6.app leaves the canonical workflow documentation telling developers that scripts select /Applications/Xcode-26.6.0.app, even though the same instructions require the stable directory and matching literals to move together. Update that path so setup and future pin changes do not follow a stale installation contract.

AGENTS.md reference: AGENTS.md:L56-L65

Useful? React with 👍 / 👎.

@malpern
malpern merged commit b999960 into master Sep 13, 2026
7 of 8 checks passed
@malpern
malpern deleted the codex/xcode-sdk-selection branch September 13, 2026 16:51
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