feat(V2-5): macOS 빌드 지원 — 플랫폼 분기 + electron-builder mac 타겟 + CI
플랫폼 분기 (paths.ts): - EXE_SUFFIX 상수로 sox/sidecar/ffmpeg 실행파일 확장자 통합 - Windows에선 .exe 자동 부착, Mac/Linux에선 빈 문자열 - 미사용 getProjectRoot 헬퍼 제거 런타임 서비스 Mac 분기: - SoundEffectService: darwin → /usr/bin/afplay, linux → aplay 분기 추가 (execFile로 안전하게) - ScreenContextService._getActiveWindowInfo: win32 → PowerShell + user32.dll (기존), darwin → osascript (System Events frontmost process + 윈도우 타이틀) Linux는 미지원 (null) electron-builder.yml: - mac 타겟 추가 (dmg + zip, arm64 + x64 매트릭스) - hardenedRuntime, gatekeeperAssess, entitlements 설정 - extendInfo로 NSMicrophoneUsage / NSCameraUsage / NSAppleEvents / NSSystemAdministration 권한 메시지 - dmg 레이아웃 (드래그 to /Applications) - linux AppImage placeholder - notarize: false 기본, NOTARIZE 환경변수로 활성화 build/entitlements.mac.plist: - allow-jit, allow-unsigned-executable-memory (Electron 필수) - audio-input, camera, network.client - automation.apple-events (활성 윈도우 조회용) - files.user-selected.read-write - allow-dyld-environment-variables (sox/ffmpeg 라이브러리 로드) scripts/build-sidecar.py: - IS_WINDOWS / IS_MACOS / EXE_SUFFIX 도입 - Windows에서만 --noconsole 플래그 - 빌드 결과 경로 + size 출력 플랫폼 통합 scripts/install-sox.sh (신규): - Mac/Linux용 SoX 번들 스크립트 - macOS는 otool로 dylib 의존성 식별 후 함께 복사, install_name_tool로 rpath를 @loader_path로 변경 - electron-builder의 extraResources 대상 디렉토리에 배치 resources/icons/ (신규): - README.md만 커밋, 실제 아이콘 파일은 분리 - sips/iconutil/imagemagick으로 .icns/.ico/.png 생성 가이드 .github/workflows/build-mac.yml (신규): - macos-14 runner (Apple Silicon), arm64/x64 matrix - brew sox, npm install, @electron/rebuild, install-sox.sh, build-sidecar.py, electron-builder dist - CSC/NOTARIZE 환경변수 자동 처리 - artifact 업로드 (dmg + zip, retention 7일) docs/v2/phase-V2-5-mac-guide.md (신규): - 사전 조건, 시스템 의존성, dev 실행, dist 빌드, Code signing + Notarization, CI 트리거, 트러블슈팅 검증 (Windows에서): - typecheck 통과 (Mac 분기 추가에도 회귀 없음) - build 통과 - dev 런타임 정상 Mac 검증은 사용자 본인 Mac에서 수행 (V2-5 사용자 액션).
This commit is contained in:
parent
97eb886ec3
commit
77d514222e
12 changed files with 780 additions and 79 deletions
235
docs/v2/phase-V2-5-mac-guide.md
Normal file
235
docs/v2/phase-V2-5-mac-guide.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# Phase V2-5: macOS 빌드 / 실행 / 배포 가이드
|
||||
|
||||
> 본인 Mac에서 D3RO Voice를 dev 실행하거나 dist(.dmg/.zip) 빌드하기 위한 단계별 가이드.
|
||||
> Windows에서 작성된 코드는 V2-5에서 모든 플랫폼 분기를 추가했습니다 — Mac에서는 별도 코드 수정 없이 아래 단계만 따르면 됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 0. 사전 조건
|
||||
|
||||
- **macOS 12 (Monterey) 이상** 권장
|
||||
- **Apple Silicon(M1/M2/M3) 또는 Intel** 둘 다 지원
|
||||
- Xcode Command Line Tools (`xcode-select --install`)
|
||||
- Homebrew 설치 (`brew --version`으로 확인)
|
||||
|
||||
## 1. 시스템 의존성 설치
|
||||
|
||||
```bash
|
||||
# 기본 도구
|
||||
brew install node@22 python@3.11 git
|
||||
|
||||
# 오디오 캡처 (필수)
|
||||
brew install sox
|
||||
|
||||
# Ollama (로컬 LLM, 선택)
|
||||
brew install ollama
|
||||
ollama serve & # 백그라운드 실행
|
||||
ollama pull qwen3:4b
|
||||
```
|
||||
|
||||
## 2. 프로젝트 클론 + 설치
|
||||
|
||||
```bash
|
||||
git clone https://github.com/yunchan8804/d3ro-voice.git
|
||||
cd d3ro-voice
|
||||
|
||||
# npm workspace 설치 (모든 패키지 한 번에)
|
||||
npm install
|
||||
|
||||
# native 모듈 재빌드 (electron 33 ABI에 맞게)
|
||||
npx @electron/rebuild@3 --version=33.4.11
|
||||
```
|
||||
|
||||
## 3. SoX 바이너리 번들 (선택, dev에서는 PATH로 충분)
|
||||
|
||||
dev 실행만 할 거라면 `brew install sox`만으로 충분합니다. `paths.ts`가 `sox` 시스템 명령을 자동 fallback으로 사용합니다.
|
||||
|
||||
dist 빌드를 할 거라면 `resources/sox/`에 바이너리를 미리 복사:
|
||||
|
||||
```bash
|
||||
cd apps/desktop
|
||||
bash scripts/install-sox.sh
|
||||
```
|
||||
|
||||
이 스크립트는:
|
||||
1. `command -v sox`로 시스템 sox 위치 찾기
|
||||
2. `apps/desktop/resources/sox/`에 복사
|
||||
3. `otool -L`로 dylib 의존성 확인 후 함께 복사
|
||||
4. `install_name_tool`로 rpath를 `@loader_path`로 변경 (앱 번들 내부에서도 동작)
|
||||
|
||||
## 4. Python sidecar 빌드 (faster-whisper)
|
||||
|
||||
dev에서도 sidecar는 Python 직접 실행. dist에서는 PyInstaller로 묶어야 함.
|
||||
|
||||
```bash
|
||||
cd apps/desktop
|
||||
|
||||
# 가상 환경 (선택, 권장)
|
||||
python3.11 -m venv .venv-sidecar
|
||||
source .venv-sidecar/bin/activate
|
||||
|
||||
pip install pyinstaller
|
||||
pip install -r sidecar/requirements.txt
|
||||
|
||||
# 빌드 (sidecar-dist/sidecar/sidecar 생성)
|
||||
python scripts/build-sidecar.py
|
||||
```
|
||||
|
||||
빌드 결과: `apps/desktop/sidecar-dist/sidecar/sidecar` (실행 파일)
|
||||
|
||||
`electron-builder.yml`이 이 경로를 `extraResources`로 번들합니다.
|
||||
|
||||
## 5. 아이콘 준비 (dist 빌드 전)
|
||||
|
||||
`apps/desktop/resources/icons/`는 placeholder 디렉토리입니다.
|
||||
|
||||
```bash
|
||||
cd apps/desktop/resources/icons
|
||||
|
||||
# 1024x1024 PNG 원본을 icon.png로 저장한 뒤:
|
||||
mkdir -p icon.iconset
|
||||
sips -z 16 16 icon.png --out icon.iconset/icon_16x16.png
|
||||
sips -z 32 32 icon.png --out icon.iconset/icon_16x16@2x.png
|
||||
sips -z 32 32 icon.png --out icon.iconset/icon_32x32.png
|
||||
sips -z 64 64 icon.png --out icon.iconset/icon_32x32@2x.png
|
||||
sips -z 128 128 icon.png --out icon.iconset/icon_128x128.png
|
||||
sips -z 256 256 icon.png --out icon.iconset/icon_128x128@2x.png
|
||||
sips -z 256 256 icon.png --out icon.iconset/icon_256x256.png
|
||||
sips -z 512 512 icon.png --out icon.iconset/icon_256x256@2x.png
|
||||
sips -z 512 512 icon.png --out icon.iconset/icon_512x512.png
|
||||
sips -z 1024 1024 icon.png --out icon.iconset/icon_512x512@2x.png
|
||||
iconutil -c icns icon.iconset
|
||||
rm -r icon.iconset
|
||||
```
|
||||
|
||||
자세한 내용: `apps/desktop/resources/icons/README.md`
|
||||
|
||||
## 6. dev 실행
|
||||
|
||||
```bash
|
||||
# 루트에서 (npm workspace가 자동으로 apps/desktop 호출)
|
||||
npm run dev
|
||||
```
|
||||
|
||||
처음 실행 시 macOS가 다음 권한을 요구합니다:
|
||||
|
||||
| 권한 | 사용처 | 거부 시 영향 |
|
||||
|---|---|---|
|
||||
| 마이크 | 음성 인식 (필수) | 녹음 불가 |
|
||||
| Accessibility | 전역 핫키 (uiohook-napi), 활성 윈도우 조회 (AppleScript) | 핫키/스크린 컨텍스트 동작 안 함 |
|
||||
| 화면 녹화 | 스크린 컨텍스트 캡처 (선택) | OCR/LLM 컨텍스트 비어있음 |
|
||||
| Apple Events | 활성 앱 이름 조회 (osascript) | appName이 null |
|
||||
|
||||
권한은 **시스템 설정 → 개인 정보 보호 및 보안**에서 수동으로도 조정 가능합니다.
|
||||
|
||||
## 7. dist 빌드 (DMG/ZIP)
|
||||
|
||||
```bash
|
||||
cd apps/desktop
|
||||
|
||||
# Apple Silicon만
|
||||
npx electron-builder --mac --arm64
|
||||
|
||||
# Intel만
|
||||
npx electron-builder --mac --x64
|
||||
|
||||
# 둘 다
|
||||
npx electron-builder --mac
|
||||
```
|
||||
|
||||
결과: `apps/desktop/release/<version>/`
|
||||
- `D3RO Voice-1.0.0-arm64.dmg`
|
||||
- `D3RO Voice-1.0.0-arm64.zip`
|
||||
- `D3RO Voice-1.0.0.dmg` (intel)
|
||||
- `D3RO Voice-1.0.0.zip` (intel)
|
||||
|
||||
## 8. Code Signing + Notarization (배포용)
|
||||
|
||||
### 8.1 Apple Developer 인증서 준비
|
||||
|
||||
1. https://developer.apple.com → Account → Certificates
|
||||
2. `+` → `Developer ID Application` (배포용) 생성
|
||||
3. 다운로드한 `.cer`을 키체인에 추가
|
||||
4. 키체인에서 **인증서 + 개인 키**를 함께 export → `.p12` 파일 (암호 설정)
|
||||
|
||||
### 8.2 환경 변수
|
||||
|
||||
```bash
|
||||
# Code signing
|
||||
export CSC_LINK="$(base64 -i path/to/cert.p12)"
|
||||
export CSC_KEY_PASSWORD="<p12 암호>"
|
||||
|
||||
# Notarization (App-specific password 필요)
|
||||
export APPLE_ID="your-apple-id@example.com"
|
||||
export APPLE_APP_SPECIFIC_PASSWORD="abcd-efgh-ijkl-mnop" # appleid.apple.com에서 생성
|
||||
export APPLE_TEAM_ID="ABCDE12345"
|
||||
```
|
||||
|
||||
App-specific password 생성: https://appleid.apple.com → Sign-In and Security → App-Specific Passwords
|
||||
|
||||
### 8.3 서명+공증 빌드
|
||||
|
||||
```bash
|
||||
cd apps/desktop
|
||||
npx electron-builder --mac -c.mac.notarize=true
|
||||
```
|
||||
|
||||
공증은 보통 5~30분 걸립니다. 완료되면 자동으로 staple됩니다.
|
||||
|
||||
검증:
|
||||
```bash
|
||||
spctl --assess --verbose=4 release/<version>/D3RO\ Voice.app
|
||||
# accepted source=Notarized Developer ID
|
||||
```
|
||||
|
||||
## 9. CI 빌드 (GitHub Actions)
|
||||
|
||||
`.github/workflows/build-mac.yml` 워크플로우를 사용하여 자동 빌드:
|
||||
|
||||
```bash
|
||||
# 태그 푸시 시 자동 트리거
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
또는 GitHub UI에서 `Actions → Build macOS → Run workflow`.
|
||||
|
||||
### 필요한 GitHub Secrets
|
||||
|
||||
| Secret | 설명 |
|
||||
|---|---|
|
||||
| `APPLE_ID` | Apple 계정 이메일 |
|
||||
| `APPLE_APP_SPECIFIC_PASSWORD` | App-specific password |
|
||||
| `APPLE_TEAM_ID` | 10자리 팀 ID |
|
||||
| `MAC_CERT_P12_BASE64` | `.p12` 인증서를 base64 인코딩한 문자열 |
|
||||
| `MAC_CERT_P12_PASSWORD` | `.p12` 암호 |
|
||||
|
||||
`workflow_dispatch`로 수동 실행 시 `notarize=true`를 선택하면 공증까지 진행.
|
||||
|
||||
## 10. 트러블슈팅
|
||||
|
||||
### "App is damaged and can't be opened" (서명 없는 dmg)
|
||||
```bash
|
||||
xattr -cr /Applications/D3RO\ Voice.app
|
||||
```
|
||||
또는 시스템 설정 → 보안 → "확인 없이 열기"
|
||||
|
||||
### `sox: command not found` (dev 모드)
|
||||
```bash
|
||||
brew install sox
|
||||
```
|
||||
|
||||
### `ImportError: dlopen failed` (sidecar 실행 시)
|
||||
PyInstaller 빌드가 현재 Mac 아키텍처와 다를 가능성. arm64 Mac에서 `arch -x86_64 python` 같은 cross 빌드는 권장하지 않음.
|
||||
|
||||
### Electron 앱이 즉시 종료
|
||||
```bash
|
||||
# 콘솔 로그 확인
|
||||
~/Library/Logs/d3ro-voice/main.log
|
||||
# 또는 dev 모드로 직접 실행
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Accessibility 권한 거부 후 재요청
|
||||
시스템 설정 → 개인정보보호 → 손쉬운 사용 → 좌하단 자물쇠 해제 → D3RO Voice 체크
|
||||
권한 변경 후 앱 재시작.
|
||||
Loading…
Add table
Add a link
Reference in a new issue