一台長期用於封存與自動化測試的雲端 Mac,最常見的故障未必出在程式碼。Xcode 會持續寫入 DerivedData、封存檔、裝置支援檔案與模擬器資料;當磁碟接近滿載時,編譯器可能回報看似無關的寫入錯誤,簽署流程也可能只留下不完整的產物。可靠的處理方式並不是定期清空所有目錄,而是先量化空間用量、建立建置門檻,再依資料的可恢復程度逐層清理。
先建立磁碟用量基準
先記錄系統的可用空間,再檢查開發相關目錄。df 用來確認檔案系統還能寫入多少資料,du 則用來找出空間由哪些目錄占用。兩者無法互相取代。
df -Pk /
for path in \
"$HOME/Library/Developer/Xcode/DerivedData" \
"$HOME/Library/Developer/Xcode/Archives" \
"$HOME/Library/Developer/CoreSimulator" \
"$HOME/Library/Developer/Xcode/iOS DeviceSupport"
do
if [ -e "$path" ]; then
du -sk "$path"
fi
done
建議分別在一次乾淨建置開始前、封存完成後,以及測試結束後取樣。這三組數據可以反映單次工作的空間增量,也能避免把正常的用量高峰誤判為空間洩漏。若要進一步找出 DerivedData 內占用較大的目錄,可執行:
du -sk "$HOME/Library/Developer/Xcode/DerivedData"/* 2>/dev/null \
| sort -nr \
| head -20
APFS 顯示的可用空間可能會受到可清除資料影響。建置門檻應以
df回報的可用區塊為準,不要只根據目錄的標示大小判斷工作一定能夠完成。
為建置設定明確的容量門檻
容量門檻應根據實際尖峰用量決定,而不是憑經驗寫死。可以先以 30GB 為起點,記錄完整封存流程中的最低剩餘空間;穩定執行數次後,再將門檻調整為尖峰增量的 1.5 倍,並額外納入匯出產物所需的空間。
下列指令稿可放在管線最前端。空間不足時會以狀態碼 75 結束,讓排程器將工作標記為暫時無法執行,而不是啟動一個極可能失敗的建置。
#!/bin/zsh
set -euo pipefail
minimum_kb=$((30 * 1024 * 1024))
available_kb=$(df -Pk / | awk 'NR == 2 {print $4}')
if (( available_kb < minimum_kb )); then
printf 'Insufficient disk capacity: %s KB available
' "$available_kb"
exit 75
fi
printf 'Disk capacity check passed: %s KB available
' "$available_kb"
門檻值應針對每個節點分別保存。不同的 Xcode 版本、模擬器組合與專案規模會產生不同的尖峰用量,不應以小型專案的結果替大型工作區提供容量保證。
將每次建置限制在獨立目錄中
共用預設 DerivedData 的問題不只在於容量,還包括難以判斷每個目錄究竟屬於哪一項工作。為工作區指定獨立路徑後,工作結束時即可精準刪除相關資料,而不會影響其他仍在執行的建置。
job_root="$HOME/build-jobs/$BUILD_ID"
derived_data="$job_root/DerivedData"
archive_path="$job_root/artifacts/App.xcarchive"
mkdir -p "$job_root/artifacts"
xcodebuild \
-workspace App.xcworkspace \
-scheme App \
-configuration Release \
-derivedDataPath "$derived_data" \
-archivePath "$archive_path" \
archive
BUILD_ID 應由工作系統提供,且只能包含英文字母、數字、句點、底線或連字號。清理前還必須驗證目標路徑位於 build-jobs 之下,避免空變數讓刪除範圍意外擴大至家目錄。
依可恢復程度分層清理
清理順序應從能夠重新產生的資料開始,最後才處理需要人工確認的封存檔。
| 層級 | 資料 | 建議動作 | 主要邊界 |
|---|---|---|---|
| 1 | 已結束工作的 DerivedData | 依工作目錄刪除 | 確認沒有建置程序仍在使用 |
| 2 | 已無法使用的模擬器記錄 | 使用 simctl 清理 |
不直接刪除 Devices 目錄 |
| 3 | 舊版裝置支援檔案 | 依實際測試版本複核 | 保留仍需除錯的系統版本 |
| 4 | xcarchive 與 dSYM | 人工核對後移出 | 已發布版本必須可追溯 |
移除失效的模擬器應交由系統工具處理:
xcrun simctl delete unavailable
不要直接清空 CoreSimulator/Devices。當檔案目錄與模擬器服務的狀態不一致時,後續建立與啟動裝置將出現更難排查的問題。封存目錄也不適合僅以「超過若干天」為條件直接刪除,因為 xcarchive 中可能保存已發布版本對應的 dSYM。正確做法是先登記版本、建置編號與移出位置,再刪除本機副本。
自動化清理時避開三個陷阱
第一,不要在建置期間清理共用目錄。即使某個檔案看起來已經很舊,編譯器仍可能透過索引或中間產物參照它。清理工作應取得節點層級的鎖定,或僅操作已標記為完成的獨立工作目錄。
第二,不要把「清理成功」等同於「空間已經釋放」。刪除後應再次執行 df -Pk /,確認可用區塊確實增加;如果沒有變化,應繼續檢查仍被程序開啟的檔案,而不是反覆執行刪除命令。
第三,不要讓指令稿自行判斷封存檔是否重要。無人值守的工作只應負責列出候選項目,例如產生目錄大小、最後修改時間與關聯的建置編號;是否移出或刪除,應依發布記錄決定。
以一次可重複建置完成驗收
清理完成後,至少執行一次與正式工作相同的封存流程。驗收時應記錄開始容量、最低容量、結束容量、封存路徑與結束狀態,並檢查 .xcarchive 是否完整產生。若節點也負責模擬器測試,還應將測試矩陣中每個保留版本各啟動一次,確認裝置可以正常建立、啟動與關閉。
最終檢查項目可以保持精簡:建置前門檻已通過;工作使用獨立的 DerivedData;結束後沒有殘留的編譯程序;封存檔與 dSYM 已登記;simctl list 中沒有異常裝置;磁碟剩餘空間高於下一次工作的門檻。只有同時符合這些條件,清理流程才算真正閉環。
常見問題
Xcode 建置節點至少要保留多少磁碟空間?
沒有適合所有專案的固定值。可先以 30GB 作為建置前最低門檻,再依完整封存的峰值調整;大型多模組專案至少應保留最近峰值的 1.5 倍。
可以直接刪除整個 DerivedData 目錄嗎?
可以,但必須先確認沒有建置程序正在使用。較穩妥的方法是為每個工作區指定獨立 DerivedDataPath,只刪除已完成工作的目錄。
哪些 Xcode 檔案不應自動刪除?
未完成的封存、尚未備份的 xcarchive、發布版本對應的 dSYM,以及測試矩陣仍在使用的模擬器裝置,都不應交給無人值守清理。
讓可重現的環境固定在同一台雲端 Mac 上
選擇固定的機型、地區與租用期間,透過完整的 macOS 圖形介面與命令列完成開發或自動化工作。