xcodebuild exit code 65:2026 遠端建構怎麼修?

日誌最後只剩 xcodebuild exit code 65,但真正的錯誤通常早已出現在前面。

最快的修法不是立即清空快取,而是先保存完整輸出與 .xcresult,再按建構、測試、Archive、exportArchive 所處階段,依序檢查 Scheme、依賴、執行目標、簽名與遠端會話。只有在相同提交於不同環境呈現不同結果時,才應把問題轉向遠端建構環境。

這篇文章適合以下讀者:

  • 在 Xcode 介面建構成功,但透過 SSH 或 CI 執行 xcodebuild 失敗的獨立開發者。
  • 使用模擬器進行自動化測試,遇到裝置不可用、啟動逾時或測試失敗的小型團隊。
  • 需要在遠端 Mac 上執行 Archive、簽名與無人值守打包的發布維護者。

xcodebuild exit code 65 不是一種固定故障,也不是可以直接套用的修復命令。它只表示這次 xcodebuild 工作沒有成功完成;根因必須回到錯誤發生的階段尋找。

一個經過脫敏的失敗片段可能如下:

Test session results, code coverage, and logs:
    /private/.../Build/TestResults.xcresult

Testing failed:
    The app could not be launched because the target device was unavailable.

** TEST FAILED **
Command PhaseScriptExecution failed with a nonzero exit code
xcodebuild: error: Could not build workspace ...
Process exited with code 65

最後一行的資訊量最低。應保留以下資料:

  • 完整標準輸出與錯誤輸出,而不是只截取最後十行。
  • 實際執行的命令、工作目錄與提交識別。
  • DEVELOPER_DIR 或目前使用的 Xcode 活動開發者目錄。
  • 建構動作,例如 buildtestarchive-exportArchive
  • .xcresult 結果包及其保存位置。

Apple 的執行測試與解讀結果文件說明了測試結果與日誌的查看方式。CI 應把結果包視為失敗產物一併上傳,否則重試後原始證據可能被覆蓋。

先看任務停在哪裡:

任務階段 主要觀察點 優先排查方向 不應直接下的結論
Build 編譯、資源處理、連結、Run Script 原始碼、依賴、腳本、Configuration 不是看到 65 就判定簽名
Test 測試產品是否完成、測試是否啟動 Destination、模擬器服務、啟動設定 不是所有測試失敗都屬於編譯
Archive .xcarchive 是否產生 Scheme、Archive 設定、簽名資產 Archive 失敗不等於匯出失敗
exportArchive Archive 已存在但無法輸出 Distribution 身份、Profile、匯出選項 不應先刪除 Keychain 內容

圖形介面開啟的是 workspace,CI 卻可能仍然呼叫 project。當專案加入套件管理或多個 target 後,這個差異足以令本機與遠端結果不同。

應先確認命令實際指向的入口:

xcodebuild -workspace RedactedApp.xcworkspace \
  -scheme RedactedScheme \
  -configuration Release \
  -destination 'platform=iOS Simulator,id=REDACTED' \
  build

上例中的專案名稱、Scheme 與裝置識別字串都只是脫敏佔位符。實際排查時,不要把含有帳號、路徑或識別資訊的完整日誌直接貼到公開討論區。

Scheme 還有一個常被忽略的邊界:本機可用,不代表它已共享並進入版本控制。請在遠端的乾淨檢出目錄確認:

  • 使用 .xcworkspace 還是 .xcodeproj
  • Scheme 是否存在於該提交,是否已設定為共享。
  • DebugRelease 或自訂 Configuration 是否真的存在。
  • 遠端使用的 SDK、Destination 與互動式 Xcode 會話是否一致。
  • Build Configuration 檔案是否依賴本機才有的絕對路徑或環境變數。

Apple 的自訂專案 Build Scheme 說明可用來核對 Scheme 的動作與設定;加入 Build Configuration 檔案的文件則適合檢查不同會話讀到的設定來源。

最快的對照方式,是以同一份脫敏命令分別在互動式終端與自動化會話執行,並比較 showBuildSettings 輸出。若設定已不同,先修正入口與環境;不要把清理 DerivedData 當成第一答案。

