雲端 Mac CI 參數列表過長診斷與治理

雲端 Mac CI 參數列表過長診斷與治理

大型 iOS 儲存庫在雲端 Mac 上執行數週後,某個分支可能突然讓指令碼回報 argument list too long,相鄰分支卻仍可正常封存。此時先不要清空 DerivedData,也不要將問題歸咎於 Xcode 的偶發故障。這個錯誤通常源自 execve 傳回的 E2BIG:命令參數、每個參數的結束符,以及繼承的環境共同耗盡了程序可用的空間。

先確認失敗發生在哪一層

第一步是保留完整命令與結束狀態。若日誌只顯示「指令碼階段失敗」,應在對應的 Run Script 中暫時啟用 set -x,確認失敗的是 findrm、封存工具、程式碼產生器,還是編譯器驅動程式。不要將整個環境輸出至公開日誌,其中可能包含權杖;只記錄變數名稱、位元組數,以及經過遮蔽處理的命令結構。

常見的觸發方式有三種:萬用字元一次展開數萬個路徑;指令碼將所有原始碼檔案串接成單一變數;CI 將大段 JSON、憑證內容或多行設定注入為環境變數。過長的工作區路徑,以及重複的 -I-F-D 參數,也會持續消耗剩餘空間。

能在小型分支執行,不代表命令結構正確。只有在檔案數量增加後才失敗,通常表示輸入傳遞方式缺乏上限設計。

量測參數與環境的剩餘空間

macOS 的可用上限不能只根據命令文字長度判斷。請先以與建置任務相同的使用者和啟動方式取樣:

getconf ARG_MAX

python3 - <<'PY'
import os
limit = os.sysconf("SC_ARG_MAX")
env_bytes = sum(len(k) + len(v) + 2 for k, v in os.environ.items())
largest = sorted(
    ((len(k) + len(v) + 2, k) for k, v in os.environ.items()),
    reverse=True
)[:10]
print("arg_max", limit)
print("environment_bytes", env_bytes)
for size, key in largest:
    print(size, key)
PY

這份結果適合用於比較,不應視為可以全部使用的預算。系統仍需保留指標、結束符與啟動開銷所需的空間。實務上應預留充足餘裕,並留意環境大小是否相較歷史基準突然增加。

找出膨脹的變數

優先檢查 PATH、搜尋路徑、暫存目錄、相依性管理器參數,以及 CI 注入的變數。若某個 JSON 設定已達數十 KB,應將其寫入權限受控的暫存檔案,僅把檔案路徑傳給子程序。重複附加 PATH 時應先去除重複項目,而不是在每個步驟再次串接。

精簡繼承環境而不破壞工具鏈

不要直接以空白環境啟動完整的 Xcode 建置;缺少 HOMEPATH、暫存目錄或開發者目錄,反而會造成新的故障。較穩妥的做法是為單一工具建立允許清單:

env -i \
  HOME="$HOME" \
  PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
  TMPDIR="$TMPDIR" \
  DEVELOPER_DIR="$DEVELOPER_DIR" \
  /bin/zsh -lc 'xcrun --find xcodebuild'

正式任務還應依照實際相依項目補齊語系、快取目錄與代理伺服器設定。敏感內容應透過生命週期短暫的檔案傳遞,並於使用後刪除;一般布林開關與簡短識別碼則仍可保留為環境變數。SetMini 上的自動化任務也應將環境初始化集中於單一入口指令碼,避免互動式 shell、CI runner 與 Xcode 指令碼各自重複附加設定。

將大量檔案集合移出命令列

通用指令碼使用空字元分隔

檔名可能包含空格或換行符,因此不能使用 for f in $(find ...)。支援批次處理的工具可搭配 find -print0xargs -0

find "$PWD/Artifacts" -type f -name '*.dSYM' -print0 |
  xargs -0 -n 50 /usr/bin/file

-n 50 可為每個批次設定明確上限。若工具支援從標準輸入讀取清單,應優先使用標準輸入,以減少重複啟動程序。

Xcode 階段使用檔案列表

Run Script 的輸入與輸出應寫入 .xcfilelist,再設定至 Input File Lists 與 Output File Lists。如此一來,Xcode 便能追蹤相依關係,指令碼也不必將數千個路徑展開成單一命令。若編譯器或連結器明確支援回應檔案,可將穩定參數寫入回應檔案;不要假設所有第三方工具都能理解 @file 語法。

對於透過建置設定傳入的大量選項,共用值應移至 .xcconfig。指令碼只接收少量必要參數,路徑集合交由檔案列表處理,結構化設定則寫入暫存檔案。將這三類輸入分開後,日誌也更容易稽核。

建立成長門檻與迴歸檢查

修正後,至少應使用檔案最多、路徑最長的分支執行一次乾淨建置,再執行一次增量建置。檢查指令碼能否正確處理含空格的檔名、批次處理失敗時是否會傳回非零狀態,以及暫存檔案是否會在各種結束路徑中清除。

可以在 CI 開始階段記錄 ARG_MAX、環境總位元組數與最大變數名稱,但不要記錄變數值。為環境大小建立團隊基準;超過門檻時主動發出警告,而不是等到系統拒絕建立程序。對於會持續成長的檔案集合,還應同時記錄數量與最長路徑長度。

最終目標不是讓目前的命令勉強低於上限,而是讓命令長度不再隨儲存庫規模線性成長。環境維持精簡、檔案集合透過專用通道傳遞、批次處理具有固定上限後,E2BIG 才能從偶發故障轉變為可預先發現的設定問題。

常見問題

為什麼相同建置命令有時成功,有時卻顯示參數列表過長?

參數與繼承環境共用同一份程序空間。分支檔案數、依賴路徑或注入變數改變後,即使命令模板相同,也可能跨過系統上限。

修正參數過長時,應優先使用 xargs 還是回應檔?

一般批次工作優先使用支援空字元分隔的 xargs;編譯器或連結器支援回應檔時,使用回應檔更穩定。Xcode 腳本輸入適合放入 xcfilelist。

可以直接提高 macOS 的 ARG_MAX 嗎?

不建議依賴修改系統限制。應精簡環境、移除重複參數,並將長檔案集合改為檔案列表、標準輸入或分批執行。

獨享實體節點

將工程步驟放進隨時可連線的雲端 Mac

從兩種 Apple Silicon 配置與四個資料中心中選擇,資源不與其他租戶共享;實際可用狀態以控制台即時回傳為準。

選擇方案