Two tagged release pipelines failed and no installer has been published since 1.1.0. The cause is not in the build: the release repository has no Actions secrets at all, so every run stops at the signing guard. A helper now reports which release secrets are missing and registers them once the signing material exists, and the release guide documents the required values, the Forgejo-side check, and how to re-run a pipeline for an existing tag without recreating it.
17 KiB
D3RO Voice 릴리스 가이드
기준일: 2026-09-18. 이 문서는 desktop GitLab 패키지·자동 업데이트와 mobile store release의 경계를 분리한다. 태그 생성이나 HTTP 200 하나만으로 배포 완료를 선언하지 않는다.
현재 release identity
| 항목 | 정본 | 현재 판정 |
|---|---|---|
| 제품 버전 | release/product-version.json: 1.3.0 |
source SSOT 확정 |
| Android | versionCode 1030001 |
production AAB 미생성 |
| iOS | build 1030001 |
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 주입 대기 |
| Windows Authenticode | external public-trust code-signing certificate | production PFX 없음. Forgejo 저장소 시크릿 4종(WIN_CSC_LINK/WIN_CSC_KEY_PASSWORD/WIN_CSC_EXPECTED_SIGNER_SUBJECT/FORGEJO_TOKEN)이 0건이라 릴리스 파이프라인이 fail-closed. scripts/ci/set-forgejo-secrets.mjs --check로 확인한다. GREEN 전까지 게시 금지 |
| 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 (canonical) | https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/latest |
Forgejo Generic Registry. GitLab project 1172은 legacy mirror |
| release notes | CHANGELOG.md ## [1.3.0] + Play changelog 1030001.txt (ko/en) |
태그 전 확정·검증 필수 |
| 직전 게시본 | Forgejo Release v1.1.0 (2026-09-15 게시, unsigned installer) |
v1.2.0·v1.3.0 파이프라인은 Forgejo 저장소 시크릿이 없어 실패 = 게시본 없음. 설치본이 있는 마지막 버전은 1.1.0 |
live canonical feed(git.chanpaca.net/.../d3ro-voice/latest)의 latest.yml은 현재 1.1.0을 보고한다. 시크릿을 채우고 v1.3.0 파이프라인이 GREEN이 되면 그 값이 1.3.0으로 올라가고, 그때부터 기존 설치본이 자동 업데이트를 받는다.
desktop 릴리스 파이프라인
authoritative release commit
→ version/check/test/build GREEN
→ annotated tag v1.3.0
→ package-windows (build-win-x64)
→ package-macos (build-mac-arm64)
→ publish-release (build-linux-x64)
├─ publish-forgejo-release.mjs ← canonical
│ ├─ Forgejo Generic Registry /d3ro-voice/1.3.0/ (버전별 보존)
│ ├─ Forgejo Generic Registry /d3ro-voice/latest/ (updater feed + update-policy.json)
│ └─ Forgejo Release + CHANGELOG notes + 자산 첨부
└─ publish-gitlab-release.mjs ← legacy mirror (pre-Forgejo 설치본)
├─ GitLab Generic Registry /d3ro-voice/1.3.0/
├─ GitLab Generic Registry /d3ro-voice/latest/
└─ GitLab Release
-
Forgejo·GitHub Actions도 동일한
publish-forgejo-release.mjs로 수렴한다. 어느 빌더가 성공해도 canonical feed는 하나다. -
publish-forgejo-release.mjs는 설치 자산을 먼저,latest.yml을 마지막에 게시하고, 공개 URL에서 재검증한 뒤 Release 자산을 첨부한다. -
설치 바이너리는 저장소에 커밋하지 않는다. 배포 정본은 Forgejo feed이며,
site/public/releases/*/,apps/web/public/releases/*/,apps/api-server/wwwroot/releases/*/는 build graph 밖이라 배포 산출물에 포함되지 않는다. 사이트·웹 다운로드 센터는 로컬 경로가 아니라 feed URL을 링크한다. (역사적1.0.0자산만 추적 상태로 남아 있다.) -
scripts/ci/sync-version.mjs --check --tag v1.3.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-forgejo-release.mjs는 canonical이다. 버전별 패키지를 먼저 올리고,latest에서 설치 자산 참조를 검증한 뒤latest.yml과update-policy.json을 마지막에 게시하고 공개 URL에서 재검증한다.scripts/ci/publish-gitlab-release.mjs는 legacy mirror로 동일 자산을 GitLab에도 올린다. -
Windows installer와
latest.yml은 필수다. macOS 산출물이 없는 Windows-only release를 의도했다면 그 판단을 release record에 남긴다. -
package-windows는 external public-trust code-signing PFX를 protected file variableWIN_CSC_PFX_FILE로, 암호와 exact certificate subject를 protectedWIN_CSC_KEY_PASSWORD,WIN_CSC_EXPECTED_SIGNER_SUBJECT로 받아야 한다. GitHub Release도WIN_CSC_LINK,WIN_CSC_KEY_PASSWORD,WIN_CSC_EXPECTED_SIGNER_SUBJECT가 모두 없으면 실패한다. -
scripts/ci/verify-windows-release-artifact.ps1는 installer와 unpacked app의 AuthenticodeValid, exact signer subject, non-self-signed code-signing EKU, PE version,latest.ymlpath/size/SHA-512를 검증한다. 로컬 self-signedEverything2EverythingDev는 production 신뢰 인증서가 아니며 gate에서 명시적으로 거부한다. -
현재 GitLab Linux runner는 Shell executor이므로 job의
image:를 툴체인 근거로 간주하지 않는다..gitlab-ci.yml은scripts/ci/bootstrap-linux-toolchain.sh를 source해 .NET 10.0.302, Deno 2.8.1, Microsoft OpenJDK 17.0.20.1, Android command-line tools 15859902를 고정 해시로 검증·캐시한 뒤 실행한다.
자동 업데이트 계약
apps/desktop/src/main/update-feed.ts와 apps/desktop/electron-builder.yml은
버전 없는 같은 canonical Forgejo URL을 가리켜야 한다.
https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/latest
legacy mirror(제거 예정, 런타임 참조 금지):
https://gitlab.twentyoz.kr:8443/api/v4/projects/1172/packages/generic/d3ro-voice/latest
아래 면이 하나라도 깨지면 release를 중단한다.
- Forgejo Generic Registry의 무인증 public pull이 허용됐다 (
FORGEJO_TOKEN으로 게시, 익명으로 읽기). latest.yml의version이 태그와 일치한다.latest.ymlURL/path가 같은latest경로의 실제 installer를 참조한다.- installer 파일명에 공백이 없다:
D3RO-Voice-Setup-<version>-x64.exe. - metadata SHA-512와 다운로드한 installer가 일치한다.
- installer와
win-unpacked/D3RO Voice.exe모두 external public-trust certificate의 AuthenticodeValid이고, signer subject가 protected CI identity와 정확히 일치한다. - 이전 실제 설치본이 feed를 탐지하고, 다운로드·재시작·버전 상승을 끝까지 완료한다.
2026-09-16 live canonical latest.yml은 1.1.0을 보고한다(2026-09-15 hand-publish).
그 installer는 Authenticode 서명이 없으므로 정책상 정상 게시본이 아니다. 1.2.0은
서명 gate를 통과한 CI build로 이 값을 대체하는 forward-fix다. legacy GitLab feed는
여전히 0.2.1-alpha다.
업데이트 채널과 메이저/증분 정책
정책 정본은 release/update-policy.json이고, 런타임 로직은
apps/desktop/src/main/update-policy.ts다. publisher가 이 파일을 feed 루트에
게시하면 실행 중 앱이 내려받아 적용한다.
| 필드 | 의미 |
|---|---|
defaultChannel / channels |
latest(stable) · beta · alpha. allowPrerelease가 false면 stable 고객은 prerelease를 받지 않는다 |
minimumSupportedVersion |
이 미만 설치본은 업데이트가 필수(연기·건너뛰기 불가) |
forceInstallBelow |
이 미만은 다이얼로그 없이 즉시 설치. null이면 비활성 |
fullInstallOnMajorChange |
major 승격 시 blockmap 차분 대신 전체 설치자 |
fullInstallVersionGap |
같은 major에서 minor 갭이 이 값 이상이면 전체 설치자 |
stagingPercentage |
stable 업데이트를 노출할 사용자 비율(0~100). 강제 업데이트는 무시 |
killSwitch |
true면 업데이트 확인 자체를 중단 |
- 증분(delta): 기본.
.blockmap으로 변경 블록만 받는다. 첫 업데이트는 항상 full이다. - 전체(full): 현재 버전을 신뢰할 수 없거나, major 승격이거나, minor 갭이 임계 이상일 때
disableDifferentialDownload를 켠다. - 회수(rollback):
stagingPercentage를 낮추거나killSwitch를 켠다. 이미 배포된 버전은 되돌리지 않고 더 높은 patch로 forward-fix한다.
1.3.0 릴리스 절차 (canonical = Forgejo Actions)
선행 조건 — Forgejo 저장소 시크릿. .forgejo/workflows/release.yml은 아래 4개가
없으면 fail-closed로 중단한다. 실측(2026-09-18): 저장소 시크릿이 0건이라 v1.2.0과
v1.3.0 태그 파이프라인이 서명 가드에서 실패했고, 그래서 설치본·업데이트가 게시되지
않았다. 현재 상태 점검과 등록은 다음 한 줄로 한다.
node scripts/ci/set-forgejo-secrets.mjs --check
node --env-file-if-exists=.env scripts/ci/set-forgejo-secrets.mjs --write
| 시크릿 | 의미 |
|---|---|
WIN_CSC_LINK |
public-trust Authenticode PFX (base64). 개발용 Everything2EverythingDev는 거부된다 |
WIN_CSC_KEY_PASSWORD |
PFX password |
WIN_CSC_EXPECTED_SIGNER_SUBJECT |
인증서의 정확한 subject. verify-windows-release-artifact.ps1이 이 identity를 요구한다 |
FORGEJO_TOKEN |
write:package + write:repository (릴리스 게시 단계) |
release/product-version.json의 version/build 값과 모든 버전 면을npm run version:check로 대조한다.CHANGELOG.md## [1.3.0] - 2026-09-18섹션을 사용자 변경점 중심으로 확정한다. publisher는 이 섹션과 그에 대응하는 Play changelog(apps/mobile-rn/metadata/android/*/changelogs/<versionCode>.txt)가 없으면 실패한다.- dirty/untracked 작업을 임의로 reset·clean하지 말고, release 범위만 검토 가능한 authoritative commit으로 보존한다.
- 같은 commit에서 lint, typecheck, test, build, release metadata·security·artifact gate를 전부 GREEN으로 만든다.
- 로컬 전사 엔진 검증: 파이프라인은
sidecar:setup→sidecar:build→verify-sidecar-bundle.mjs를 패키징 전에 실행한다. 이 게이트가 없으면 설치본에 엔진이 빠진 채 게시된다(과거 실제 사고). 로컬에서electron-builder --dir만 볼 때는 서명 실패로 extraResources 복사가 중간에 끊기므로-c.win.forceCodeSigning=false로 확인한다. - desktop offline license를 제공한다면 external private key를 admin의
ADMIN_LICENSE_PRIVATE_KEYsecret로 주입하고, 저장소 public key와 sign/verify roundtrip 및 발급 감사 로그를 확인한다. - 이미 게시된 버전보다 높은 annotated 태그
v1.3.0을 생성해 push한다.npm run release:tag -- --dry-run으로 검증한 뒤npm run release:tag(GPG 사용 시-- --sign)와git push chanpaca v1.3.0를 실행한다. 태그는 불변이며 게이트를 시작하는 후속 단계지 검증을 대체하지 않는다. 이미 게시된 버전을 재게시하지 않는다: canonical publisher는 버전별 자산이 다른 바이트를 가지면 fail-closed로 중단한다. - 시크릿을 나중에 채웠다면 태그를 새로 만들 필요가 없다 —
release.yml은workflow_dispatch를 지원하므로 Forgejo UI에서 해당 태그 ref로 수동 실행한다. - Forgejo Actions run(
/actions/tasksAPI 또는 UI)에서 단계별 결과를 확인한다. pending/stuck/skipped를 GREEN으로 기록하지 않는다. - Forgejo Release note/asset,
latest.yml,update-policy.json, installer hash를 외부 public URL에서 다시 검증한다. - 이전 설치본에서 자동 업데이트 E2E를 실행하고 실행 중 버전·프로세스·사용자 데이터 보존, 그리고 업데이트 후 로컬 받아쓰기 1회 성공을 확인한다.
1.2.0 릴리스 절차 (기록)
release/product-version.json의 version/build 값과 모든 버전 면을npm run version:check로 대조한다.CHANGELOG.md## [1.2.0] - 2026-09-16섹션을 사용자 변경점 중심으로 확정한다. publisher는 이 섹션이 없으면 실패해야 한다.- dirty/untracked 작업을 임의로 reset·clean하지 말고, release 범위만 검토 가능한 authoritative commit으로 보존한다.
- 같은 commit에서 lint, typecheck, test, build, release metadata·security·artifact gate를 전부 GREEN으로 만든다. Windows는 production Authenticode PFX를 주입한 CI build와
verify-windows-release-artifact.ps1GREEN이 필수다. - desktop offline license를 제공한다면 external private key를 admin의
ADMIN_LICENSE_PRIVATE_KEYsecret로 주입하고, 저장소 public key와 sign/verify roundtrip 및 발급 감사 로그를 확인한다. - 이미 게시된 버전보다 높은 annotated 태그
v1.2.0을 생성해 push한다.npm run release:tag -- --dry-run으로 검증한 뒤npm run release:tag(GPG 사용 시-- --sign)와git push origin v1.2.0를 실행한다. 태그는 불변이며 게이트를 시작하는 후속 단계지 검증을 대체하지 않는다. 이미 게시된 버전을 재게시하지 않는다: canonical publisher는 버전별 자산이 다른 바이트를 가지면 fail-closed로 중단한다. - GitLab에서 package-windows, package-macos, publish-release와 의도한 mobile job 상태를 모두 확인한다. publish-release가 Forgejo와 GitLab 양쪽에 게시했는지 로그로 확인한다. pending/stuck/skipped를 GREEN으로 기록하지 않는다.
- Forgejo Release note/asset,
latest.yml,update-policy.json, installer hash를 외부 public URL에서 다시 검증한다. - 이전 설치본에서 자동 업데이트 E2E를 실행하고 실행 중 버전·프로세스·사용자 데이터 보존을 확인한다.
mobile release와의 경계
Desktop release를 게시해도 Android production 출시가 자동으로 완료되지 않는다. Android는 다음을 별도로 증명한다.
- 준비된 local upload/evidence key와 AdMob identity를 protected CI secret에 주입하고, 사용자 승인 후 생성한 production Firebase config와 함께 version
1.2.0, versionCode1020001AAB 생성 - 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: 게시 전이면
latestmetadata를 바꾸지 않는다. 이미 업데이트된 클라이언트는 downgrade하지 말고 더 높은 patch 버전으로 forward-fix한다. - Play: staged rollout을 중단하고 더 높은 versionCode의 수정 AAB를 새로 검증·배포한다.
- 복구 작업도 release record에 artifact hash, 시각, 판단자, 영향 범위를 남긴다.
참고 파일
release/product-version.json— 제품 version/build SSOTrelease/android-release-identity.json— Play app/package/certificate identityrelease/mobile-release-evidence-public.pem— release evidence public keyapps/desktop/resources/license/production-public.pem— desktop offline license public key SSOTscripts/ci/sync-version.mjs— 버전 면 동기화·검증scripts/ci/create-release-tag.mjs— 릴리스 태그 게이트 (annotated/서명, 불변)scripts/ci/verify-release-metadata.mjs— release metadata 자가 검증scripts/ci/verify-windows-release-artifact.ps1— Windows version·updater metadata·Authenticode gatescripts/ci/publish-gitlab-release.mjs— legacy GitLab registry·Release mirror publisherapps/desktop/src/main/update-feed.ts— runtime updater URL SSOT (canonical Forgejo + legacy mirror 상수)release/update-policy.json— 채널·최소 지원 버전·강제 업데이트·full/delta·staged rollout·킬 스위치 SSOTapps/desktop/src/main/update-policy.ts— 정책 파싱·결정 순수 로직scripts/ci/publish-forgejo-release.mjs— canonical Forgejo registry·Release·feed publisherapps/desktop/electron-builder.yml— builder publish URL·artifact contractdocs/deployment/update-system-assessment.md— 평가·2026 방법론·ADRdocs/v3/play/04-release-checklist.md— Play Console·AAB·closed test·production gate