2026 Mac 클라우드 CI: SPM이 Package.resolved 로 고정되는 시점과 -disableAutomaticPackageResolution 이 필수일 때(FAQ 포함)
노트에서 되는 빌드가 클라우드에서 패키지만 비는 현상은 대개 Xcode보다 SPM 자동 처리와 미커밋된 lockfile이 원인입니다. 이 글은 2026 기준 게이팅·결정표·병렬 계수·KPI·FAQ와 egress·디스크·웜 노드 가이드를 한 흐름으로 정리합니다.
1. 통증 분류: lockfile 미비, 무인 환경의 그래프 변경, 캐시 공유, 출구 오인
공유 Mac 빌더에서 자동 resolve가 허용되면 링크 단 flaky가 늘어납니다.
- lockfile 비정책: 전달 의존이 커밋되지 않아 두 번째 CI가 오염으로 실패한다.
- Archive에 자동 해석 잔류: main과 PR이 다른 그래프가 되면 bisect가 깨진다.
- 캐시 루트 공유: 중간 산물로 linker가 불명확해진다. 빌드 큐·디스크 여유로 슬롯별 경로 계약한다.
- SPM 지연을 컴파일 탓으로: TLS/DNS 때문에 resolve만 불안정. 기업 egress 가이드와 로그를 분리한다.
missing package product와 checksum은 resolve 클래스, resolve 이후 linker는 IO/큐로 묶습니다.
2. 워크스페이스·xcodebuild·공유 노드 결정표
Xcode가 실제로 읽는 Package.resolved 경로만 게이트하세요.
| 구성 | Package.resolved 커밋 | -disableAutomaticPackageResolution | 독립 resolve Job |
|---|---|---|---|
| 단일 저장소 Archive(기본) | 필수(PR+보호 main) | archive/test 모두 | 권장: resolve 프로브를 앞 단에 |
| 동일 노드 다중 scheme 병렬 | 브랜치별 필수, 암묵 공유 금지 | 필수+슬롯별 -derivedDataPath |
resolve와 compile IO 피크 분리 |
| SwiftPM CLI만 | 필수/swift package resolve --force-resolved-versions |
xcodebuild 플래그는 해당 없음 | 폐쇄 Git이면 SSH 워밍 별도 job |
| 실험 브랜치로 의존 업 | 머지 전 lockfile 차분 명시 | 격리 캐시에서만 임시 완화 | Unix 사용자·루트 분리 |
3. 병렬 resolve 계수 참고표
| 변수/플래그 | 시작값(동시 슬롯 8–12 가정) | 줄이거나 끌 때 |
|---|---|---|
SWIFT_PACKAGE_MANAGER_PARALLEL_FETCH_LIMIT |
8–12(CPU·출구 재조정) | 여유 공간<15% 또는 큐 경보 |
XCODE_PACKAGE_RESOLVE_PARALLELISM=YES |
resolve 프로브에만 | Archive와 동일 볼륨 동시 만충 |
-scmProvider system |
사내 Git+ssh-agent | known_hosts 없으면 인증 실패 |
-disableAutomaticPackageResolution |
lock 운영의 compile/archive/test | lock 재생성 격리 job 외에서는 상시 |
병렬 계수를 resolve 쪽으로만 두고 Archive 피크와 분리합니다.
4. 다섯 단계 Runbook 과 예시
- lockfile을 버전 정책으로 격상: 매니페스트 변경 PR에
Package.resolved설명을 붙인다. - compile보다 앞 resolve: 동일 scheme·destination으로
xcodebuild -resolvePackageDependencies를 실행하고 로그를 아티팩트로 남긴다. - 슬롯별 경로 고정:
DERIVED_DATA_PATH와-derivedDataPath일치, archive에 자동 해석 차단. 사설 패키지면-scmProvider system. - 야간 스모크: 무거운 의존으로 출구를 한 번 통과시킨다.
- 동일 커밋 세 번: resolve/링크 재현성을 비교한다.
#!/bin/zsh
set -euo pipefail
export DERIVED_DATA_PATH="/Volumes/ci/slot${JOB_SLOT:-0}/DerivedData"
xcodebuild -resolvePackageDependencies -scheme App -derivedDataPath "$DERIVED_DATA_PATH" -destination 'generic/platform=iOS'
xcodebuild -scheme App -derivedDataPath "$DERIVED_DATA_PATH" \
-disableAutomaticPackageResolution -scmProvider system \
-destination 'generic/platform=iOS' archive
set -euo pipefail
export DERIVED_DATA_PATH="/Volumes/ci/slot${JOB_SLOT:-0}/DerivedData"
xcodebuild -resolvePackageDependencies -scheme App -derivedDataPath "$DERIVED_DATA_PATH" -destination 'generic/platform=iOS'
xcodebuild -scheme App -derivedDataPath "$DERIVED_DATA_PATH" \
-disableAutomaticPackageResolution -scmProvider system \
-destination 'generic/platform=iOS' archive
5. 세 가지 KPI
- E2E에서 resolve 비중 약 20% 초과(코드 변경 없는 대형 모듈)→출구 우선 검토.
- lockfile 불변 성공 주 2건 초과→자동 해석 누락.
- 로그 라벨 일관성: checksum은 resolve·link는 IO로.
6. 디스크·출구와 정렬
resolve는 성공인데 linker만 불안하면 NVMe 경쟁을 의심하고 콜드·웜·어피니티와 egress 가이드로 되돌아갑니다.
7. FAQ
lockfile 소실? CI 존재 검사 필수.한 user로 여러 repo? 비권장.Linux만? iOS는 macOS에서 확정해야 한다.
8. 맺음말
공유 캐시 루트에만 의존하면 진실 원천이 흔들리고, 호스티드 환경의 불투명한 캐시만으로는 출구 게이팅까지 설계하기 어렵습니다. resolve와 Archive를 저동시 슬롯으로 나누는 VPSMAC Apple Silicon은 이런 Runbook의 착지에 잘 맞습니다.