3 Commits

Author SHA1 Message Date
40c47fbeb3 build: bundle sharp's win32-x64 prebuilt for Windows packaging
The packaged installer-rp crashed on launch with
"Could not load the 'sharp' module using the win32-x64 runtime" because
electron-builder ran on Linux and only the Linux sharp variants were
present in node_modules.

- Add `preinstall:sharp-win32` script that force-installs
  `@img/sharp-win32-x64@0.34.5` into the local node_modules (npm refuses
  it on Linux without --force due to its os/cpu restrictions).
- Chain that script before both `dist:win` and `dist:win:rp` so future
  Windows builds always have the native prebuilt available.
- Exclude `@img/sharp-{,libvips-}linux*` from the packaged files list in
  both electron-builder configs so the unused Linux variants don't bloat
  the portable exe.

Verified `release/win-unpacked/resources/app.asar.unpacked/node_modules/
@img/sharp-win32-x64/lib/sharp-win32-x64.node` is present and that no
linux sharp variants ship inside the asar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 02:14:55 +09:00
6e170646a7 env: commit .env.build with production SITE_BASE_URL
Track `.env.build` in version control so the production site domain
(`https://mc.tkrmagid.kr`) is baked into every portable exe build by
default. `.env` (server/dev secrets) stays gitignored.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:15:22 +09:00
3017e77479 env: merge .env + .env.build instead of stopping at first match
Reviewer noted that returning on the first found file meant a project-root
`.env.build` could shadow the dev `.env`, leaving the server without
`PORT`/`HOST`/`SESSION_SECRET`. Switch `loadEnv()` to iterate every
candidate with `override:false` so multiple files merge — first-loaded
value wins per key.

Order also reshuffled so dev's `.env` takes precedence over `.env.build`
at the project root (server settings stay alive), while in packaged
mode `resources/.env.build` still wins. Verified manually with a
temp-dir reproduction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 01:11:15 +09:00
6 changed files with 39 additions and 18 deletions

4
.env.build Normal file
View File

@@ -0,0 +1,4 @@
# 빌드용 환경변수 — `npm run dist:win` / `npm run dist:win:rp` 로 패키징될 때
# 설치기 exe 의 `resources/.env.build` 로 함께 배포되어 런타임에 로드됨.
# 서버 운영용(PORT/HOST/SESSION_SECRET) 값은 여기 두지 말고 `.env` 에.
SITE_BASE_URL=https://mc.tkrmagid.kr

1
.gitignore vendored
View File

@@ -7,4 +7,3 @@ conversations/
.env
.env.local
.env.*.local
.env.build

View File

