빌드는 성공했는데 후속 업로드 단계에서 이진 파일이 없다고 실패합니다.
가장 빠른 수정은 .build 하위 폴더를 더 이상 추측하지 않고 swift build --show-bin-path로 실제 출력 위치를 조회한 뒤, 캐시와 산출물 수집 단계까지 같은 결과를 사용하게 만드는 것입니다.
이 글은 다음과 같은 경우에 맞습니다.
- Shell, Fastlane, Makefile 또는 CI 스크립트를 관리하며 업그레이드 뒤 산출물을 찾지 못하는 개발자
- Swift Package, 빌드 플러그인, 다중 패키지 저장소의 전달 과정을 담당하는 유지 관리자
- 원격 맥 노드의 도구 모음, 캐시, 재시작, 롤백을 관리하는 데브옵스와 배포 엔지니어
실패 지점부터 분리하기
“구성 명령은 종료 코드 0으로 끝났지만 파일을 복사할 수 없다”면 먼저 컴파일 실패와 경로 해석 실패를 나눠야 합니다. 작업 디렉터리가 달라졌거나, 조회할 때와 빌드할 때의 구성이 달라도 같은 증상이 발생합니다.
다음 정보를 한 작업의 로그에 함께 남기십시오.
- 저장소 커밋과 실제 작업 디렉터리
- SwiftPM과 Xcode 도구 모음 버전
- 빌드에 사용한 구성, 아키텍처, 대상
- 전체 빌드 명령과 종료 상태
--show-bin-path가 반환한 실제 경로- 복사 또는 업로드 단계가 검사한 경로
주의: 명령이 성공했다는 사실은 원하는 파일이 예전 위치에 있다는 뜻이 아닙니다. 빌드 성공, 경로 조회 성공, 파일 검증 성공을 서로 다른 증거로 기록해야 합니다.
스크립트의 출력 인터페이스 바꾸기
먼저 Shell, Makefile, Fastlane, 사용자 정의 배포 스크립트에서 .build를 직접 이어 붙이는 부분을 찾습니다. find로 우연히 파일을 찾는 방식도 대상이 여러 개일 때 잘못된 파일을 선택할 수 있으므로 주 인터페이스로 삼지 않는 편이 좋습니다.
빌드와 경로 조회에 같은 인자를 넣는 예시는 다음과 같습니다.
set -eu
WORKSPACE="/path/to/workspace"
CONFIGURATION="debug"
ARCHITECTURE="arm64"
DESTINATION="generic/platform=macOS"
PACKAGE_PATH="/path/to/package"
cd "$WORKSPACE"
swift build \
--package-path "$PACKAGE_PATH" \
-c "$CONFIGURATION" \
--arch "$ARCHITECTURE"
BIN_PATH="$(
swift build \
--package-path "$PACKAGE_PATH" \
-c "$CONFIGURATION" \
--arch "$ARCHITECTURE" \
--show-bin-path
)"
test -d "$BIN_PATH"
printf '%s\n' "$BIN_PATH"
DESTINATION을 사용하는 실제 래퍼나 프로젝트 명령이 있다면 경로 조회에도 동일한 대상을 전달해야 합니다. 핵심은 명령의 모양이 아니라 “실제로 산출물을 만든 조건”과 “경로를 물어본 조건”을 일치시키는 것입니다. 공식 보조 구현이 어떤 방식으로 경로를 소비하는지는 [SourceKit-LSP의 빌드 보조 스크립트](https://github.com/swiftlang/sourcekit-lsp/blob/main/Utilities/build-script-helper.py)에서도 참고할 수 있습니다.
| 확인 항목 | 예전 방식 | 수정 방식 | 판단 |
|---|---|---|---|
| 이진 파일 위치 | .build 아래 고정 경로 | --show-bin-path 결과 | 조회 결과를 전달하면 통과 |
| 구성 | 빌드와 수집이 서로 다를 수 있음 | 같은 -c 값 사용 | 값이 다르면 실패로 분류 |
| 아키텍처 | 기본값에 의존 | 빌드와 조회에 같은 --arch 사용 | 원격 노드 차이를 차단 |
| 작업 공간 | 실행기 기본 디렉터리 의존 | cd 뒤 절대 경로 기록 | 로그에 실제 위치가 있어야 함 |
패키지와 플러그인의 산출물 계약 점검
Swift Package 작성자는 패키지 내부에서도 같은 실수를 확인해야 합니다. 다음 소비자를 각각 분리해 기록하십시오.
- 이진 타깃: 실행 파일 또는 라이브러리의 실제 소비 위치
- 자원 처리: 번들 자원과 복사 대상
- 빌드 도구 플러그인: 입력 파일과 생성 파일
- 스크립트 플러그인: 실행 권한, 환경 변수, 출력 선언
- 다중 패키지 저장소: 어느 패키지가 어느 결과를 전달하는지
.build 경로를 직접 조합한다면 공개된 명령 인터페이스와 내부 디렉터리 구조를 혼동한 것입니다. 플러그인 입력, 출력 선언, 실행 로그를 저장하고, 실패 시 어느 파일을 만들려고 했는지 남겨야 합니다.
| 산출물 소비자 | 관찰할 증거 | 멈춰야 하는 조건 |
|---|---|---|
| 이진 타깃 | 파일 이름, 형식, 생성 로그 | 예상 이름만 있고 실제 파일이 없음 |
| 자원 처리 | 번들 안의 자원과 복사 로그 | 빌드는 성공했지만 번들이 비어 있음 |
| 빌드 도구 플러그인 | 입력·출력 선언과 실행 경로 | 내부 .build 경로를 직접 조합함 |
| 스크립트 플러그인 | 권한, 환경 변수, 표준 오류 | 실행은 되었지만 출력 선언이 없음 |
테스트 결과 수집 규칙 재설계
테스트 단계는 최종 파일 하나의 존재 여부만 검사하면 안 됩니다. swift test의 종료 상태, 테스트 보고서의 출처, 실패한 테스트의 위치를 각각 검증해야 합니다.
테스트 수집 스크립트는 다음 순서로 구성하는 것이 안전합니다.
- 동일한 작업 디렉터리에서 테스트 명령을 실행합니다.
- 명령의 종료 상태를 별도 변수로 보존합니다.
- 표준 출력과 표준 오류를 각각 파일로 저장합니다.
- 테스트 보고서가 실제로 생성된 위치를 명시적으로 확인합니다.
- 실패한 테스트 이름과 로그 위치를 배포 기록에 넣습니다.
- 파일이 없으면 업로드를 성공으로 처리하지 않습니다.
원격 맥 CI 캐시와 노드 상태 고정
로컬 Swift Build가 성공했는데 원격 맥 CI에서 업로드가 실패한다면 코드만 비교해서는 부족합니다. 다음 항목이 노드마다 같은지 로그로 확인해야 합니다.
- Swift와 Xcode 도구 모음 선택 상태
- 실행 계정과 파일 권한
- 작업 공간의 절대 경로
- 구성과 아키텍처
- 의존성 잠금 파일
- 빌드 시스템 선택
- 캐시 삭제 시점과 범위
- 노드 재시작 뒤 작업 디렉터리 생성 방식
| 검증 상태 | 실행 방법 | 합격 기준 | 점수 |
|---|---|---|---|
| 냉 캐시 | 새 작업 공간과 빈 캐시 | 빌드부터 업로드까지 재현 | 3 |
| 온 캐시 | 같은 커밋을 다시 실행 | 경로 조회 결과와 파일 검증이 일치 | 2 |
| 재시작 뒤 | 노드 재시작 후 새 작업 | 계정, 도구 모음, 작업 공간이 복원 | 3 |
| 이중 시스템 | Swift Build와 native 비교 | 차이를 로그로 설명 가능 | 2 |
배포 전 이중 경로 승인
다음 목록을 한 항목씩 실행하십시오.
- [ ] 같은 커밋을 새 작업 공간에서 Swift Build로 구성하고 빌드합니다.
- [ ] 빌드 명령과 동일한 구성, 아키텍처, 대상을 넣어
--show-bin-path를 실행합니다. - [ ] 조회 결과에서 이진 파일, 테스트 결과, 자원 또는 보관 파일을 검증합니다.
- [ ] 하드코딩한
.build경로를 복사와 업로드 단계에서 제거합니다. - [ ] 냉 캐시와 온 캐시에서 경로와 파일 검사를 반복합니다.
- [ ] 원격 맥 노드를 재시작한 뒤 실행 계정과 도구 모음 상태를 기록합니다.
- [ ] Swift Build와 native 결과를 같은 커밋으로 비교합니다.
- [ ] Swift Build만 실패하면 최소 재현, 전체 오류, 환경 정보를 보존합니다.
- [ ] 공식 알려진 문제와 일치할 때만 임시 native 전환을 승인합니다.
- [ ] 다시 Swift Build로 전환할 조건과 담당자를 배포 기록에 명시합니다.
자주 발생하는 질문
경로 변경은 컴파일 실패를 뜻하나요?
아닙니다. 빌드 명령의 종료 상태가 성공이고 뒤의 복사나 업로드만 실패한다면 우선 산출물 위치 계약이 깨졌다고 봐야 합니다. 작업 디렉터리, 구성, 아키텍처, 대상이 빌드와 조회에서 같은지 확인한 뒤 실제 경로를 출력하십시오. 그 다음 파일 형식과 이름을 검사해야 컴파일 문제와 수집 문제를 구분할 수 있습니다.
경로 조회 결과를 캐시에 어떻게 연결하나요?
캐시 복원 뒤에도 고정된 .build 경로를 다시 만들지 말고, 현재 작업의 빌드 조건으로 --show-bin-path를 호출하십시오. 캐시 키에는 Swift 버전, 빌드 시스템, 아키텍처, 구성, 잠금 파일을 반영해야 합니다. 복원된 파일이 있어도 현재 조회 결과와 다르면 성공으로 처리하지 말고 냉 캐시 작업으로 되돌아가야 합니다.
native를 바로 기본값으로 바꿔도 되나요?
생산 노드 전체를 바로 바꾸는 것은 권장하지 않습니다. 먼저 Swift Build와 native를 같은 커밋과 조건으로 실행해 차이를 기록해야 합니다. Swift Build에만 재현되는 공식 알려진 문제가 있고 일정상 우회가 필요할 때만 native를 임시로 사용하십시오. 회귀 조건, 재전환 날짜, 책임자를 함께 기록하지 않으면 임시 조치가 고착됩니다.
원격 맥에서만 문제가 반복되면 권한을 먼저 보나요?
권한은 확인해야 하지만 첫 번째 결론으로 삼으면 안 됩니다. 실행 계정, xcode-select 상태, 절대 작업 경로, 캐시 복원 결과, 노드 재시작 뒤 환경을 먼저 비교하십시오. 경로 조회 결과 자체가 올바르다면 파일 권한을 검사하고, 결과가 서로 다르면 도구 모음이나 빌드 조건의 불일치를 우선 조사해야 합니다.
현재 노드와 원격 맥의 선택
현재 생산 노드가 개인 개발 맥이라면 캐시가 개발 작업과 섞이고, 재시작 시 작업 공간이 달라지며, 도구 모음 업데이트를 통제하기 어렵다는 문제가 있습니다. 로컬 장비를 계속 켜 두는 방식도 전원과 네트워크 단절, 담당자별 환경 차이를 감수해야 합니다. 특히 SwiftPM 6.4 전환처럼 냉 캐시와 재시작 검증이 필요한 시기에는 이런 변수가 원인 분석을 늦춥니다.
경로 수정 후에는 격리하고 초기화할 수 있는 원격 맥에서 냉 캐시, 온 캐시, 재시작 뒤 작업을 순서대로 실행하는 편이 운영 판단에 유리합니다. 기존 생산 노드에서 바로 업그레이드하기 어렵다면 MACGPU의 원격 맥 환경과 도구 모음 고정 안내를 먼저 확인하고, 실제 검증용 노드가 필요할 때는 원격 맥 이용 환경을 조건에 맞춰 살펴보십시오. 단기간의 전환 검증이나 재현 작업이라면 새 장비를 상시 구매하는 것보다 격리 가능한 임대 환경이 더 맞을 수 있습니다. 반대로 장기간의 고정 부하, 물리 장치 연결, 사내 보안망 고정이 필수라면 자체 장비가 더 적합합니다.
경로 조회와 이중 검증을 끝낸 뒤, 반복 실행할 격리 환경이 필요하다면 MACGPU 원격 맥 이용 방법에서 접속 방식과 운영 조건을 확인해 보십시오.