MangoVM 工程指南

用 xcconfig 管理 iOS 多環境設定並檢查封存檔

用 xcconfig 管理 iOS 多環境設定並檢查封存檔

同一套 iOS 專案同時連接開發、預備與正式服務時,最危險的錯誤通常不是編譯失敗,而是成功封存了錯誤的環境。只要開發網址、日誌開關或內部功能旗標進入發佈套件,即使流水線仍顯示成功,也可能造成問題。更穩妥的做法是分層管理公開設定、固定建置入口,並將封存檢查列為正式發佈步驟,而不是依賴某位開發者在 Xcode 中選對選單。

先劃清設定與祕密的界線

xcconfig 適合保存可隨用戶端公開的建置參數,例如 API 基底網址、Bundle ID 後綴、日誌層級和功能開關預設值。它不是祕密保險箱。凡是寫入用戶端建置流程的值,都可能出現在 Info.plist、資源檔、編譯參數或執行檔中。

判斷標準很簡單:如果使用者取得安裝套件後不應知道某個值,就不要交給 Xcode 編入 App。

伺服器端管理金鑰、簽署服務憑證,以及具備寫入權限的權杖,都應留在伺服器端或受控的 CI 憑證儲存區。用戶端只接收短效、低權限且可撤銷的授權結果。即使 CI 透過環境變數注入祕密,也無法改變「最終產物可被分析」這項事實。

先列出一份設定清單,並為每一項標明是否公開、來源,以及封存後的預期位置。團隊應審查這份清單,而不是散落在 Build Settings 中的數十個值。

建立可審查的 xcconfig 分層

建議將共用設定、環境差異與本機覆寫分開:

Config/
  Base.xcconfig
  Development.xcconfig
  Staging.xcconfig
  Production.xcconfig
  LocalOverrides.xcconfig.example

Base.xcconfig 只存放所有環境共用的值。各環境檔案先納入它,再覆寫少量差異:

#include "Base.xcconfig"

APP_ENVIRONMENT = staging
API_BASE_URL = https:/$()/staging-api.invalid
ENABLE_VERBOSE_LOGGING = YES
PRODUCT_BUNDLE_IDENTIFIER = com.example.product.staging

這裡使用 $(),是為了避免 URL 中的雙斜線被解析成註解。範例網域只是無法解析的文件示意值,實際專案應替換為自己的服務網址。

本機覆寫檔案不要提交至儲存庫,可以使用選擇性納入來降低首次取出專案的門檻:

#include? "LocalOverrides.xcconfig"

不過,本機覆寫只能用於提升開發便利性,不能成為正式封存的隱性輸入。正式環境設定在缺少該檔案時,仍應能完整解析。將 DevelopmentStagingRelease 三個 Configuration 分別綁定至對應檔案,並確認 CI 使用的 Scheme 已設為 Shared。

固定命令列建置入口

雲端 Mac 上的封存工作不應繼承某次圖形介面操作留下的狀態。workspace、scheme、configuration 與輸出路徑都必須明確傳入:

set -euo pipefail

WORKSPACE="App.xcworkspace"
SCHEME="App"
CONFIGURATION="Release"
ARCHIVE_PATH="$PWD/build/App.xcarchive"

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration "$CONFIGURATION" \
  -showBuildSettings > build-settings.txt

xcodebuild \
  -workspace "$WORKSPACE" \
  -scheme "$SCHEME" \
  -configuration "$CONFIGURATION" \
  -archivePath "$ARCHIVE_PATH" \
  clean archive

在封存前驗證解析結果

不要只檢查來源檔案,還要檢查 Xcode 最終解析出的設定。可以從 build-settings.txt 擷取關鍵項目,並對正式環境設定加入強制斷言:

grep -E "APP_ENVIRONMENT|API_BASE_URL|PRODUCT_BUNDLE_IDENTIFIER|ENABLE_VERBOSE_LOGGING" \
  build-settings.txt

grep -q "APP_ENVIRONMENT = production" build-settings.txt
grep -q "ENABLE_VERBOSE_LOGGING = NO" build-settings.txt

如果專案使用多個 Target,應分別檢查每一個 Target,因為擴充功能、測試套件與主 App 可能繼承不同的 Base Configuration。指令碼還應輸出目前的 Git 提交、Xcode 版本與使用的 Scheme,方便發生差異時還原現場。