@@ -13,6 +13,12 @@ files:
- installer-rp/**
- build/icon.*
- package.json
# sharp 는 플랫폼별 prebuilt 가 분리 패키지로 배포됨. Windows 빌드에서는
# win32-x64 만 포함하고 linux/* 변종은 묶지 않아 exe 크기를 줄임.
- "!node_modules/@img/sharp-linux-*"
- "!node_modules/@img/sharp-linuxmusl-*"
- "!node_modules/@img/sharp-libvips-linux-*"
- "!node_modules/@img/sharp-libvips-linuxmusl-*"
# 메인 설치기와 동일하게 빌드 전용 `.env.build` 와 locales 를 함께 배포.
extraResources:
- from: .

View File

@@ -9,6 +9,12 @@ files:
- installer/**
- build/icon.*
- package.json
# sharp 는 플랫폼별 prebuilt 가 분리 패키지로 배포됨. Windows 빌드에서는
# win32-x64 만 포함하고 linux/* 변종은 묶지 않아 exe 크기를 줄임.
- "!node_modules/@img/sharp-linux-*"
- "!node_modules/@img/sharp-linuxmusl-*"
- "!node_modules/@img/sharp-libvips-linux-*"
- "!node_modules/@img/sharp-libvips-linuxmusl-*"
# 빌드 전용 `.env.build` 를 설치기 옆에 함께 배포(없으면 조용히 패스).
# `.env` 는 서버/개발 실행용이라 빌드 산출물에는 포함되지 않으며, 패키지된 exe
# 는 `resources/.env.build` 를 우선 로드함(없으면 `resources/.env` 로 폴백).

View File

@@ -9,8 +9,9 @@
"dev:server": "tsc -p tsconfig.server.json && node dist/server/app.js",
"installer": "tsc -p tsconfig.installer.json && electron .",
"installer:rp": "tsc -p tsconfig.installer-rp.json && electron dist/installer-rp/main.js",
"dist:win": "tsc -p tsconfig.installer.json && electron-builder --win --config electron-builder.yml",
"dist:win:rp": "tsc -p tsconfig.installer-rp.json && electron-builder --win --config electron-builder-rp.yml"
"preinstall:sharp-win32": "npm install --no-save --force @img/sharp-win32-x64@0.34.5",
"dist:win": "npm run preinstall:sharp-win32 && tsc -p tsconfig.installer.json && electron-builder --win --config electron-builder.yml",
"dist:win:rp": "npm run preinstall:sharp-win32 && tsc -p tsconfig.installer-rp.json && electron-builder --win --config electron-builder-rp.yml"
},
"dependencies": {
"@types/archiver": "^7.0.0",

View File

@@ -4,21 +4,27 @@ import dotenv from 'dotenv'
import { projectRoot } from './paths.js'
/**
* `.env` 를 읽어 `process.env` 에 주입.
* `.env` / `.env.build` 를 읽어 `process.env` 에 주입.
*
* 탐색 순서(처음 발견된 것만 사용):
* 1. 패키징된 Electron 앱이면 `process.resourcesPath/.env.build`
* — electron-builder 의 extraResources 로 빌드 시점 `.env.build` 가 함께
* 배포됨. 빌드용 도메인/설정을 dev `.env` 와 분리해 관리하기 위함.
* 2. 패키징된 Electron 앱이면 `process.resourcesPath/.env`
* — 운영자가 패키징 후 직접 `.env` 를 옆에 두고 덮어쓰는 경우를 대비한 폴백.
* 3. `<프로젝트 루트>/.env.build`
* — 개발 환경에서도 빌드용 값을 그대로 테스트하고 싶을 때.
* 4. `<프로젝트 루트>/.env`
* — 개발 실행(npm start / npm run installer*) 및 서버 운영용.
* 여러 파일을 순서대로 읽되 `override:false` 로 병합하므로 **먼저 로드된 값이
* 우선**. 두 도메인(패키지 빌드용 vs 개발/서버용) 이 한 함수에서 자연스럽게
* 분리됨:
*
* - 이미 설정된 환경변수는 덮어쓰지 않음(쉘/systemd 에서 넘긴 값이 우선).
* - 파일이 없으면 조용히 통과.
* 1. 패키징된 Electron 앱: `process.resourcesPath/.env.build`
* — electron-builder 가 빌드 시점 `.env.build` 를 함께 배포. 패키지된 exe
* 에서 가장 먼저 적용되는 값.
* 2. 패키징된 Electron 앱: `process.resourcesPath/.env`
* — 운영자가 패키징 후 직접 `.env` 를 옆에 두고 덮어쓰는 경우 폴백.
* 3. `<프로젝트 루트>/.env`
* — 개발 실행(npm start / npm run installer*) 및 서버 운영용. 서버의
* `PORT/HOST/SESSION_SECRET` 처럼 dev 에서 반드시 살아 있어야 하는 값들이
* 있어, `.env.build` 보다 먼저 로드해 우선권을 줌.
* 4. `<프로젝트 루트>/.env.build`
* — dev 환경에서 빌드용 값(예: 운영 도메인 SITE_BASE_URL)을 테스트하고
* 싶을 때 사용. `.env` 에 없는 키만 채움.
*
* - 이미 설정된 환경변수는 덮어쓰지 않음(쉘/systemd 에서 넘긴 값이 최우선).
* - 존재하지 않는 후보는 조용히 건너뜀.
* - 서버/설치기/리소스팩설치기 진입점에서 한 번씩 호출.
*/
export function loadEnv(): void {
@@ -28,13 +34,12 @@ export function loadEnv(): void {
candidates.push(path.join(resourcesPath, '.env.build'))
candidates.push(path.join(resourcesPath, '.env'))
}
candidates.push(path.join(projectRoot, '.env.build'))
candidates.push(path.join(projectRoot, '.env'))
candidates.push(path.join(projectRoot, '.env.build'))
for (const envPath of candidates) {
if (fs.existsSync(envPath)) {
dotenv.config({ path: envPath, override: false, quiet: true })
return
}
}
}