exit code 65 也可能包住完全不同的失敗。日誌中若出現套件解析、原始碼編譯、資源處理、連結或 Run Script 錯誤,修法不能混用。

依賴解析

乾淨檢出時,鎖定檔必須與成功建構的提交一致。私有套件倉庫還可能需要 SSH 金鑰、憑證或已登入的工具狀態。互動式終端能讀到的認證,不代表背景 CI 使用者也能讀到。

檢查:

  • 鎖定檔是否已提交且未被 CI 改寫。
  • 私有倉庫認證是否由同一使用者提供。
  • 工作目錄是否正確,腳本是否以預期相對路徑執行。
  • PATH、腳本解譯器與必要環境變數是否一致。

編譯、連結與 Run Script

若第一個有效錯誤是 Swift 或 Objective-C 編譯錯誤,應回到原始碼與編譯設定。若是 undefined symbols 或 framework 找不到,應檢查連結設定、架構與 SDK。若錯誤來自 Run Script,則要確認腳本的執行權限、解譯器、輸入檔與輸出檔宣告。

修復後,請用相同提交做乾淨檢出再驗證。只在曾被手動修改過的工作目錄重試,無法證明修復已進入版本控制。若需要比較兩次建構,可把建構日誌與 xcresult 分析方法納入團隊記錄流程,避免只依賴終端機最後一行。

提醒: 清除 DerivedData 可能掩蓋快取污染,但不會修復錯誤的 Scheme、缺少的私有依賴、失效的腳本權限或不存在的測試目標。清理前先保存結果包,並寫明回退條件。

Test 任務要拆成兩個問題:測試產品是否成功建出,以及測試執行時能否啟動目標。兩者都可能最後呈現 xcodebuild exit code 65,但證據完全不同。

可使用分段方式縮小範圍:

xcodebuild -workspace RedactedApp.xcworkspace \
  -scheme RedactedScheme \
  -destination 'platform=iOS Simulator,id=REDACTED' \
  build-for-testing

xcodebuild -workspace RedactedApp.xcworkspace \
  -scheme RedactedScheme \
  -destination 'platform=iOS Simulator,id=REDACTED' \
  test-without-building

第一段失敗,優先看編譯、測試 target 與依賴;第一段成功、第二段失敗,才看模擬器啟動、測試服務與 Destination。

遠端 Mac 上尤其要核對:

  • 目標系統是否已安裝,且與專案要求相容。
  • 裝置識別字串是否仍有效,不要依賴本機曾建立的模擬器。
  • SSH 背景工作是否有測試所需的圖形會話與服務。
  • 測試環境變數是否由 CI 明確傳入,而不是依賴 GUI 使用者設定。

Apple 的測試環境變數參考可用於逐項比對;測試動作也能參照分階段執行測試的工作流程文件。不要以「模擬器可以列出」作為測試必然能啟動的證明。

發布流程必須分開記錄四個狀態:編譯完成、Archive 產生、代碼簽名完成、匯出成功。Archive 已產生但 exportArchive 失敗,排查方向就不再是原始碼編譯。

遠端無人值守環境應確認:

  • Keychain 內有帶私密金鑰的 Distribution 簽名身份。
  • Provisioning Profile 與 Bundle ID、團隊及簽名用途相符。
  • SSH 或背景工作可在非互動狀態讀取所需憑據。
  • Archive 與匯出選項使用的是同一個提交和預期 Configuration。
  • Keychain 解鎖、權限調整與憑證存放方式已寫入運維紀錄。

Apple 的建立 Distribution-signed 程式碼文件團隊簽名憑證同步說明可用來核實簽名身份;Provisioning Profile 的結構則應對照TN3125 技術說明

任何刪除憑證、替換 Profile 或放寬 Keychain 權限的操作,都應先記下影響範圍、備份位置與回退方案。若只是 SSH 使用者沒有權限,刪除整套簽名資產反而會擴大故障。

