GitHub Actions macOS 14 러너 11월 2일 퇴역 — 10월 5일부터 브라운아웃, Xcode·시뮬레이터 바뀌는 점
GitHub Actions의 macOS 14(Sonoma) 러너 이미지가 11월 2일 퇴역한다. 퇴역은 해당 이미지를 더 이상 제공하지 않는다는 뜻이고, 그날 이후 runs-on: macos-14로 지정한 작업은 실행되지 못한다. 대상 레이블은 macos-14, macos-14-large, macos-14-xlarge 세 개. 그 전에 맛보기 장애도 예정돼 있다. 10월 1일 GitHub 체인지로그는 "jobs using macOS 14 will temporarily fail during the following scheduled brownout periods"(아래 브라운아웃 기간에는 macOS 14 작업이 일시적으로 실패한다)라고 공지했고, 첫 회차는 한국 시간으로 10월 5일 밤 11시에 시작했다.
브라운아웃(brownout)은 서비스를 완전히 끄기 전에 정해진 시간 동안만 일부러 실패시켜, 아직 옮기지 않은 사용자가 알아차리게 하는 방식이다. 갑자기 멈추는 것보다는 친절하다. 다만 야간 빌드나 배포 파이프라인이 그 시간에 걸리면 이유를 모른 채 실패 알림만 받게 된다.
브라운아웃, 한국 시간으로는 언제인가
공지에는 UTC로만 적혀 있다. 매 회차가 14:00~다음 날 00:00 UTC라서, 한국 시간으로 바꾸면 밤 11시부터 다음 날 아침 9시까지다.
| 회차 | 한국 시간 (KST) |
|---|---|
| 1 | 10월 5일(월) 23:00 ~ 6일(화) 09:00 |
| 2 | 10월 12일(월) 23:00 ~ 13일(화) 09:00 |
| 3 | 10월 16일(금) 23:00 ~ 17일(토) 09:00 |
| 4 | 10월 19일(월) 23:00 ~ 20일(화) 09:00 |
| 5 | 10월 23일(금) 23:00 ~ 24일(토) 09:00 |
| 6 | 10월 26일(월) 23:00 ~ 27일(화) 09:00 |
| 7 | 10월 29일(목) 23:00 ~ 30일(금) 09:00 |
| 8 | 10월 30일(금) 23:00 ~ 31일(토) 09:00 |
한국 팀에게는 하필 새벽 예약 빌드(cron) 시간대와 겹친다. 마지막 주에는 이틀 연속이다. 브라운아웃 바깥 시간에도 안심할 수는 없다. 체인지로그는 "GitHub may reduce macOS 14 runner capacity, which could result in longer queue times"(macOS 14 러너 용량을 줄일 수 있고, 대기 시간이 길어질 수 있다)라고 덧붙였다.
사실 이 일정은 갑자기 나온 게 아니다. 러너 이미지 저장소(actions/runner-images)의 공지 이슈 #13518이 1월 11일에 올라왔고, 7월 6일부터 이미 지원 중단 절차가 진행 중이었다. 이유는 한 문장이다. "GitHub Actions maintains the latest two stable versions of any given OS version."(각 OS는 최신 안정 버전 두 개만 유지한다.) macOS 26이 2월 26일 정식 지원에 들어가면서 15·26 두 버전이 남고, 14는 그 범위 밖으로 밀려났다. 퇴역 뒤 결과도 명시돼 있다. 이 레이블을 쓰는 워크플로는 "will be terminated with an error"(오류로 종료된다).
macOS 15·26으로 가면 뭐가 바뀌나 (README 직접 대조)
대체 레이블부터 정리한다. 체인지로그가 권하는 건 Apple Silicon(arm64) 레이블인 macos-latest(현재 macOS 26), macos-15, 그리고 대형 러너 macos-latest-xlarge, macos-15-xlarge다. 하나 빠진 게 있다. macos-14-large는 Intel(x64) 이미지라서 arm64 레이블로 바꾸면 아키텍처까지 바뀐다. Intel 빌드가 꼭 필요하면 macos-15-intel이나 macos-15-large, macos-26-intel로 옮겨야 한다. 2025년 9월 공지(#13045)에 따르면 macos-15-intel은 2027년 8월까지 제공된다.
그럼 이미지 안의 내용물은? runner-images 저장소에서 세 이미지의 설치 목록(README)을 gh api로 받아 비교했다. 10월 2일 기준이고, arm64 이미지 버전은 macOS 14가 20260831.0302.1, 15가 20260907.0337.1, 26이 20260907.0351.1이다.
| 항목 | macos-14 | macos-15 | macos-26 (macos-latest) |
|---|---|---|---|
| OS | 14.8.9 | 15.7.9 | 26.6.2 |
| 설치된 Xcode | 15.0.1 ~ 16.2 | 16.0 ~ 16.4, 26.0.1 ~ 26.3 | 26.0.1 ~ 26.6 |
| 기본 Xcode | 15.4 | 16.4 | 26.6 |
| iOS 시뮬레이터 런타임 | 17.0 ~ 18.2 | 18.5, 18.6, 26.0 ~ 26.2 | 26.2, 26.4, 26.5 |
| Node.js 기본 | 22.23.2 | 22.23.2 | 24.20.0 |
| Ruby | 3.3.12 | 3.3.12 | 3.4.10 |
| OpenSSL | 1.1.1w | 1.1.1w | 3.6.4 |
| Mono·NuGet | 있음 | 없음 | 없음 |
가장 큰 변화는 Xcode 15가 통째로 사라진다는 점이다. macos-15에도 Xcode 15.x는 없다. 기본값에 기대고 있었다면 15.4에서 16.4(15) 또는 26.6(26)으로 한 번에 넘어간다.
시뮬레이터 기기 이름도 함정이다. macos-14에는 iPhone 15 계열 시뮬레이터가 있었지만 macos-15와 macos-26에는 iPhone 15라는 이름의 기기가 하나도 없다. macos-15는 iPhone 16 계열·16e·SE(3세대)에 iOS 26 런타임용 iPhone 17 계열이 더해진 구성이고, macos-26은 iPhone 17 계열과 iPhone Air가 중심이다(SE도 없다). -destination 'platform=iOS Simulator,name=iPhone 15'처럼 기기를 이름으로 박아 둔 테스트는 레이블만 바꿔도 "Unable to find a destination" 류로 실패할 수 있다.
그리고 어차피 넘어가야 할 이유가 하나 더 있다. Apple 개발자 공지에 따르면 4월 28일부터 "Apps uploaded to App Store Connect must be built with Xcode 26 or later"(App Store Connect에 올리는 앱은 Xcode 26 이상으로 빌드해야 한다)다. macos-14의 최신 Xcode는 16.2다. 즉 macos-14 러너로는 4월 말부터 이미 App Store 제출용 빌드를 만들 수 없었다는 얘기다. 배포 잡은 진작 옮겼고 테스트 잡만 남아 있는 팀이 꽤 있을 것으로 보인다.
아직 얼마나 남아 있나 — gh로 직접 세 봤다
10월 2일 밤(KST) gh CLI 2.101.0으로 GitHub 코드 검색 API를 호출해, 공개 저장소의 .github/workflows 안에서 레이블이 등장하는 파일 수를 셌다. 읽기 전용 검색이다.
gh api -X GET search/code \
-f q='runs-on: macos-14 path:.github/workflows' \
--jq .total_count
| 검색어 | 파일 수(근사치) |
|---|---|
runs-on: macos-14 | 55,680 |
macos-14-large | 361 |
macos-14-xlarge | 389 |
runs-on: macos-15 | 67,456 |
runs-on: macos-26 | 26,400 |
runs-on: macos-latest | 372,736 |
결과는 이렇다. macos-14를 쓰는 워크플로 파일이 macos-latest의 15% 정도 규모로 아직 남아 있다. 코드 검색의 total_count는 정확한 값이 아니라 추정치라서 규모 감만 보는 용도다. 행렬(matrix) 변수로 조립하는 레이블은 이 검색에 안 잡히니 실제로는 더 많을 수 있다.
그 사례가 GitHub 자신의 저장소에도 있었다. org:actions로 범위를 좁혀 검색하니 Python 바이너리를 빌드하는 actions/python-versions의 워크플로 한 건이 나왔다. 기본 플랫폼 목록에 macos-14_arm64가 문자열로 들어 있는 형태라, runs-on:으로 찾으면 놓친다. 왜 14를 골랐는지는 파일에 적혀 있지 않다. 다만 오래된 OS에서 빌드해 호환 범위를 넓히는 건 흔한 관행이고, 그런 이유였다면 15로 올리면서 MACOSX_DEPLOYMENT_TARGET으로 최소 지원 버전을 따로 지정하는 쪽으로 바꿔야 한다.
린터도 아직은 알려 주지 않는다. 워크플로 린터 actionlint 최신판(1.7.12, 3월 30일 릴리스)을 Docker로 돌려 보니, 이미 퇴역한 macos-13은 "label is unknown"으로 잡았지만 macos-14, macos-14-large는 통과시켰다. 퇴역이 끝난 레이블만 걸러 내고, 지원 중단 '예정' 레이블은 경고하지 않는 셈이다. 린트가 초록불이어도 11월 2일에는 깨진다.
옮기는 순서
- 저장소 전체에서 레이블을 찾는다.
runs-on:만이 아니라 문자열 전체를 본다.
조직 단위라면grep -rnE 'macos-14(-large|-xlarge)?([^0-9]|$)' .github/gh search code 'macos-14 path:.github/workflows' --owner <조직명>으로 한 번에 훑을 수 있다. macos-14-large를 쓰는 잡은 Intel이 정말 필요한지부터 따진다. 필요하면macos-15-intel, 아니면 arm64 레이블로 간다.- Xcode 버전을 기본값에 맡기지 말고 고정한다. 아래처럼
xcode-select로 경로를 지정하면 이미지 기본값이 바뀌어도 영향이 없다. - 시뮬레이터 대상은 새 이미지에 실제로 있는 기기 이름과 OS 버전으로 바꾼다. 스텝에서
xcrun simctl list runtimes를 한 번 출력해 두면 다음 전환 때도 바로 확인할 수 있다. - CocoaPods·Fastlane처럼 Ruby를 쓰는 도구는 macos-26에서 Ruby 3.4로 바뀌니
Gemfile.lock과 함께 한 번 돌려 본다. Mono·NuGet에 의존하던 스크립트(Xamarin 잔재 등)는 15부터 동작하지 않는다.
먼저 15와 26을 나란히 돌려 보는 점검용 워크플로다. actionlint 1.7.12로 문법 검사를 통과한 것을 확인했다.
# .github/workflows/macos-migration-check.yml
name: macOS 14 이전 점검
on:
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-15
xcode: "16.4"
device: "iPhone 16"
ios: "18.5"
- os: macos-26
xcode: "26.6"
device: "iPhone 17"
ios: "26.5"
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
- name: 설치된 Xcode·시뮬레이터 확인
run: |
sw_vers
ls -d /Applications/Xcode_*.app
xcrun simctl list runtimes
- name: Xcode 고정
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
- name: 테스트
run: |
xcodebuild test -scheme MyApp \
-destination "platform=iOS Simulator,name=${{ matrix.device }},OS=${{ matrix.ios }}"
MyApp은 각자 스킴 이름으로 바꾼다. 기기·OS 조합은 위 README 대조 결과에서 각 이미지에 실제로 있는 것으로 골랐다.
15로 갈까, 26으로 갈까
개인적으로는 앱을 배포하는 팀이라면 26(macos-latest)으로 바로 가는 게 낫다고 본다. App Store 제출에 Xcode 26이 필수인데, 15 이미지에 든 Xcode 26은 26.3까지이고 최신 26.6은 26 이미지에만 있다. "최신 두 버전만 유지" 원칙대로라면 15도 언젠가 지금의 14와 같은 절차를 밟는다 — 두 번 옮길 이유가 없다. 반대로 Xcode 16 기반 SDK·라이브러리 빌드를 당분간 유지해야 한다면 15가 현실적이다. 16.x와 26.x를 모두 갖춘 이미지는 지금 15뿐이다.
macos-latest를 쓸 때는 한 가지만 기억하면 된다. 이 레이블은 언젠가 또 다음 OS로 넘어간다. 우분투 쪽에서도 같은 일이 진행 중이다 — ubuntu-latest가 26.04로 넘어가는 과정은 우분투 26.04 러너 전환 글에 정리해 뒀다. 리눅스와 macOS 잡을 같이 돌리는 저장소라면 이번 달에 두 전환을 한꺼번에 점검하는 편이 덜 번거롭다.
보안 쪽으로는 macOS 14 러너 이미지와 관련해 공개된 사고나 CVE를 찾지 못했다. 이번 퇴역은 순수하게 지원 정책에 따른 일정이다. 그래서 더 미루기 쉽다. 브라운아웃은 10월 5일부터 31일 아침까지 여덟 차례 잡혀 있다.
actions/runner-images 이슈·README 문장은 MIT 라이선스 저장소에서, GitHub 문서(docs.github.com)는 CC BY 4.0 라이선스로 참고했다. GitHub 체인지로그와 Apple 개발자 공지는 원문을 짧게 인용했다.