feat(release): switchboard 릴리스 체계 이식 — Mac 배포 + Registry 게시 + 자동 업데이트

- package-macos CI job: arm64 무서명(ad-hoc) dmg+zip, D3RO_MAC_RUNNER 변수 게이트
- sync-version.mjs: 태그 → package.json 버전 동기화
- publish-gitlab-release.mjs: Generic Package Registry(버전별+latest) 업로드 + Release 생성
- electron-builder: publish generic(latest.yml 생성), 공백 없는 artifactName, mac arm64 단일화
- UpdateService: electron-updater 4h 주기 체크 + 재시작 다이얼로그 (update-feed.ts SSOT)
- @rollup/rollup-win32-x64-msvc → optionalDependencies (mac npm ci EBADPLATFORM 해소)
- docs/deployment/release-guide.md: runner 등록·feed 설정·파일명 규칙 가이드

리뷰 워크플로(3관점 적대적 검증)로 확정된 결함 8건 반영:
publish 부재로 latest.yml 미생성, 파일명 공백 vs 레지스트리 404,
mac EBADPLATFORM, needs optional의 실패 미커버, DELETE 권한 의존,
CHANGELOG regex 경계, --arm64 무시, 다이얼로그 parent 부재
This commit is contained in:
Yun Chan 2026-07-21 12:43:16 +09:00
parent 8f7d300b89
commit 3beba99668
11 changed files with 776 additions and 2291 deletions

View file

@ -86,6 +86,8 @@ package-windows:
- python --version
- npm ci
script:
# 0) 태그 → package.json 버전 동기화 (설치파일명/latest.yml 일치)
- node scripts/ci/sync-version.mjs
# 1) Python sidecar 의존성 + PyInstaller 빌드
- python -m pip install --upgrade pip
- python -m pip install pyinstaller
@ -97,7 +99,7 @@ package-windows:
- powershell -ExecutionPolicy Bypass -File apps/desktop/scripts/download-ollama.ps1
# 4) electron-vite 빌드 + electron-builder NSIS 패키징
- npm run build --workspace=@d3ro/desktop
- npm run dist --workspace=@d3ro/desktop
- npm run dist:win --workspace=@d3ro/desktop
artifacts:
name: "d3ro-voice-windows-${CI_COMMIT_SHORT_SHA}"
# win-unpacked/ (수GB)은 제외하고 최종 .exe 설치파일 + 메타만 업로드.
@ -114,8 +116,61 @@ package-windows:
allow_failure: true
# ────────────────────────────────────────────────────────────────────
# Release — 같은 Windows runner에서 GitLab API를 curl로 호출해 Release 생성.
# release-cli Docker 이미지 대신 shell+curl로 처리하여 Docker runner 불필요.
# Package macOS — arm64 무서명 dmg+zip. (agent-switchboard 파이프라인 패턴 이식)
# self-hosted mac runner (shell executor, tags: macos+arm64) 필요.
# 전제: brew install sox, Xcode CLT, Python 3.11+.
# 서명/공증은 후속 단계 (현재 notarize:false — 사용자는 우클릭→열기로 실행).
# ────────────────────────────────────────────────────────────────────
package-macos:
stage: package
tags:
- macos
- arm64
before_script:
# 무서명(ad-hoc) 빌드 강제 — runner에 인증서가 있어도 집어쓰지 않게 차단
# (agent-switchboard의 dist:mac:unsigned 검증된 조합)
- export CSC_IDENTITY_AUTO_DISCOVERY=false
- node --version
- python3 --version
- npm ci
script:
# 0) 태그 → package.json 버전 동기화
- node scripts/ci/sync-version.mjs
# 1) Python sidecar — PEP 668(externally-managed) 회피를 위해 venv 사용
- python3 -m venv apps/desktop/sidecar/.venv-ci
- apps/desktop/sidecar/.venv-ci/bin/pip install --upgrade pip
- apps/desktop/sidecar/.venv-ci/bin/pip install pyinstaller
- apps/desktop/sidecar/.venv-ci/bin/pip install -r apps/desktop/sidecar/requirements.txt
- apps/desktop/sidecar/.venv-ci/bin/python apps/desktop/scripts/build-sidecar.py
# 2) SoX 번들 (brew sox → resources/sox/ 복사 + dylib rpath 재배치)
- bash apps/desktop/scripts/install-sox.sh
# 3) Ollama 다운로드 (darwin tgz)
- bash apps/desktop/scripts/download-ollama.sh
# 4) electron-vite 빌드 + electron-builder dmg/zip (arm64)
- npm run dist:mac --workspace=@d3ro/desktop
artifacts:
name: "d3ro-voice-macos-${CI_COMMIT_SHORT_SHA}"
paths:
- apps/desktop/release/*/*.dmg
- apps/desktop/release/*/*.zip
- apps/desktop/release/*/latest-mac.yml
- apps/desktop/release/*/*.blockmap
expire_in: 90 days
rules:
# mac runner 등록 후 프로젝트 CI/CD 변수 D3RO_MAC_RUNNER="true" 설정 시에만 실행.
# 변수 미설정 시 잡이 생성되지 않아(release-create의 optional needs) 릴리스가 막히지 않는다.
# allow_failure: mac 잡이 실패해도(needs가 성공 취급) Windows 단독 릴리스로 진행.
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/ && $D3RO_MAC_RUNNER == "true"
allow_failure: true
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $D3RO_MAC_RUNNER == "true"
when: manual
allow_failure: true
# ────────────────────────────────────────────────────────────────────
# Release — artifacts를 Generic Package Registry(버전별 + latest)에 업로드하고
# GitLab Release + asset 링크를 생성. (agent-switchboard 패턴 이식)
# latest 패키지 경로는 electron-updater feed로 사용된다.
# macOS artifacts는 optional — mac runner 미등록/실패 시 Windows 단독 릴리스.
# ────────────────────────────────────────────────────────────────────
release-create:
stage: release
@ -124,20 +179,12 @@ release-create:
- shell
needs:
- job: package-windows
artifacts: false
artifacts: true
- job: package-macos
artifacts: true
optional: true
resource_group: d3ro-voice-release
script:
- |
$artifactUrl = "$env:CI_PROJECT_URL/-/jobs/artifacts/$env:CI_COMMIT_TAG/download?job=package-windows"
$description = "## D3RO Voice $env:CI_COMMIT_TAG`n`nWindows 설치 파일은 [package-windows 잡 artifacts]($artifactUrl)에서 받을 수 있습니다.`n`n- 기본 LLM 모델: ``gemma4:e4b`` (첫 실행 시 자동 다운로드, ~9.6GB)`n- Whisper 모델: ``large-v3`` (첫 전사 시 자동 다운로드, ~3GB)`n- 설치 마법사가 Visual C++ 재배포 패키지를 자동 다운로드/설치"
$body = @{
name = "D3RO Voice $env:CI_COMMIT_TAG"
tag_name = "$env:CI_COMMIT_TAG"
description = $description
} | ConvertTo-Json -Compress
Invoke-RestMethod `
-Uri "$env:CI_API_V4_URL/projects/$env:CI_PROJECT_ID/releases" `
-Method Post `
-Headers @{ "JOB-TOKEN" = "$env:CI_JOB_TOKEN" ; "Content-Type" = "application/json" } `
-Body $body
- node scripts/ci/publish-gitlab-release.mjs
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/