d3ro-voice/apps/desktop/electron-builder.yml
yunchan8804 77d514222e 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 사용자 액션).
2026-04-08 15:57:16 +09:00

114 lines
4.2 KiB
YAML

appId: com.d3ro.voice
productName: D3RO Voice
copyright: Copyright © 2026 D3RO
directories:
buildResources: resources
output: release/${version}
files:
- out/**/*
- "!out/**/*.map"
# native 모듈은 asar 외부에 배치
asarUnpack:
- "node_modules/better-sqlite3/**"
- "node_modules/uiohook-napi/**"
- "node_modules/@nut-tree-fork/**"
# ────────────────────────────────────────────────────────────────────
# Windows
# ────────────────────────────────────────────────────────────────────
win:
target:
- target: nsis
arch:
- x64
icon: resources/icons/icon.ico
nsis:
oneClick: false
perMachine: false
allowToChangeInstallationDirectory: true
createDesktopShortcut: true
createStartMenuShortcut: true
shortcutName: D3RO Voice
deleteAppDataOnUninstall: false
# ────────────────────────────────────────────────────────────────────
# macOS
# ────────────────────────────────────────────────────────────────────
mac:
category: public.app-category.productivity
icon: resources/icons/icon.icns
target:
- target: dmg
arch:
- arm64
- x64
- target: zip
arch:
- arm64
- x64
hardenedRuntime: true
gatekeeperAssess: false
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.plist
# notarize: 환경변수 APPLE_ID / APPLE_APP_SPECIFIC_PASSWORD / APPLE_TEAM_ID 설정 시 활성화.
# CI/dev 환경에서는 NOTARIZE=false로 스킵 가능.
notarize: false
extendInfo:
NSMicrophoneUsageDescription: "D3RO Voice는 음성 인식을 위해 마이크 접근이 필요합니다."
NSCameraUsageDescription: "D3RO Voice는 스크린 컨텍스트 캡처를 위해 화면 녹화 권한이 필요합니다."
NSAppleEventsUsageDescription: "D3RO Voice는 활성 앱 정보를 가져오기 위해 다른 앱 제어 권한이 필요합니다."
NSSystemAdministrationUsageDescription: "D3RO Voice는 전역 핫키 등록을 위해 접근성 권한이 필요합니다."
dmg:
writeUpdateInfo: false
contents:
- x: 130
y: 220
- x: 410
y: 220
type: link
path: /Applications
# ────────────────────────────────────────────────────────────────────
# Linux (참고용)
# ────────────────────────────────────────────────────────────────────
linux:
target:
- AppImage
category: Utility
icon: resources/icons/icon.png
# ────────────────────────────────────────────────────────────────────
# 공통 리소스 번들
# ────────────────────────────────────────────────────────────────────
extraResources:
# 효과음 (플랫폼 공통)
- from: resources/sounds/
to: sounds/
filter:
- "*.wav"
# 앱 아이콘 (트레이 등 런타임에서 참조)
- from: resources/icons/
to: icons/
filter:
- "**/*"
# SoX 바이너리 (번들되어 있으면 번들, 없으면 시스템 PATH)
- from: resources/sox/
to: sox/
filter:
- "**/*"
# Python sidecar (PyInstaller 빌드 결과)
# 빌드 전: python scripts/build-sidecar.py (플랫폼에서 개별 실행)
- from: sidecar-dist/sidecar/
to: sidecar/
filter:
- "**/*"
npmRebuild: true