掃描實際交付的封存檔

Build Settings 正確,不代表產物一定正確。Run Script、產生程式碼或複製資源步驟,仍可能將測試設定帶入封存檔。封存完成後,至少要檢查主 App 的屬性列表、內嵌資源與執行檔字串。

APP_PATH="$(find build/App.xcarchive/Products/Applications -maxdepth 1 -name '*.app' -print -quit)"
test -n "$APP_PATH"

plutil -p "$APP_PATH/Info.plist"

if grep -RInaE "staging-api|debug-token|INTERNAL_ONLY" "$APP_PATH"; then
  echo "forbidden marker found in archive" >&2
  exit 1
fi

EXECUTABLE_NAME=$(/usr/libexec/PlistBuddy -c "Print :CFBundleExecutable" "$APP_PATH/Info.plist")
if strings "$APP_PATH/$EXECUTABLE_NAME" | grep -E "staging-api|debug-token|INTERNAL_ONLY"; then
  echo "forbidden marker found in executable" >&2
  exit 1
fi

掃描字詞應使用團隊定義的標記,不要將真正的祕密直接寫入指令碼或日誌。建議維護「允許出現」與「禁止出現」兩份簡短清單,避免一般詞彙造成誤報。掃描結果只保留檔案路徑與規則名稱,不要回顯命中的完整敏感內容。

檢查對象 應核對內容 失敗處理
最終 Build Settings 環境名稱、Bundle ID、日誌開關 立即停止封存
Info.plist 服務網址、URL Scheme、環境標籤 阻止匯出
App 資源目錄 除錯設定、測試資料、暫存檔 移除來源並重新建置
執行檔 內部網址特徵、權杖標記 找出產生步驟並輪替相關憑證

處理常見的設定漂移與誤區

最常見的設定漂移來自「Scheme 存在於本機,但未設為共享」。儲存庫缺少對應的 xcshareddata 後,CI 可能找不到 Scheme,或工程師臨時改用另一個入口。第二類問題是將環境名稱寫入指令碼分支,卻未將 Configuration 設為唯一來源,時間一久便會形成兩套判斷邏輯。

另一個常見誤區,是認為變數經過混淆、拆分,或寫入二進位檔後就已安全。用戶端中的長期祕密沒有可靠的隱藏方式。如果發現它已進入歷史封存檔,應先撤銷或輪替憑證,再修正建置設定;只刪除目前分支中的字串,無法消除已發佈版本帶來的風險。

在 MangoVM 的固定實體節點上執行這套流程時,也應讓 Runner 使用乾淨的工作目錄,不要重複使用上一次工作產生的 DerivedData、暫存設定和匯出目錄。清理範圍必須限制於本次工作區,避免並行工作互相刪除檔案。

將檢查設為合併與發佈門檻

最終可將流程拆成兩個階段:合併請求階段解析設定並檢查設定檔結構,發佈階段執行完整封存與產物掃描。前者能快速提供回饋,後者則以真正交付的產物為準。任何環境斷言失敗都應傳回非零狀態,不能只在日誌中顯示提示。

提交前可用以下清單完成最後確認:

當設定來源、解析結果與最終產物形成三層證據,環境選擇便不再依賴操作習慣。發佈失敗會在封存檔離開流水線前發生,而不是由使用者代替團隊發現。

常見問題

可以把 API 密鑰直接放進 xcconfig 嗎?

不可以。建置設定可能進入 Info.plist、編譯參數、資源或執行檔,封存檔中的內容能被提取。App 只能包含可公開的客戶端設定,具權限的密鑰應留在伺服器端。

為什麼本機封存正常,CI 卻用了錯誤環境?

常見原因是 Scheme 未共享、Configuration 對應不同,或變數只存在於互動式 Shell。應明確傳入 workspace、scheme 與 configuration,並保存 showBuildSettings 結果比對。

獨享實體節點

為開發流程設定固定的雲端 Mac

MangoVM 提供 M4 與 M4 Pro 兩款 Apple Silicon 實體節點。選擇租期、地區與儲存空間附加項目後,即可核對完整訂單明細。

選擇設定並訂購