From f14341ace4b8b5b8bb72b476345ce29bb02b1fa4 Mon Sep 17 00:00:00 2001 From: Yun Chan Date: Fri, 18 Sep 2026 16:35:03 +0900 Subject: [PATCH] fix(release): keep the updater configuration in the installer Installing the previous build left an app that could not update itself: the packaging path used to guarantee the native module build does not create the updater configuration file, so the update client had nothing to read. That file is now written from the single feed source and its presence in the packaged app is checked before anything is published, so an installer that cannot update can no longer be released. --- CHANGELOG.md | 9 +++ apps/admin-swagger/openapi.json | 2 +- apps/admin/package.json | 2 +- apps/api-server/D3ROVoice.Api.csproj | 2 +- apps/desktop/package.json | 2 +- apps/mobile-rn/android/app/build.gradle | 4 +- .../ios/D3ROVoice.xcodeproj/project.pbxproj | 8 +-- .../android/en-US/changelogs/1031004.txt | 1 + .../android/ko-KR/changelogs/1031004.txt | 1 + apps/mobile-rn/package-lock.json | 14 ++-- apps/mobile-rn/package.json | 2 +- apps/web/package.json | 2 +- apps/web/src/components/layout/sidebar.tsx | 2 +- bucket/d3ro-voice.json | 8 +-- docs/map/10-feature-catalog.md | 2 +- package-lock.json | 20 +++--- package.json | 2 +- packages/api-client/package.json | 2 +- packages/core/package.json | 2 +- packages/i18n/package.json | 2 +- packages/ui-native/package.json | 2 +- packages/ui/package.json | 2 +- release/product-version.json | 6 +- scripts/ci/build-portable.mjs | 3 + scripts/ci/publish-updater-release.mjs | 10 +++ scripts/ci/write-app-update-yml.mjs | 70 +++++++++++++++++++ site/package-lock.json | 4 +- site/package.json | 2 +- 28 files changed, 141 insertions(+), 47 deletions(-) create mode 100644 apps/mobile-rn/metadata/android/en-US/changelogs/1031004.txt create mode 100644 apps/mobile-rn/metadata/android/ko-KR/changelogs/1031004.txt create mode 100644 scripts/ci/write-app-update-yml.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 75062a8..84cf8b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Cloud-optional backup (encrypted, opt-in) - Plugin system for custom pipelines +## [1.3.4] - 2026-09-18 + +### Fixed +- **Automatic updates could not start on 1.3.3**: its installer was missing the updater + configuration file, because the packaging path used to guarantee the native module build + does not create it. The file is now written from the single feed source and its presence + in the packaged app is verified before publishing, so an installer that cannot update is + never shipped. + ## [1.3.3] - 2026-09-18 ### Fixed diff --git a/apps/admin-swagger/openapi.json b/apps/admin-swagger/openapi.json index 6c1edc1..6dc41cd 100644 --- a/apps/admin-swagger/openapi.json +++ b/apps/admin-swagger/openapi.json @@ -3,7 +3,7 @@ "info": { "title": "D3RO-VOICE Admin API", "description": "Admin CRM Edge Functions for user management, subscription CRUD, payment history, and audit logs.", - "version": "1.3.3" + "version": "1.3.4" }, "servers": [ { diff --git a/apps/admin/package.json b/apps/admin/package.json index 6b80dc6..d3cac23 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/admin", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice Admin CRM — SaaS 관리 도구", "scripts": { diff --git a/apps/api-server/D3ROVoice.Api.csproj b/apps/api-server/D3ROVoice.Api.csproj index 42a627f..17327fb 100644 --- a/apps/api-server/D3ROVoice.Api.csproj +++ b/apps/api-server/D3ROVoice.Api.csproj @@ -2,7 +2,7 @@ net10.0 - 1.3.3 + 1.3.4 enable enable diff --git a/apps/desktop/package.json b/apps/desktop/package.json index eed0ba2..e27248c 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/desktop", - "version": "1.3.3", + "version": "1.3.4", "productName": "d3ro-voice", "description": "로컬 AI 음성 어시스턴트 (Electron)", "main": "./out/main/index.js", diff --git a/apps/mobile-rn/android/app/build.gradle b/apps/mobile-rn/android/app/build.gradle index 45de6b8..4414a73 100644 --- a/apps/mobile-rn/android/app/build.gradle +++ b/apps/mobile-rn/android/app/build.gradle @@ -151,8 +151,8 @@ def versionSettingsValid = configuredVersionName != null && configuredVersionName ==~ strictSemver && configuredVersionCodeValue != null && configuredVersionCodeValue <= 2100000000L -def resolvedVersionName = versionSettingsValid ? configuredVersionName : "1.3.3" -def resolvedVersionCode = versionSettingsValid ? configuredVersionCodeValue.toInteger() : 1031003 +def resolvedVersionName = versionSettingsValid ? configuredVersionName : "1.3.4" +def resolvedVersionCode = versionSettingsValid ? configuredVersionCodeValue.toInteger() : 1031004 def requiredReleaseSettings = [ D3RO_RELEASE_STORE_FILE: releaseStoreFilePath, diff --git a/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj b/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj index a610a49..cb97790 100644 --- a/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj +++ b/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj @@ -257,7 +257,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1031003; + CURRENT_PROJECT_VERSION = 1031004; ENABLE_BITCODE = NO; INFOPLIST_FILE = D3ROVoice/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; @@ -265,7 +265,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.3; + MARKETING_VERSION = 1.3.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -287,14 +287,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1031003; + CURRENT_PROJECT_VERSION = 1031004; INFOPLIST_FILE = D3ROVoice/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.3; + MARKETING_VERSION = 1.3.4; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/apps/mobile-rn/metadata/android/en-US/changelogs/1031004.txt b/apps/mobile-rn/metadata/android/en-US/changelogs/1031004.txt new file mode 100644 index 0000000..27365b8 --- /dev/null +++ b/apps/mobile-rn/metadata/android/en-US/changelogs/1031004.txt @@ -0,0 +1 @@ +Fixed an installer that was missing its update configuration. diff --git a/apps/mobile-rn/metadata/android/ko-KR/changelogs/1031004.txt b/apps/mobile-rn/metadata/android/ko-KR/changelogs/1031004.txt new file mode 100644 index 0000000..0d0d3e4 --- /dev/null +++ b/apps/mobile-rn/metadata/android/ko-KR/changelogs/1031004.txt @@ -0,0 +1 @@ +자동 업데이트 설정이 누락되던 문제를 고쳤습니다. diff --git a/apps/mobile-rn/package-lock.json b/apps/mobile-rn/package-lock.json index 8275499..7ef41af 100644 --- a/apps/mobile-rn/package-lock.json +++ b/apps/mobile-rn/package-lock.json @@ -1,12 +1,12 @@ { "name": "@d3ro/mobile-rn", - "version": "1.3.3", + "version": "1.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@d3ro/mobile-rn", - "version": "1.3.3", + "version": "1.3.4", "dependencies": { "@d3ro/api-client": "file:../../packages/api-client", "@d3ro/core": "file:../../packages/core", @@ -62,7 +62,7 @@ }, "../..": { "name": "d3ro-voice-monorepo", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "workspaces": [ "apps/desktop", @@ -81,7 +81,7 @@ }, "../../packages/api-client": { "name": "@d3ro/api-client", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "@d3ro/core": "*", @@ -98,7 +98,7 @@ }, "../../packages/core": { "name": "@d3ro/core", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "docx": "^9.6.1" @@ -109,7 +109,7 @@ }, "../../packages/i18n": { "name": "@d3ro/i18n", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "devDependencies": { "@types/react": "^19.0.0" @@ -120,7 +120,7 @@ }, "../../packages/ui-native": { "name": "@d3ro/ui-native", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "devDependencies": { "@types/react": "*" diff --git a/apps/mobile-rn/package.json b/apps/mobile-rn/package.json index d8807f5..b11ec4a 100644 --- a/apps/mobile-rn/package.json +++ b/apps/mobile-rn/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/mobile-rn", - "version": "1.3.3", + "version": "1.3.4", "private": true, "scripts": { "android": "react-native run-android", diff --git a/apps/web/package.json b/apps/web/package.json index ba7e257..9d46c88 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/web", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice 웹 앱 — Next.js 기반 SaaS 인터페이스", "scripts": { diff --git a/apps/web/src/components/layout/sidebar.tsx b/apps/web/src/components/layout/sidebar.tsx index fdecc28..e593e0d 100644 --- a/apps/web/src/components/layout/sidebar.tsx +++ b/apps/web/src/components/layout/sidebar.tsx @@ -139,7 +139,7 @@ export function Sidebar(): React.ReactElement { - v1.3.3 + v1.3.4 diff --git a/bucket/d3ro-voice.json b/bucket/d3ro-voice.json index cc74cce..f1eb5e9 100644 --- a/bucket/d3ro-voice.json +++ b/bucket/d3ro-voice.json @@ -1,15 +1,15 @@ { - "version": "1.3.3", + "version": "1.3.4", "description": "로컬 AI 음성 어시스턴트 (faster-whisper + Ollama, 100% 오프라인 지원)", "homepage": "https://d3ro.chanpaca.net", "license": "MIT", "architecture": { "64bit": { "url": [ - "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-1.3.3/D3RO-Voice-1.3.3-x64-portable.7z.001" + "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-1.3.4/D3RO-Voice-1.3.4-x64-portable.7z.001" ], "hash": [ - "b4bb847a7f25732cf0abf8516f6c708d6d9e6ab7d1fd813abf7795a0466c17b0" + "259e4803d3edbc1d4e6fd79ff8d034e75fb55f2ebac9e0f6459d0880531852e4" ] } }, @@ -27,7 +27,7 @@ "architecture": { "64bit": { "url": [ - "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-$version/D3RO-Voice-1.3.3-x64-portable.7z.001" + "https://git.chanpaca.net/api/packages/yunchan/generic/d3ro-voice/portable-$version/D3RO-Voice-1.3.4-x64-portable.7z.001" ] } } diff --git a/docs/map/10-feature-catalog.md b/docs/map/10-feature-catalog.md index de94cc0..20e1714 100644 --- a/docs/map/10-feature-catalog.md +++ b/docs/map/10-feature-catalog.md @@ -195,7 +195,7 @@ Status quick-reference: `[x]` done+verified · `[~]` partial/unverified · `[ ]` | INFRA-16 | Desktop STT engine packaging | [x] | `apps/desktop/scripts/setup-sidecar.mjs` + `build-sidecar.mjs`, `electron-builder.yml` `extraResources` (`sidecar-dist/sidecar` → `resources/sidecar`, `resources/ffmpeg`), and `scripts/ci/verify-sidecar-bundle.mjs` run in `package-windows`/`package-macos` before electron-builder. Verified on the real bundle: `sidecar.exe` + `_internal` including `faster_whisper/assets/silero_vad_v6.onnx`, plus a packaged-engine transcription round-trip on GPU. | | INFRA-17 | 서명 없는 배포 채널 (portable + Scoop) | [x] | `scripts/ci/build-portable.mjs` (95MiB 7z 분할 볼륨 + Scoop 매니페스트), `scripts/ci/publish-portable-release.mjs`, `scripts/local/install-d3ro-voice.ps1`, `bucket/` 버킷, `.forgejo/workflows/portable.yml`; 7z 분할 볼륨(Scoop, 162MiB) + zip 분할 부품(수동 설치, 243MiB, 7-Zip 불필요); updater feed와 분리. 2026-09-18 `portable-1.3.1` 게시 + 실제 설치 검증. | | INFRA-18 | 로컬 런타임 온디맨드 설치 | [x] | `RuntimeProvisioner`(부품 다운로드 + SHA-256 검증 + tar 해제, `%APPDATA%/d3ro-voice/runtime`), `POST runtime:ensure` / `runtime:progress` IPC, 설정 > STT 상태/내려받기 UI. 설치본에서 엔진/ffmpeg를 분리해 189MB → 90.6MiB, 업데이트 피드 게시 복구. 2026-09-18 실제 feed 통합 검증(엔진 94.4MiB/17초, ffmpeg 21.7MiB/5초). | -| INFRA-19 | 네이티브 ABI 패키징 게이트 | [x] | `scripts/ci/verify-native-abi.mjs`(패키징된 `better_sqlite3.node`가 Electron ABI인지 호스트 Node 로드 거부로 판별) + `scripts/ci/fix-native-abi.mjs`(로컬 잠금 우회용 주입). GitLab/Forgejo/GitHub 패키징 단계에 검증 삽입. 2026-09-18: Node ABI 모듈이 설치본에 들어가 앱이 시작 즉시 죽은 사고의 재발 방지. | +| INFRA-19 | 네이티브 ABI + updater 설정 게이트 | [x] | `scripts/ci/verify-native-abi.mjs`(패키징된 `better_sqlite3.node`가 Electron ABI인지 호스트 Node 로드 거부로 판별) + `scripts/ci/fix-native-abi.mjs`(로컬 잠금 우회용 주입). GitLab/Forgejo/GitHub 패키징 단계에 검증 삽입. 2026-09-18: Node ABI 모듈로 앱이 시작 즉시 죽은 사고 + 누락으로 자동 업데이트가 죽은 사고를 함께 방지(). | --- diff --git a/package-lock.json b/package-lock.json index 571d0db..e229380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "d3ro-voice-monorepo", - "version": "1.3.3", + "version": "1.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "d3ro-voice-monorepo", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "workspaces": [ "apps/desktop", @@ -25,7 +25,7 @@ }, "apps/admin": { "name": "@d3ro/admin", - "version": "1.3.3", + "version": "1.3.4", "dependencies": { "@d3ro/api-client": "*", "@d3ro/core": "*", @@ -109,7 +109,7 @@ }, "apps/desktop": { "name": "@d3ro/desktop", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "@d3ro/core": "*", @@ -158,7 +158,7 @@ }, "apps/web": { "name": "@d3ro/web", - "version": "1.3.3", + "version": "1.3.4", "dependencies": { "@d3ro/api-client": "*", "@d3ro/core": "*", @@ -16861,7 +16861,7 @@ }, "packages/api-client": { "name": "@d3ro/api-client", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "@d3ro/core": "*", @@ -16878,7 +16878,7 @@ }, "packages/core": { "name": "@d3ro/core", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "docx": "^9.6.1" @@ -16889,7 +16889,7 @@ }, "packages/i18n": { "name": "@d3ro/i18n", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "devDependencies": { "@types/react": "^19.0.0" @@ -16900,7 +16900,7 @@ }, "packages/ui": { "name": "@d3ro/ui", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "dependencies": { "@d3ro/core": "*" @@ -16920,7 +16920,7 @@ }, "packages/ui-native": { "name": "@d3ro/ui-native", - "version": "1.3.3", + "version": "1.3.4", "license": "MIT", "devDependencies": { "@types/react": "*" diff --git a/package.json b/package.json index 92c41de..c707f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "d3ro-voice-monorepo", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice — 멀티플랫폼 AI 음성 어시스턴트 (Monorepo)", "author": "D3RO", diff --git a/packages/api-client/package.json b/packages/api-client/package.json index 95675ee..cc4e37d 100644 --- a/packages/api-client/package.json +++ b/packages/api-client/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/api-client", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice API 클라이언트 — Supabase 래퍼 (web/desktop/mobile 공유)", "license": "MIT", diff --git a/packages/core/package.json b/packages/core/package.json index 9c3d921..f754567 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/core", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice 공유 비즈니스 로직 — 타입, 에러, IPC 채널, 상수, 유틸", "license": "MIT", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index d3e4b06..6cbd967 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/i18n", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice 공유 i18n — 12개 locale, 타입 안전 키, Context, 포맷 유틸", "license": "MIT", diff --git a/packages/ui-native/package.json b/packages/ui-native/package.json index ef44250..f5e10a9 100644 --- a/packages/ui-native/package.json +++ b/packages/ui-native/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/ui-native", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice React Native DS — MetalCard/PhosphorText/Led/WaveBars etc.", "license": "MIT", diff --git a/packages/ui/package.json b/packages/ui/package.json index facc0d6..f61a54c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@d3ro/ui", - "version": "1.3.3", + "version": "1.3.4", "private": true, "description": "D3RO Voice 공유 UI — 디자인 시스템 컴포넌트 + 테마 토큰 + CSS 변수 맵", "license": "MIT", diff --git a/release/product-version.json b/release/product-version.json index aa03c00..c59625f 100644 --- a/release/product-version.json +++ b/release/product-version.json @@ -1,8 +1,8 @@ { "schemaVersion": 1, - "version": "1.3.3", - "androidVersionCode": 1031003, - "iosBuildNumber": 1031003, + "version": "1.3.4", + "androidVersionCode": 1031004, + "iosBuildNumber": 1031004, "releaseDate": "2026-09-18", "desktopLicensePublicKeyId": "5c52b765135ee2531c681b53cd4dc0e96fff8737f496c0b04ba8334fc81a887f" } diff --git a/scripts/ci/build-portable.mjs b/scripts/ci/build-portable.mjs index c9dd870..91fa3c1 100644 --- a/scripts/ci/build-portable.mjs +++ b/scripts/ci/build-portable.mjs @@ -146,6 +146,9 @@ if (!existsSync(appDir)) { // (실측 사고: better_sqlite3.node가 NODE_MODULE_VERSION 131 → Electron 130 요구) runNodeScript('scripts/ci/fix-native-abi.mjs', ['--dir', appDir]) runNodeScript('scripts/ci/verify-native-abi.mjs', ['--dir', appDir]) +// electron-builder의 --dir/--prepackaged 경로는 app-update.yml을 만들지 않는다. +// 이 파일이 없으면 electron-updater가 설정을 읽지 못해 자동 업데이트가 죽는다(실측). +runNodeScript('scripts/ci/write-app-update-yml.mjs', ['--dir', appDir]) // 이전 볼륨 정리 (같은 버전 재드 시 잔여 볼륨이 섞이지 않도록) for (const name of readdirSync(releaseDir)) { diff --git a/scripts/ci/publish-updater-release.mjs b/scripts/ci/publish-updater-release.mjs index 69ae3e8..fe72a08 100644 --- a/scripts/ci/publish-updater-release.mjs +++ b/scripts/ci/publish-updater-release.mjs @@ -70,6 +70,9 @@ if (build) { runNodeScript('scripts/ci/fix-native-abi.mjs', ['--dir', appDir]) runNodeScript('scripts/ci/verify-native-abi.mjs', ['--dir', appDir]) + // electron-updater 설정 파일을 보장한다(없으면 자동 업데이트가 동작하지 않는다) + runNodeScript('scripts/ci/write-app-update-yml.mjs', ['--dir', appDir]) + // 3) 검증된 트리에서 설치본 생성 (--prepackaged = 재빌드 없이 그대로 패키징) console.log('[updater] electron-builder --prepackaged (NSIS x64)') const packageResult = spawnSync( @@ -157,6 +160,13 @@ if (installerSize > MAX_UPLOAD_BYTES) { process.exit(1) } +// 설치본에 app-update.yml이 없으면 electron-updater가 설정을 읽지 못해 자동 업데이트가 죽는다. +const packagedUpdateConfig = join(releaseDir, 'win-unpacked', 'resources', 'app-update.yml') +if (!existsSync(packagedUpdateConfig)) { + console.error('[updater] app-update.yml 누락 — write-app-update-yml.mjs를 먼저 실행하세요.') + process.exit(1) +} + const metadata = readFileSync(metadataPath, 'utf8') if (!metadata.includes(`version: ${version}`)) { console.error('[updater] latest.yml의 버전이 product-version.json과 다릅니다.') diff --git a/scripts/ci/write-app-update-yml.mjs b/scripts/ci/write-app-update-yml.mjs new file mode 100644 index 0000000..0a8fe03 --- /dev/null +++ b/scripts/ci/write-app-update-yml.mjs @@ -0,0 +1,70 @@ +// scripts/ci/write-app-update-yml.mjs +// 패키징된 앱 트리에 electron-updater 설정 파일(resources/app-update.yml)을 보장한다. +// +// 배경(실측 사고): electron-builder는 `--dir`/`--prepackaged` 경로에서 app-update.yml을 +// 생성하지 않는다. 그래서 1.3.3 설치본에는 이 파일이 없었고, electron-updater가 설정을 +// 읽지 못해 **자동 업데이트가 동작하지 않는다**. +// (일반 `electron-builder --win nsis` 경로에서는 생성되지만, 우리는 네이티브 ABI 검증을 위해 +// --dir → 검증 → --prepackaged 순서를 쓰므로 직접 만들어 준다.) +// +// 값의 출처는 런타임 SSOT인 apps/desktop/src/main/update-feed.ts의 UPDATE_FEED_URL 하나뿐이다. +// +// 사용: node scripts/ci/write-app-update-yml.mjs --dir + +import { existsSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..', '..') + +const dirFlagIndex = process.argv.indexOf('--dir') +const packagedDir = dirFlagIndex >= 0 ? process.argv[dirFlagIndex + 1] : null +if (!packagedDir || !existsSync(packagedDir)) { + console.error('사용: node scripts/ci/write-app-update-yml.mjs --dir ') + process.exit(1) +} + +// 피드 URL은 update-feed.ts가 정본이다 (electron-builder.yml의 publish.url과 동일해야 한다). +const feedSource = readFileSync(join(root, 'apps', 'desktop', 'src', 'main', 'update-feed.ts'), 'utf8') +const feedMatch = feedSource.match(/export const UPDATE_FEED_URL\s*=\s*'([^']+)'/) +if (!feedMatch) { + console.error('[app-update] update-feed.ts에서 UPDATE_FEED_URL을 찾을 수 없습니다.') + process.exit(1) +} +const feedUrl = feedMatch[1] + +const electronBuilderConfig = readFileSync( + join(root, 'apps', 'desktop', 'electron-builder.yml'), + 'utf8', +) +const publishMatch = electronBuilderConfig.match(/^publish:\s*$[\s\S]*?url:\s*"([^"]+)"/m) +if (publishMatch && publishMatch[1] !== feedUrl) { + console.error( + [ + '[app-update] feed URL 불일치:', + ` update-feed.ts : ${feedUrl}`, + ` electron-builder.yml : ${publishMatch[1]}`, + ' 두 값은 같아야 합니다(자동 업데이트 계약).', + ].join('\n'), + ) + process.exit(1) +} + +const target = join(packagedDir, 'resources', 'app-update.yml') +const contents = [ + 'provider: generic', + `url: ${feedUrl}`, + // electron-builder가 일반 경로에서 생성하는 값과 동일한 규칙(제품명 기반) + "updaterCacheDirName: 'd3ro-voice-updater'", + '', +].join('\n') + +const existing = existsSync(target) ? readFileSync(target, 'utf8') : null +if (existing === contents) { + console.log('[app-update] 이미 최신 상태입니다') + process.exit(0) +} + +writeFileSync(target, contents, 'utf8') +console.log(`[app-update] 작성: ${target}`) +console.log(contents.trimEnd()) \ No newline at end of file diff --git a/site/package-lock.json b/site/package-lock.json index 172e126..6257b11 100644 --- a/site/package-lock.json +++ b/site/package-lock.json @@ -1,12 +1,12 @@ { "name": "d3ro-voice-site", - "version": "1.3.3", + "version": "1.3.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "d3ro-voice-site", - "version": "1.3.3", + "version": "1.3.4", "dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/site/package.json b/site/package.json index f557d13..80b6c44 100644 --- a/site/package.json +++ b/site/package.json @@ -1,7 +1,7 @@ { "name": "d3ro-voice-site", "private": true, - "version": "1.3.3", + "version": "1.3.4", "type": "module", "scripts": { "dev": "vite --port 5199 --host",