將公開金鑰加入遠端,再變更密碼
在遠端建立 ~/.ssh,並將目錄權限設為 700,公開金鑰檔案權限設為 600。使用編輯器新增公開金鑰,保留現有授權。接著執行 passwd 變更目前使用者的密碼。
另開一個終端機,使用 ssh -i ~/.ssh/osxhost_build -p "$MAC_PORT" "$MAC_USER@$MAC_HOST" 確認登入成功後,再結束原本的連線。請勿先關閉唯一可用的連線。
從連線到第一份建置成品
前半小時用來確認連線、檢查工具鏈並首次編譯,後半小時整理簽章與流水線。你取得的是獨享實體 Mac,不是虛擬機器;原有的 macOS 圖形介面與命令列工作流程都能繼續使用。
已下單?請先登入控制台,查看實際主機位址、連接埠、使用者名稱與交付資訊。範例變數不是連線憑證。
核對主機指紋並建立 SSH 連線。
確認 Xcode、專案相依套件與磁碟空間。
先執行免簽章檢查,再驗證 archive。
步驟一 · 先確認連線
先在本機產生金鑰,只上傳公開金鑰。私密金鑰請留在自己的裝置上,並設定密碼。以下指令中的主機、連接埠和使用者名稱,請以控制台提供的實際資訊為準。
read -r -p "主機地址: " MAC_HOST
read -r -p "SSH 端口: " MAC_PORT
read -r -p "用户名: " MAC_USER
ssh-keygen -t ed25519 -f "$HOME/.ssh/osxhost_build"
cat "$HOME/.ssh/osxhost_build.pub"
ssh -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
公開金鑰檔案的副檔名是 .pub 。首次連線時使用交付的登入方式,將公開金鑰加入遠端 ~/.ssh/authorized_keys,再從另一個本機終端機驗證金鑰登入。
$MacHost = Read-Host "主機地址"
$MacPort = Read-Host "SSH 端口"
$MacUser = Read-Host "用户名"
ssh-keygen -t ed25519 -f "$HOME/.ssh/osxhost_build"
Get-Content "$HOME/.ssh/osxhost_build.pub"
ssh -p $MacPort "$MacUser@$MacHost"
請使用 Windows 的 OpenSSH 用戶端。如果系統提示找不到 ssh,請先在系統選用功能中啟用 OpenSSH 用戶端,再重新開啟 PowerShell。
read -r -p "主機地址: " MAC_HOST
read -r -p "SSH 端口: " MAC_PORT
read -r -p "用户名: " MAC_USER
ssh-keygen -t ed25519 -f "$HOME/.ssh/osxhost_build"
cat "$HOME/.ssh/osxhost_build.pub"
ssh -p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
請使用 Linux 發行版提供的 OpenSSH 用戶端。先核對主機指紋,不要為了略過提示而停用主機金鑰檢查;系統重新安裝後若指紋變更,也請重新確認。
在遠端建立 ~/.ssh,並將目錄權限設為 700,公開金鑰檔案權限設為 600。使用編輯器新增公開金鑰,保留現有授權。接著執行 passwd 變更目前使用者的密碼。
另開一個終端機,使用 ssh -i ~/.ssh/osxhost_build -p "$MAC_PORT" "$MAC_USER@$MAC_HOST" 確認登入成功後,再結束原本的連線。請勿先關閉唯一可用的連線。
在本機執行下方的通道指令,並保持終端機開啟。在 macOS 開啟「螢幕共享」,連線至 vnc://127.0.0.1:5901;Windows 和 Linux 請使用支援相應 VNC 驗證方式的用戶端,連線至本機連接埠 5901。
遠端必須已啟用螢幕共享,並授權目前使用者。圖形介面登入憑證與 SSH 私密金鑰是兩種不同的驗證資料;請勿將 VNC 連接埠直接開放給所有來源。
驗收:可檢視並操作 macOS 桌面ssh -N -L 5901:127.0.0.1:5900 \
-i "$HOME/.ssh/osxhost_build" \
-p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
步驟二 · 本機 Mac → 雲端 Mac
先完成資料與工具鏈驗收,再註冊 CI 執行器。分開確認「可以登入」與「可以無人值守建置」,避免把本機互動式設定誤當成流水線設定。
.p12,並透過獨立的安全管道傳送密碼。驗收條件:專案版本一致、憑證包含私密金鑰,且敏感檔案未加入 Git。
xcodes list 查看版本;完成所需授權後,再安裝目標 Xcode。xcode-select 明確指定目前使用的工具鏈,不依賴圖形介面中最近一次的選擇。驗收條件:終端機與 CI 使用的 Xcode、Ruby、Bundler 版本一致。
驗收條件:推送一筆可信任的提交,並可清除記錄與建置成品。
rsync -avz \
--exclude='.git/' \
--exclude='build/' \
--exclude='DerivedData/' \
--exclude='Pods/' \
--exclude='node_modules/' \
-e "ssh -p $MAC_PORT -i $HOME/.ssh/osxhost_build" \
./ "$MAC_USER@$MAC_HOST:~/build-workspace/"
這個同步指令不會刪除遠端檔案。正式流水線建議直接從版本儲存庫檢出固定提交;快取鍵至少應包含工具鏈版本、相依套件鎖定檔與目標架構。請勿讓不受信任的外部合併請求讀取簽章憑證。
步驟三 · 分別驗收簽章與建置成品
先在專案根目錄執行 bundle install,並提交 Gemfile.lock。以下流程假設簽章儲存庫已初始化、App 已建立,且 CI 已安全注入環境變數;不包含建立新的簽章資產。
xcode-select -p
xcodebuild -version
xcodebuild -showsdks
ruby --version
bundle --version
bundle exec fastlane --version
security find-identity -v -p codesigning
df -h "$HOME"
將簽章儲存庫網址設為 MATCH_GIT_URL,並將解密密碼設為 MATCH_PASSWORD。上傳時使用伺服器端 API 金鑰:金鑰檔案路徑由 ASC_KEY_PATH 指定,金鑰 ID 與發行者 ID 則分別由 ASC_KEY_ID、ASC_ISSUER_ID 指定。金鑰檔案應由 CI 秘密管理機制保管,並在執行期間寫入磁碟;請勿提交至專案儲存庫。
設定 IOS_APP_IDENTIFIER、IOS_TEAM_ID、IOS_WORKSPACE 和 IOS_SCHEME。簽章儲存庫中的發佈類型必須與 appstore 相符;先確認共享 scheme,再執行完整流水線。
default_platform(:ios)
platform :ios do
desc "Archive a signed build and upload it to TestFlight"
lane :release do
setup_ci
api_key = app_store_connect_api_key(
key_id: ENV.fetch("ASC_KEY_ID"),
issuer_id: ENV.fetch("ASC_ISSUER_ID"),
key_filepath: ENV.fetch("ASC_KEY_PATH"),
in_house: false
)
match(
type: "appstore",
readonly: true,
git_url: ENV.fetch("MATCH_GIT_URL"),
app_identifier: ENV.fetch("IOS_APP_IDENTIFIER"),
team_id: ENV.fetch("IOS_TEAM_ID"),
api_key: api_key
)
gym(
workspace: ENV.fetch("IOS_WORKSPACE"),
scheme: ENV.fetch("IOS_SCHEME"),
configuration: "Release",
clean: true,
export_method: "app-store",
output_directory: "build/artifacts",
output_name: "release.ipa"
)
pilot(
api_key: api_key,
ipa: "build/artifacts/release.ipa",
skip_waiting_for_build_processing: true
)
end
end
bundle exec fastlane ios release
find build/artifacts -maxdepth 1 -type f -print
上傳成功不代表處理已完成。這份範例會略過上傳後的處理等待。請前往發佈後台確認建置處理結果,並確保每次上傳使用不同的建置編號。readonly: true 會阻止 CI 修改簽章儲存庫;憑證或描述檔到期時,請在受控環境更新後重新執行。
跨平台專案 · 保留鎖定檔
Flutter 和 React Native 的 iOS 階段都需要 Xcode、CocoaPods 與簽章環境。先通過平台診斷,再串接上傳步驟。不要因為快取命中就跳過相依套件一致性檢查。
固定 Flutter SDK 版本,並保留 pubspec.lock。重複使用 Pub 下載快取;Pods 以 Podfile.lock 作為快取鍵,DerivedData 則以 Xcode 版本和專案設定作為快取鍵。首次快取為空時,下載與編譯所需時間會比之後更長。
在專案根目錄執行下方指令。簽章設定完成後,flutter build ipa --release 才能完成實機建置匯出。
提交 JavaScript 套件管理工具的鎖定檔,並依專案慣例使用對應的安裝指令。以下以 npm 為例;Ruby 相依套件由 Bundler 管理,避免系統層級的 gem 與專案版本互相覆蓋。
只有在新增相依套件確實需要更新索引時,才使用 pod install --repo-update。日常執行 bundle exec pod install,不要每次都刪除 Pods 和鎖定檔。
flutter --version
flutter doctor -v
flutter pub get
flutter build ios --simulator
flutter build ipa --release
node --version
npm ci
bundle install
(cd ios && bundle exec pod install)
xcrun simctl list devices available
並行模擬器測試必須設限。從可用裝置清單取得兩個實際 UDID,分別設為 SIM_UDID_A、SIM_UDID_B;在已設定 IOS_WORKSPACE、IOS_SCHEME 的專案中執行以下測試。首次先測試一個目標,再增加至兩個,並觀察記憶體壓力與磁碟寫入狀況。
xcodebuild test \
-workspace "$IOS_WORKSPACE" \
-scheme "$IOS_SCHEME" \
-destination "platform=iOS Simulator,id=$SIM_UDID_A" \
-destination "platform=iOS Simulator,id=$SIM_UDID_B" \
-parallel-testing-enabled YES \
-maximum-concurrent-test-simulator-destinations 2 \
-resultBundlePath "build/TestResults-$(date +%s).xcresult"
容器範圍 · 區分 Linux 服務與 macOS 建置
節點本身是獨享實體 Mac。Docker Desktop 或 OrbStack 提供的 Linux 容器環境仍透過 Linux 執行;「實體節點」不代表容器與 macOS 共用同一個核心。
| 工作 | 建議執行位置 | 注意事項 |
|---|---|---|
| 測試資料庫、快取與內部 API | Linux 容器 | 優先選用 arm64 映像檔,並限制記憶體與監聽範圍。 |
| Xcode archive 與模擬器測試 | macOS 主機系統 | 需要 macOS SDK、簽章工具與模擬器執行環境。 |
| 僅提供 amd64 映像檔的服務 | 先驗證相容性 | 轉譯可能增加額外負擔,不能直接套用 arm64 的效能預期。 |
| 長時間執行的配套服務 | 依資源隔離需求決定 | 會與建置工作共用實體記憶體、SSD 與網路,請設限並保留備份。 |
uname -m
docker version
docker info --format '{{.Architecture}}'
docker system df
docker stats --no-stream
選擇 Docker Desktop 或 OrbStack 其中一套來管理容器,避免不清楚目前的 Docker context 指向哪裡。新增服務前,先記錄可用記憶體與磁碟空間;不要把清除容器資料當成預設的故障排除方式。
擴充功能 · 先識別裝置,再寫入資料
外接 SSD 與 Thunderbolt 5 並聯是個別設定項目。實際裝置名稱、掛載路徑、介面支援與連線拓撲,請以訂單交付資訊為準。尚未確認前,請勿執行格式化或分割磁碟指令。
使用 diskutil list external 識別外接裝置,再查看個別磁碟區資訊。如果磁碟區已格式化但尚未掛載,確認識別碼後執行 diskutil mount "$DISK_IDENTIFIER"。不要只憑磁碟編號就認定它是外接 SSD。
建議將相依套件快取、DerivedData 和暫存成品放在此處。將實際路徑設為 CI 變數 BUILD_CACHE_ROOT,確認建置使用者具有寫入權限,並驗證重新啟動後路徑仍然有效。原始碼與簽章資料請另外備份。
並聯不會自動將多台機器的 CPU 或統一記憶體合併成單一程序可用的資源池。必須由應用程式或 CI 排程層拆分工作,並在每台機器上分別設定工具鏈、執行器與工作目錄。
如果交付方案使用 Thunderbolt 網路連線,請逐台確認網路介面、位址與路由,再測試點對點連線。採購前請透過工單說明台數、機型與傳輸用途,不要自行假設所有機型的介面能力都相同。
分散式工作需要應用程式本身支援diskutil list external
ls -la /Volumes
df -h
system_profiler SPThunderboltDataType
networksetup -listallhardwareports
擴充項目的四種租用期限價格請見方案頁。若需變更現有訂單,請先提交工單確認作業方式與資料移轉安排,請勿在正式建置期間直接變更磁碟結構。
最後一步 · 提供資訊,精準定位問題
記錄訂單編號、節點、發生時間與時區,以及最近一次成功建置的提交編號。保留第一個錯誤訊息與結束代碼,不要只提供記錄的最後一行。以下指令可協助縮小問題範圍,不會自動修復所有問題。
先核對位址、連接埠、使用者名稱與私密金鑰路徑。連線逾時通常先檢查網路是否可達;驗證失敗則先檢查使用者名稱、公開金鑰授權與權限。若主機指紋變更,請先透過工單確認,不要直接忽略。
ssh -vv -o ConnectTimeout=10 \
-i "$HOME/.ssh/osxhost_build" \
-p "$MAC_PORT" "$MAC_USER@$MAC_HOST"
檢查憑證是否包含私密金鑰、是否已過期,以及描述檔是否與 App 和團隊相符。CI 專用鑰匙圈也必須能在建置工作階段中存取。請先在受控環境修復簽章資產,再以唯讀模式同步 match。
security find-identity -v -p codesigning
security list-keychains -d user
bundle exec fastlane match appstore --readonly
先區分系統磁碟區與外接磁碟區,再找出 DerivedData、模擬器和建置成品的空間用量。只清除確認可重新產生的快取;不要批次刪除鑰匙圈、簽章檔案或正在使用的工作目錄。
df -h
du -sh "$HOME/Library/Developer/Xcode/DerivedData" \
"$HOME/Library/Developer/CoreSimulator" \
"$HOME/build-workspace" 2>/dev/null
xcrun simctl list devices unavailable
先確認索引程序是否仍在執行,再檢查相依套件安裝狀況與剩餘磁碟空間。圖形介面的索引狀態與命令列建置完成並非同一條件;可查看記錄,確認 archive 是否確實卡住。
ps -axo pid,%cpu,%mem,etime,comm | grep -E 'Xcode|SourceKit|swift'
xcode-select -p
xcodebuild -list -workspace "$IOS_WORKSPACE"
df -h "$HOME"
請勿在工單中附上私密金鑰、憑證密碼、存取權杖或完整的簽章儲存庫內容。提交前,請遮蔽記錄中的憑證與敏感路徑。若無法登入控制台,請寄信至 support@osxhost.com並提供訂單編號與故障情況;電子郵件和控制台工單都是公開的支援管道。
將工具鏈版本、簽章注入方式、快取鍵與成品目錄寫入流水線。需要機器就下單,遇到問題就附上診斷結果提交工單。