feat(release): prepare 1.1.0 candidate

This commit is contained in:
Yun Chan 2026-08-29 18:33:45 +09:00
parent 5a34f66981
commit 5205dcdfa9
736 changed files with 115667 additions and 12203 deletions

View file

@ -1,111 +1,102 @@
# D3RO Voice 릴리스 가이드
agent-switchboard-client의 릴리스 체계를 이식한 멀티플랫폼 배포 파이프라인.
기준일: 2026-08-29. 이 문서는 desktop GitLab 패키지·자동 업데이트와 mobile store release의 경계를 분리한다. 태그 생성이나 HTTP 200 하나만으로 배포 완료를 선언하지 않는다.
## 파이프라인 개요
## 현재 release identity
```
git tag vX.Y.Z → push
├─ package-windows (runner: build-win-x64, TW-VIVEN-BUILD) → NSIS exe + latest.yml
├─ package-macos (runner: build-mac-arm64, TW-BUILD-MAC-ARM64) → dmg + zip + latest-mac.yml [D3RO_MAC_RUNNER="true"일 때만]
└─ release-create (runner: build-linux-x64 Docker, TW-BUILD01)
├─ Generic Package Registry 업로드
│ ├─ d3ro-voice/<version>/ (버전별 보관)
│ └─ d3ro-voice/latest/ (electron-updater feed — 매 릴리스 갱신)
└─ GitLab Release 생성 + asset 링크
```
- 버전은 태그에서 자동 동기화된다 (`scripts/ci/sync-version.mjs``v0.2.0` → package.json `0.2.0`).
- macOS artifacts가 없으면 Windows 단독으로 릴리스가 생성된다 (경고 로그만).
## 릴리스 절차
```bash
git tag v0.2.0
git push origin v0.2.0
```
끝. 파이프라인이 패키징 → 레지스트리 업로드 → Release 생성까지 자동 수행한다.
## 사용자가 1회 설정해야 하는 것
### 1. macOS runner (2026-07-21 기준 완료됨)
사내 공용 runner `TW-BUILD-MAC-ARM64`(tags: `build-mac-arm64`)가 온라인이며,
CI 변수 `D3RO_MAC_RUNNER="true"`도 설정 완료. 별도 등록 불필요.
Mac runner 환경 전제조건 (미충족 시 package-macos 잡 실패 — allow_failure라 릴리스는 진행):
- Xcode Command Line Tools, Node 22+(또는 volta), Python 3.11+
- `brew install sox` — install-sox.sh가 이 바이너리를 번들함
### 2. 자동 업데이트 활성화 (Windows) — 2026-07-21 기준 완료됨
1. 프로젝트 ID = **1172** (기입 완료)
2. **두 곳**에 같은 feed URL 기입 (어긋나면 안 됨):
- `apps/desktop/src/main/update-feed.ts``UPDATE_FEED_URL`
- `apps/desktop/electron-builder.yml``publish.url`
```
https://gitlab.twentyoz.kr:8443/api/v4/projects/1172/packages/generic/d3ro-voice/latest
```
`publish` 섹션은 latest.yml/latest-mac.yml **생성**을 트리거하는 필수 설정이다
(없으면 electron-builder가 update info 파일을 아예 만들지 않음).
3. `package_registry_access_level=public` 설정 완료 ✅ (2026-07-21, API로 적용) —
앱의 무인증 다운로드/랜딩 페이지 다운로드 링크의 전제. 비공개로 되돌리면 자동 업데이트가 401로 조용히 중단됨.
4. v0.1.1-alpha부터 배포된 앱이 4시간 주기로 업데이트를 체크한다
### 산출물 파일명 규칙
파일명에 공백 금지 — GitLab Generic Package Registry가 공백을 불허해
latest.yml의 url과 레지스트리 파일명이 어긋나면 업데이트 다운로드가 404 난다.
- Windows: `D3RO-Voice-Setup-<version>-x64.exe`
- macOS: `D3RO-Voice-<version>-arm64.dmg` / `.zip`
## 알려진 제약 / 후속 단계
| 항목 | 현재 상태 | 후속 |
| 항목 | 정본 | 현재 판정 |
|---|---|---|
| macOS 서명/공증 | ❌ ad-hoc 서명 (`identity=-`, switchboard 검증 조합) — Gatekeeper가 차단하면 우클릭→열기 또는 `xattr -dr com.apple.quarantine "/Applications/D3RO Voice.app"` | Apple Developer 계정($99/년) 확보 시 switchboard의 prepare/cleanup-macos-keychain.mjs + 공증 패턴 이식. 주의: D3RO는 asarUnpack native 모듈 + ollama/sox 바이너리 다중 서명 필요 |
| macOS 자동 업데이트 | ❌ 미지원 (Squirrel.Mac이 서명 요구) | 서명 도입 후 latest-mac.yml feed 연결 |
| 업데이트 무결성 서명 | latest.yml SHA-512 (electron-builder 기본) | switchboard의 Ed25519 update-policy 매니페스트 체계 이식 검토 |
| CHANGELOG | ✅ v0.2.0-alpha부터 운영 | `publish-gitlab-release.mjs``## [버전]` 섹션을 Release 노트로 자동 추출. CHANGELOG를 풍부하게 = Release 페이지 품질 직결 |
| 제품 버전 | `release/product-version.json`: `1.1.0` | source SSOT 확정 |
| Android | versionCode `1010001` | production AAB 미생성 |
| iOS | build `1010001` | production archive 미검증 |
| Android upload key | alias `d3ro-upload-20260821`, cert SHA-256 `4F:AC:69:24:...:15:2B:54` | external PKCS12·user-only ACL·Credential Manager·private-key readback GREEN; CI secret·복구 백업·AAB signer 대조 대기 |
| release evidence | Ed25519 public `release/mobile-release-evidence-public.pem`, keyId `2797d3e6...4a890b7f` | external private key ACL·roundtrip GREEN; CI private-key secret·복구 백업 대기 |
| desktop offline license | Ed25519 public `apps/desktop/resources/license/production-public.pem`, keyId `5c52b765...81a887f` | 새 전용 keypair·external private ACL·roundtrip·desktop production build GREEN; admin `ADMIN_LICENSE_PRIVATE_KEY` secret 주입 대기 |
| Firebase | Console `u/0`, `u/1` 모두 D3RO project 없음 | 사용자 승인 후 project·Android app 생성 필요 |
| AdMob | app `ca-app-pub-1039714767792854~6427959892`; banner `/9840591290`; rewarded `/2255790918` | SSOT 확정. `검토 필요`·`광고 게재 제한`·store 미연결·결제 프로필 미완료 |
| updater feed | `https://gitlab.twentyoz.kr:8443/api/v4/projects/1172/packages/generic/d3ro-voice/latest` | public `latest.yml`은 아직 `0.2.1-alpha`; `1.1.0` 미배포 |
| release notes | `CHANGELOG.md` `## [1.1.0]` | 태그 전 확정·검증 필수 |
## Release 페이지 운영 (v0.2.0-alpha부터)
source SSOT와 live updater metadata의 버전이 다르므로 아직 `1.1.0` 배포 완료가 아니다.
GitLab Release 페이지는 **CHANGELOG가 단일 진실 원천**이다. 릴리스 품질을 올기려면 CHANGELOG를 잘 쓴다.
## GitLab desktop 파이프라인
### Release 노트 = CHANGELOG 섹션 (자동)
`publish-gitlab-release.mjs``extractChangelogSection(changelog, version)`가 태그 버전(`0.2.0-alpha`)과 매칭되는 `## [0.2.0-alpha]` 섹션을 Release 노트로 추출. **CHANGELOG를 고치면 Release 노트에 자동 반영** — 릴리스 후수정해도 다음 릴리스에 반영되지 않으니 릴리스 "전"에 CHANGELOG를 확정.
### 권장 섹션 구조 (Keep a Changelog)
```text
authoritative release commit
→ version/check/test/build GREEN
→ tag v1.1.0
→ package-windows (build-win-x64)
→ package-macos (build-mac-arm64)
→ publish-release (build-linux-x64)
├─ packages/generic/d3ro-voice/1.1.0/ 버전별 보존
├─ packages/generic/d3ro-voice/latest/ latest updater feed
└─ GitLab Release + CHANGELOG release notes
```
## [버전] - YYYY-MM-DD
> 다운로드 안내 (Windows exe / macOS dmg 파일명 + 자동업데이트/서명 메모)
### Added / Changed / Fixed / Removed / Internal
- `scripts/ci/sync-version.mjs --check --tag v1.1.0`는 태그, `release/product-version.json`, package/lockfile, Android/iOS 버전 면의 일치를 fail-closed로 검증한다.
- `scripts/ci/verify-release-metadata.mjs`는 배포 메타데이터와 CI/publisher 계약을 검증한다.
- 같은 gate는 desktop license public key가 Ed25519이고 `release/product-version.json``desktopLicensePublicKeyId`와 일치하는지 검증한다. `electron.vite.config.ts`는 이 파일을 직접 읽으므로 누락·손상된 키로는 build가 시작되지 않는다.
- `scripts/ci/publish-gitlab-release.mjs`는 버전별 패키지를 먼저 올리고, `latest` 파일에서 설치 자산 참조를 검증한 후 update metadata를 마지막에 게시한다.
- Windows installer와 `latest.yml`은 필수다. macOS 산출물이 없는 Windows-only release를 의도했다면 그 판단을 release record에 남긴다.
## 자동 업데이트 계약
`apps/desktop/src/main/update-feed.ts``apps/desktop/electron-builder.yml`은 버전 없는 같은 public Generic Package Registry URL을 가리켜야 한다.
```text
https://gitlab.twentyoz.kr:8443/api/v4/projects/1172/packages/generic/d3ro-voice/latest
```
- 상단 인용 블록(`>`)에 다운로드 파일명·자동업데이트·macOS 서명 안내를 적으면 사용자가 Release 페이지에서 바로 확인.
- 내부 작업(refactor-wave 등)은 `### Internal`로 분리 — 사용자 관심사(Added/Fixed)가 위에 오도록.
### 릴리스 체크리스트
1. CHANGELOG `[버전]` 섹션 작성 (위 구조)
2. `memory/project_status.md` 최신화 (규칙 13)
3. `git tag vX.Y.Z && git push origin vX.Y.Z`
4. GitLab 파이프라인 모니터링 (package-windows / package-macos / release-create)
5. Release 페이지에서 노트·asset 링크·latest.yml 확인
6. (검증) 기존 앱에서 자동 업데이트 수신 확인
아래 면이 하나라도 깨지면 release를 중단한다.
### 버전 정책
- `0.x.y-alpha`: 개발 프리릴리스 (현재 단계). 자동업데이트 feed는 alpha 체널.
- minor 범프: 대규모 재설계/정리(Midnight Glass v2, refactor-wave). patch: 버그 수정 중심.
- alpha→정식 전환은 기능·안정성 기준 충족 시 (별도 합의).
| mac x64 (Intel) | 빌드 안 함 (사이드카가 arm64 전용) | 수요 발생 시 Intel Mac runner 추가 |
| **Linux (deb/AppImage)** | ❌ 미지원 — 사이드카 Linux 빌드·TTS 백엔드 부재 | **별도 Phase**(2026-07-22 합의). 사이드카 Linux PyInstaller(ctranslate2) + TTS 백엔드 신규(espeak-ng/Piper — 현재 Windows SAPI 전용) + electron-builder linux(deb+AppImage, afterInstall로 sox/의존 안내) + CI package-linux 잡(TW-BUILD01 Docker 툴체인 확장) + QA(Ubuntu/Debian/Fedora). switchboard 원본도 Windows-only라 참조 패턴 없음 |
1. package registry의 무인증 public pull이 허용됐다.
2. `latest.yml``version`이 태그와 일치한다.
3. `latest.yml` URL/path가 같은 `latest` 경로의 실제 installer를 참조한다.
4. installer 파일명에 공백이 없다: `D3RO-Voice-Setup-<version>-x64.exe`.
5. metadata SHA-512와 다운로드한 installer가 일치한다.
6. 이전 실제 설치본이 feed를 탐지하고, 다운로드·재시작·버전 상승을 끝까지 완료한다.
2026-08-29 live `latest.yml`의 버전은 `0.2.1-alpha`다. 이는 updater endpoint가 응답한다는 증거일 뿐 `1.1.0` 게시 증거가 아니다.
## `1.1.0` 릴리스 절차
1. `release/product-version.json`의 version/build 값과 모든 버전 면을 `npm run version:check`로 대조한다.
2. `CHANGELOG.md` `## [1.1.0] - 2026-08-29` 섹션을 사용자 변경점 중심으로 확정한다. publisher는 이 섹션이 없으면 실패해야 한다.
3. dirty/untracked 작업을 임의로 reset·clean하지 말고, release 범위만 검토 가능한 authoritative commit으로 보존한다.
4. 같은 commit에서 lint, typecheck, test, build, release metadata·security·artifact gate를 전부 GREEN으로 만든다.
5. desktop offline license를 제공한다면 external private key를 admin의 `ADMIN_LICENSE_PRIVATE_KEY` secret로 주입하고, 저장소 public key와 sign/verify roundtrip 및 발급 감사 로그를 확인한다.
6. 이전 버전보다 높은 태그 `v1.1.0`을 생성해 push한다. 태그는 게이트를 시작하는 후속 단계지 검증을 대체하지 않는다.
7. GitLab에서 package-windows, package-macos, publish-release와 의도한 mobile job 상태를 모두 확인한다. pending/stuck/skipped를 GREEN으로 기록하지 않는다.
8. 버전별 package, GitLab Release note/asset, `latest.yml`, installer hash를 외부 public URL에서 다시 검증한다.
9. 이전 설치본에서 자동 업데이트 E2E를 실행하고 실행 중 버전·프로세스·사용자 데이터 보존을 확인한다.
## mobile release와의 경계
Desktop GitLab Release를 게시해도 Android production 출시가 자동으로 완료되지 않는다. Android는 다음을 별도로 증명한다.
- 준비된 local upload/evidence key와 AdMob identity를 protected CI secret에 주입하고, 사용자 승인 후 생성한 production Firebase config와 함께 version `1.1.0`, versionCode `1010001` AAB 생성
- package/config/upload signer/ABI/16 KB page size/signed provenance GREEN
- public APK 게시 없이 Play internal track에 제한 업로드
- Play-signed 실기기 E2E, 12명·연속 14일 closed test, production access 승인
- Data safety/App content/production 선언은 사용자의 action-time 검토·승인 후에만 제출
현재 local upload/evidence key는 존재하지만 CI secret 주입·복구 백업·production Firebase·AAB가 없고 Play production access도 disabled이므로 Android production 출시는 RED다.
## rollback
- Desktop: 게시 전이면 `latest` metadata를 바꾸지 않는다. 이미 업데이트된 클라이언트는 downgrade하지 말고 더 높은 patch 버전으로 forward-fix한다.
- Play: staged rollout을 중단하고 더 높은 versionCode의 수정 AAB를 새로 검증·배포한다.
- 복구 작업도 release record에 artifact hash, 시각, 판단자, 영향 범위를 남긴다.
## 참고 파일
- `.gitlab-ci.yml` — 파이프라인 정의
- `scripts/ci/sync-version.mjs` — 태그 → package.json 버전 동기화
- `scripts/ci/publish-gitlab-release.mjs` — 레지스트리 업로드 + Release 생성
- `apps/desktop/src/main/services/UpdateService.ts` — 자동 업데이트 (electron-updater)
- `apps/desktop/src/main/update-feed.ts` — feed URL SSOT
- 원본 패턴: `D:\workspace\agent-switchboard-client` (.gitlab-ci.yml, scripts/publish-gitlab-release.mjs)
- `release/product-version.json` — 제품 version/build SSOT
- `release/android-release-identity.json` — Play app/package/certificate identity
- `release/mobile-release-evidence-public.pem` — release evidence public key
- `apps/desktop/resources/license/production-public.pem` — desktop offline license public key SSOT
- `scripts/ci/sync-version.mjs` — 버전 면 동기화·검증
- `scripts/ci/verify-release-metadata.mjs` — release metadata 자가 검증
- `scripts/ci/publish-gitlab-release.mjs` — registry·Release·updater feed publisher
- `apps/desktop/src/main/update-feed.ts` — runtime updater URL SSOT
- `apps/desktop/electron-builder.yml` — builder publish URL·artifact contract
- `docs/v3/play/04-release-checklist.md` — Play Console·AAB·closed test·production gate