diff --git a/.gitignore b/.gitignore index a547bf3..85f8eaa 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ dist-ssr *.njsproj *.sln *.sw? +.env.local diff --git a/scripts/deploy-nas.cjs b/scripts/deploy-nas.cjs index 4768e79..e3e1b2a 100644 --- a/scripts/deploy-nas.cjs +++ b/scripts/deploy-nas.cjs @@ -1,5 +1,20 @@ const { execSync } = require("child_process"); const fs = require("fs"); +const path = require("path"); + +// Load .env.local from project root if present (persists NAS_SSH_TARGET etc.) +const envLocalPath = path.join(__dirname, "..", ".env.local"); +if (fs.existsSync(envLocalPath)) { + for (const line of fs.readFileSync(envLocalPath, "utf8").split("\n")) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const idx = trimmed.indexOf("="); + if (idx < 0) continue; + const k = trimmed.slice(0, idx).trim(); + const v = trimmed.slice(idx + 1).trim(); + if (!(k in process.env)) process.env[k] = v; + } +} const isWin = process.platform === "win32"; const isMac = process.platform === "darwin"; @@ -12,10 +27,6 @@ if (!fs.existsSync(src)) { console.error("dist not found. Run build first."); process.exit(1); } -if (!fs.existsSync(dst)) { - console.error("NAS path not found. Check mount: " + dst); - process.exit(1); -} if (isWin) { const cmd = @@ -26,33 +37,43 @@ if (isWin) { const sshPath = process.env.NAS_SSH_PATH || "/volume1/docker/webpage/frontend/"; const sshPort = process.env.NAS_SSH_PORT; + + // SSH 경로: NAS_SSH_TARGET이 설정된 경우 항상 우선 if (sshTarget) { + console.log(`Deploying via SSH → ${sshTarget}:${sshPath}`); const sshCmd = sshPort ? `ssh -p ${sshPort}` : "ssh"; execSync( - `rsync -r --delete --delete-delay -e \"${sshCmd}\" ${src}/ ${sshTarget}:${sshPath}`, + `rsync -r --delete --delete-delay -e "${sshCmd}" ${src}/ ${sshTarget}:${sshPath}`, { stdio: "inherit" } ); process.exit(0); } - // rsync on macOS + SMB/NAS can be flaky; use ditto after a safe clean. + + // SMB 마운트 경로 fallback + if (!fs.existsSync(dst)) { + console.error("NAS path not found: " + dst); + printSshHint(); + process.exit(1); + } + if (!dst.includes("docker/webpage/frontend")) { console.error("Safety check failed: unexpected dst path: " + dst); process.exit(1); } + try { const testPath = `${dst}.deploy-write-test`; fs.writeFileSync(testPath, "ok"); fs.unlinkSync(testPath); } catch (err) { + console.error("NAS write test failed (EIO / permission error)."); console.error( - "NAS write test failed. Files may be locked or permissions are read-only." + "macOS SMB → Synology 쓰기 실패는 흔한 이슈입니다. SSH 배포를 사용하세요.\n" ); - console.error( - "Try stopping services using the folder, remounting the share with write access,", - "or set NAS_SSH_TARGET to deploy over SSH instead." - ); - throw err; + printSshHint(); + process.exit(1); } + const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms); const retry = (fn, attempts = 6) => { @@ -96,3 +117,15 @@ if (isWin) { const cmd = `${baseArgs.join(" ")} ${src}/ ${dst}`; execSync(cmd, { stdio: "inherit" }); } + +function printSshHint() { + console.error("──────────────────────────────────────────────────"); + console.error("SSH 배포 설정 방법:"); + console.error(" 프로젝트 루트에 .env.local 파일을 만들고 아래 내용을 입력하세요:"); + console.error(""); + console.error(" NAS_SSH_TARGET=@gahusb.synology.me"); + console.error(" NAS_SSH_PORT= # 기본 22, DSM에서 확인"); + console.error(""); + console.error(" 이후 npm run release:nas 를 다시 실행하면 rsync over SSH로 배포됩니다."); + console.error("──────────────────────────────────────────────────"); +}