完成初步修復後,請依下列清單驗收。每項都應留下可供下一次比較的產物:

  • [ ] 使用固定提交,而不是曾被手動修改的工作目錄。
  • [ ] 記錄 workspace/project、Scheme、Configuration、SDK 與 Destination。
  • [ ] 保存完整標準輸出、錯誤輸出及 .xcresult
  • [ ] 分別驗證 Build、Test、Archive 與 exportArchive 的結果。
  • [ ] 以乾淨檢出重跑,確認依賴鎖定檔與腳本都來自該提交。
  • [ ] 在互動式終端與 SSH/CI 會話比較活動開發者目錄、工作目錄和環境變數。
  • [ ] 測試任務另行確認模擬器目標、啟動服務與測試結果。
  • [ ] 簽名任務確認私密金鑰、Profile、Keychain 權限及匯出設定。
  • [ ] 重新連線或重啟主機後再執行一次,確認問題不是暫時會話狀態。
  • [ ] 若只有特定主機或使用者失敗,將修復範圍限定在遠端環境。

判斷方向可以簡化為三條:

  1. 相同提交在所有環境都失敗:修專案、依賴、腳本或簽名設定。
  2. 只有特定使用者、SSH 會話或主機失敗:修建構環境與權限。
  3. 只有原主機反覆污染或無法重現:重建隔離環境,再以同一提交對照。

對需要長期執行的團隊,可先閱讀遠端 Mac 無人值守建構環境驗收指南,把上述結果轉成固定的主機交接紀錄。

如果目前方案是把 CI 直接掛在開發者日常使用的 Mac 上,常見缺點是工作目錄會被手動操作污染、GUI 與 SSH 讀到的設定不同,而且主機重啟或使用者登出後,模擬器、Keychain 與測試服務可能失去一致狀態。臨時借用本機設備也難以保留完整 .xcresult 和可重複的 Archive 流程。

在專案級排查完成後,使用同一提交於隔離的遠端 Mac 執行一次 Build、Test 或 Archive,能更清楚分辨是程式碼問題還是主機問題。若需要按週、按月使用獨立的 macOS 建構環境,NOVAKVM 的遠端 Mac 方案可作為對照測試;但長期固定重負載、必須接觸實體周邊或需要完全掌控硬體的團隊,購買自有 Mac 仍可能更合適。

常見問題

為什麼在 Xcode 介面可以建構,改用 xcodebuild 卻失敗?

兩者可能沒有使用相同的 workspace、Scheme、Configuration、SDK、Destination 或環境變數。圖形介面還可能沿用已開啟的使用者狀態,而 SSH 或 CI 使用另一個工作目錄與使用者。應先記錄實際命令、DEVELOPER_DIR、工作目錄及 xcresult,再在兩種會話中對照第一個有效錯誤。

exit code 65 到底代表簽名問題,還是編譯錯誤?

單看 65 無法判定。它只是 xcodebuild 未成功完成工作的退出狀態;真正原因可能出現在依賴解析、原始碼編譯、連結、測試啟動、Archive 或簽名匯出階段。請以日誌中最早出現的有效錯誤、建構產物與 xcresult 內容分層判斷,不要直接刪除憑證或清空所有快取。

遠端 Mac 執行 xcodebuild 找不到 Scheme 時,應該先檢查什麼?

先確認命令指向正確的 .xcworkspace 或 .xcodeproj,並用專案實際共享的 Scheme 名稱執行。Scheme 若只存在於某位使用者的本機設定,乾淨檢出或 CI 會找不到。可在遠端工作目錄列出可用 Scheme,再核對 Configuration、SDK 與 Destination 是否與互動式會話一致。

CI 要怎樣保存 exit code 65 的完整日誌和 xcresult?

將標準輸出與標準錯誤寫入持久化檔案,並在命令結束後保留 .xcresult 結果包;即使建構失敗,也要讓 CI 收集這兩類產物。結果包不可只保留測試成功時的版本。記錄命令、提交識別、活動開發者目錄與工作目錄,但要遮蔽帳號、路徑、Bundle ID、Team ID 和憑證名稱。

遠端建構遇到 exit code 65?以 NOVAKVM 建立穩定開發環境

租用專屬遠端 Mac,為 iOS 與 macOS 專案提供一致且可重複的建構環境。

透過 SSH 連線配合 CI 工作流程,方便執行建構、測試及 Archive。

查看定價 →