Intel Mac에서 Codex Desktop 다시 패키징하기
제가 쓰는 맥미니와 맥북프로는 둘 다 오래된 인텔 맥입니다. 2012년식 맥도 아직 쓰고 있는데, 당시에는 Codex Desktop의 인텔 맥용 공식 앱이 없었습니다. 쓰던 맥에서 한번 실행해 보고 싶어서 직접 다시 패키징해 보기로 했습니다.
지금은 인텔 맥용 공식 앱도 나왔지만, 이 글은 공식 지원이 없던 때의 작업 기록입니다. 제가 쓰려고 만든 스크립트를 GitHub에 올렸는데, 비슷한 상황인 분들이 있었는지 포크하거나 스타를 남긴 분들도 있었습니다.
앱을 통째로 복사하는 것으로 끝나지 않아 Electron 런타임과 SQLite·터미널 관련 모듈, 함께 실행할 CLI를 나눠 확인해야 했습니다.
수작업으로 이 파일들을 바꾸고 DMG까지 만들면, 중간에 하나가 실패했을 때 어느 단계부터 다시 해야 하는지 헷갈리기 쉽습니다. 그래서 반복할 부분을 codex-intel-mac 의 codex-intel.sh 한 파일로 정리했습니다.
이 글은 그 스크립트가 무엇을 입력받아 어떤 파일을 만드는지 설명합니다. 특정 원본 앱을 제 환경에 맞게 재구성한 기록이며 공식 Intel 배포판을 만든 것은 아닙니다. 원본 앱이 바뀌면 같은 구성으로 동작한다고 볼 수 없어, 아래 설명은 저장소에 남긴 스크립트와 당시 작업 범위를 기준으로 읽어야 합니다.
앱 안에서 실행되는 파일들
Electron 앱은 화면을 그리는 JavaScript만 들어 있는 묶음이 아닙니다. Electron 실행 파일과 여러 Helper, 네이티브 Node 모듈, 별도 CLI가 함께 들어갑니다. 이 중 한 파일만 대상 아키텍처와 맞지 않아도 앱이 시작되다가 멈추거나 특정 기능에서 실패할 수 있습니다.
예를 들어 Electron을 x64로 바꿨는데 better_sqlite3.node가 다른 아키텍처용이면 DB 모듈을 읽을 때 문제가 생깁니다. 터미널에는 node-pty의 모듈뿐 아니라 spawn-helper 같은 추가 실행 파일도 있습니다. 파일 이름이 같다는 이유로 실행 대상을 확인하지 않고 복사할 수는 없었습니다.
| 구성 | 스크립트에서 다루는 대상 |
|---|---|
| Electron | 메인 실행 파일, Framework, Helper 실행 파일 |
| SQLite | better-sqlite3의 better_sqlite3.node |
| 터미널 | node-pty의 pty.node, spawn-helper |
| CLI | 별도로 준비한 Intel codex 바이너리 |
| 업데이트 | 선택적으로 전달하는 sparkle.node |
| 배포 파일 | 다시 구성한 .app, 이를 담는 .dmg |
CPU 아키텍처 외에 Electron 버전도 맞춰야 합니다. 네이티브 모듈은 컴파일한 환경의 ABI에 의존합니다. 일반 Node.js에서 설치한 모듈을 그대로 Electron에서 읽을 수 있다고 가정하지 않고, 대상 Electron 버전에 맞춰 다시 빌드하도록 했습니다. 이 차이는 Electron 공식 네이티브 모듈 문서 에서도 설명합니다.
원본 파일과 빌드 결과 분리
스크립트는 원본 앱을 입력으로 사용합니다. 원본 DMG를 넘기면 읽기 전용으로 마운트해 앱을 복사하고, 이미 풀어 둔 앱이 있으면 그 경로를 사용할 수 있습니다. 원본을 직접 수정하는 대신 별도 출력 경로에서 재구성합니다.
작업 디렉터리에는 빌드에 필요한 패키지와 작업 중 생성된 파일을 두고, dist에는 최종 파일을 둡니다. 기본 작업 경로는 .build/codex-intel-build입니다.
codex-intel-mac/
codex-intel.sh
Codex.dmg # 별도로 준비하는 원본 입력
.build/
source/Codex.app # DMG에서 복사한 원본
codex-intel-build/ # npm 의존성과 재빌드한 모듈
dist/
Codex-Intel.app # --app을 선택한 경우
Codex-Intel.dmg # --dmg를 선택한 경우앱과 DMG는 선택한 모드에 따라 만들어집니다. 기본 전체 실행에서 DMG만 요청하면 중간 앱은 .build/tmp를 사용하고 정리하므로, DMG가 나왔다고 dist에 앱도 반드시 남아 있는 것은 아닙니다.
실행 환경과 첫 실행
이 스크립트는 Intel macOS에서 사용하는 흐름을 기준으로 작성했습니다. macOS의 hdiutil, ditto, codesign 등을 사용하기 때문에 Windows나 Linux에서 같은 명령으로 앱을 만들 수는 없습니다. Apple Silicon에서의 교차 빌드도 이 기록에서 확인한 범위는 아닙니다.
저장소를 받은 뒤 도움말부터 볼 수 있습니다.
git clone https://github.com/ersione/codex-intel-mac.git
cd codex-intel-mac
chmod +x codex-intel.sh
./codex-intel.sh --help인자 없이 실행하면 사용법과 빠른 시작 안내를 출력합니다. 원본 DMG는 저장소가 대신 제공하는 파일이 아니라 사용자가 별도로 준비해야 하는 파일입니다. 기본 위치인 ./Codex.dmg에 두거나 --source-dmg로 경로를 지정합니다.
빌드에는 npm·npx와 Xcode Command Line Tools가 필요합니다. 스크립트는 개발 도구가 없을 때 설치 안내를 표시하고 다시 실행하도록 멈춥니다. 설치 요청을 띄웠다는 것만으로 컴파일 도구가 준비됐다고 보고 계속 진행하지는 않습니다.
앱과 DMG 생성
가장 기본적인 실행은 원본 DMG를 지정해 최종 DMG를 만드는 것입니다.
./codex-intel.sh --source-dmg ./Codex.dmg --dmg앱을 직접 확인하고 DMG도 보관하려면 앱과 DMG의 저장 경로를 함께 지정합니다.
./codex-intel.sh \
--source-dmg ./Codex.dmg \
--app \
--dmg이 경우 기본 출력은 dist/Codex-Intel.app과 dist/Codex-Intel.dmg입니다. 공백이 있는 입력 경로는 따옴표로 감쌉니다.
이미 설치하거나 풀어 둔 앱을 입력으로 쓸 수도 있습니다.
./codex-intel.sh \
--source-app "/Applications/Codex.app" \
--app--source-app은 입력이고 --output-app은 출력입니다. 같은 경로로 지정하면 원본 보존이라는 작업 방식과 맞지 않으므로 별도 위치를 사용해야 합니다. 경로를 명시할 수 있게 한 것은 파일 이름에 의존하기보다 어떤 원본과 중간 결과를 쓰는지 확인하기 위해서였습니다.
네이티브 모듈 재빌드
소스의 기본값은 Electron 40.0.0, better-sqlite3 12.5.0, node-pty 1.1.0입니다. 이 숫자는 모든 원본 앱에 대한 권장 조합이 아니라 스크립트에 지정한 기본값입니다.
prepare_artifacts에서는 의존성을 설치한 뒤 다음과 같이 두 네이티브 모듈을 다시 빌드합니다. 아래는 실제 스크립트 의 명령입니다.
npx electron-rebuild \
-f \
-w better-sqlite3,node-pty \
--arch x64 \
--version "$ELECTRON_VERSION" \
--module-dir .이 명령은 스크립트가 준비한 npm 작업 디렉터리 안에서 실행합니다. 빈 폴더에서 이 부분만 복사해 실행하는 전체 설치 명령은 아닙니다. --version은 준비한 Electron과 맞추고 --arch x64는 모듈의 빌드 대상을 지정합니다.
결과물은 위치를 확인한 뒤 재패키징 단계에 전달합니다. node-pty처럼 빌드 출력과 미리 빌드된 파일 경로가 다를 수 있는 부분은 후보 경로를 확인합니다. 경로가 발견됐다는 것 외에 파일 존재 여부도 단계마다 검사합니다.
실패한 단계부터 다시 실행
작업을 하나의 명령으로만 묶으면 네이티브 모듈 빌드가 끝났는데 DMG 창 배치 때문에 실패해도 전체를 다시 실행하기 쉽습니다. 스크립트에는 빌드와 재패키징, DMG 생성을 별도로 실행하는 모드를 뒀습니다.
| 모드 | 실행 내용 | 필요한 이전 결과 |
|---|---|---|
--build-only | Electron과 네이티브 모듈 준비 | 빌드 도구 |
--repackage-only | 기존 빌드 결과로 앱 재구성 | .build의 의존성과 모듈, 원본 앱 |
--dmg-only | 앱을 DMG에 담기 | 이미 준비된 앱 |
# 빌드 결과만 먼저 준비
./codex-intel.sh --build-only
# 같은 작업 디렉터리를 사용해 앱 재구성
./codex-intel.sh \
--repackage-only \
--source-dmg ./Codex.dmg \
--app
# 앱을 다시 빌드하지 않고 DMG만 생성
./codex-intel.sh \
--dmg-only \
--dmg-source-app ./dist/Codex-Intel.app중간 결과 재사용에는 조건이 있습니다. 처음 빌드한 Electron 버전과 재패키징에 쓸 원본, 작업 디렉터리가 맞아야 합니다. 다른 버전의 .build를 남겨 둔 채 재패키징만 성공했다고 호환성이 맞춰진 것은 아닙니다.
CLI와 자동 업데이트 처리
Codex CLI도 별도 실행 파일입니다. 스크립트는 후보 경로에서 x86_64 바이너리를 찾거나 --cli-bin으로 받은 파일을 사용합니다. CLI를 찾지 못하면 번들에 넣지 않는 경로로 진행하므로, 앱을 만들었다는 사실만으로 필요한 CLI까지 포함됐다고 볼 수는 없습니다.
./codex-intel.sh \
--source-dmg ./Codex.dmg \
--cli-bin "/absolute/path/to/intel/codex" \
--app위 CLI 경로는 예시이며 실제 Intel 실행 파일로 바꿔야 합니다. 스크립트는 file 출력에 x86_64가 있는지도 검사합니다. 이 확인은 아키텍처 확인이지 해당 CLI 버전과 앱의 모든 기능 조합을 검증하는 테스트는 아닙니다.
자동 업데이트에 사용하는 sparkle.node는 기본적으로 제거합니다. 별도로 준비한 파일은 --sparkle-node로 전달할 수 있지만, 기본 재패키징 결과에서 공식 앱의 자동 업데이트가 그대로 유지된다고 설명할 수는 없습니다.
서명과 실제 기능 확인
원본의 실행 파일을 교체하면 기존 서명을 그대로 유지할 수 없습니다. 스크립트는 재구성한 앱을 ad-hoc 서명하고 codesign --verify를 실행합니다. 이 결과는 개발자 배포 서명이나 notarization을 새로 받은 결과와 다릅니다.
앱을 별도로 남겼다면 실행 파일의 아키텍처와 서명을 다음처럼 확인할 수 있습니다.
file ./dist/Codex-Intel.app/Contents/MacOS/Codex
codesign --verify --deep --strict ./dist/Codex-Intel.app확인 순서도 나눠야 합니다. 앱의 메인 실행 파일이 맞는지, Helper와 네이티브 모듈이 로드되는지, 실제 화면과 터미널이 동작하는지는 서로 다른 확인입니다. DMG 생성까지 끝나도 로그인과 모든 화면, 장시간 실행과 자동 업데이트까지 검증된 것은 아닙니다.
당시 작업은 원본 앱과 재빌드한 실행 파일들을 묶는 절차를 스크립트로 남긴 데 의미가 있었습니다. 원본 버전이나 구성 파일이 바뀌면 경로와 호환성을 다시 확인해야 합니다. 이후 맥미니 OS를 올리고 서비스용 Node·CLI 경로를 정리한 작업은 맥미니 실행 환경 글에서 다룹니다.