215 lines
7.4 KiB
JavaScript
215 lines
7.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const { execSync } = require("child_process");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const isWin = process.platform === "win32";
|
|
const isMac = process.platform === "darwin";
|
|
|
|
// 소스 디렉토리 (현재 프로젝트 루트)
|
|
const projectRoot = path.resolve(__dirname, "..");
|
|
const projectName = "saju-web";
|
|
|
|
// NAS 경로 설정
|
|
const dstWin = `Z:\\docker\\webpage\\${projectName}\\`;
|
|
const dstMac = `/Volumes/gahusb.synology.me/docker/webpage/${projectName}/`;
|
|
const dst = isWin ? dstWin : dstMac;
|
|
|
|
// SSH 설정 (환경 변수에서 읽기)
|
|
const sshTarget = process.env.NAS_SSH_TARGET || "gahusb@gahusb.synology.me";
|
|
const sshPath = process.env.NAS_SSH_PATH || `/volume1/docker/webpage/${projectName}`;
|
|
const sshPort = process.env.NAS_SSH_PORT;
|
|
|
|
// 제외할 파일/폴더
|
|
const excludePatterns = [
|
|
"node_modules/",
|
|
".next/",
|
|
".git/",
|
|
"dist/",
|
|
"out/",
|
|
".env.local",
|
|
".env*.local",
|
|
"*.log",
|
|
".DS_Store",
|
|
"build_error.log",
|
|
"robocopy.log",
|
|
];
|
|
|
|
console.log("🚀 Saju Web NAS Deployment Started");
|
|
console.log("=====================================");
|
|
console.log(`Platform: ${process.platform}`);
|
|
console.log(`Project: ${projectName}`);
|
|
console.log("");
|
|
|
|
// SSH를 통한 배포 (추천)
|
|
if (process.env.NAS_DEPLOY_METHOD === "ssh" || sshTarget) {
|
|
console.log("📡 Deploying via SSH...");
|
|
console.log(`Target: ${sshTarget}:${sshPath}`);
|
|
console.log("");
|
|
|
|
const sshCmd = sshPort ? `ssh -p ${sshPort}` : "ssh";
|
|
const excludeArgs = excludePatterns
|
|
.map((pattern) => `--exclude='${pattern}'`)
|
|
.join(" ");
|
|
|
|
try {
|
|
// 1. rsync로 파일 동기화
|
|
console.log("📦 Step 1: Syncing files to NAS...");
|
|
const rsyncCmd = `rsync -avz --delete ${excludeArgs} -e "${sshCmd}" ${projectRoot}/ ${sshTarget}:${sshPath}/`;
|
|
console.log(`Command: ${rsyncCmd}`);
|
|
execSync(rsyncCmd, { stdio: "inherit" });
|
|
console.log("✅ Files synced successfully");
|
|
console.log("");
|
|
|
|
// 2. .env.nas를 .env로 복사
|
|
console.log("📝 Step 2: Setting up environment variables...");
|
|
const envCmd = `${sshCmd} ${sshTarget} "cd ${sshPath} && cp -f .env.nas .env"`;
|
|
execSync(envCmd, { stdio: "inherit" });
|
|
console.log("✅ Environment variables set");
|
|
console.log("");
|
|
|
|
// 3. Docker 빌드 및 배포
|
|
console.log("🐳 Step 3: Building and deploying Docker container...");
|
|
const deployCmd = `${sshCmd} ${sshTarget} "cd /volume1/docker/webpage && docker compose build ${projectName} && docker compose up -d ${projectName}"`;
|
|
console.log(`Command: ${deployCmd}`);
|
|
execSync(deployCmd, { stdio: "inherit" });
|
|
console.log("✅ Docker container deployed");
|
|
console.log("");
|
|
|
|
// 4. 헬스체크
|
|
console.log("🏥 Step 4: Health check...");
|
|
setTimeout(() => {
|
|
try {
|
|
const healthCmd = `${sshCmd} ${sshTarget} "docker exec ${projectName} wget -q -O- http://localhost:3000/api/health"`;
|
|
const health = execSync(healthCmd, { encoding: "utf-8" });
|
|
console.log("Health check response:", health);
|
|
console.log("✅ Service is healthy");
|
|
} catch (err) {
|
|
console.warn("⚠️ Health check failed (service may still be starting)");
|
|
}
|
|
console.log("");
|
|
console.log("🎉 Deployment completed!");
|
|
console.log("=====================================");
|
|
console.log(`🌐 Access: http://gahusb.synology.me/saju`);
|
|
console.log("");
|
|
console.log("📝 To check logs:");
|
|
console.log(` ssh ${sshTarget} "docker logs ${projectName} --tail 50 -f"`);
|
|
console.log("");
|
|
}, 3000);
|
|
} catch (err) {
|
|
console.error("❌ Deployment failed:", err.message);
|
|
process.exit(1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// SMB/CIFS 마운트를 통한 배포
|
|
console.log("📂 Deploying via SMB mount...");
|
|
console.log(`Destination: ${dst}`);
|
|
console.log("");
|
|
|
|
if (!fs.existsSync(dst)) {
|
|
console.error("❌ NAS path not found. Check mount: " + dst);
|
|
console.log("");
|
|
console.log("💡 Tip: Use SSH deployment instead:");
|
|
console.log(" NAS_DEPLOY_METHOD=ssh npm run release:nas");
|
|
console.log("");
|
|
console.log(" Or set environment variables:");
|
|
console.log(" NAS_SSH_TARGET=user@gahusb.synology.me");
|
|
console.log(" NAS_SSH_PORT=22 (optional)");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (isWin) {
|
|
console.log("📦 Step 1: Copying files to NAS (Windows - Robocopy)...");
|
|
|
|
// Robocopy 제외 패턴
|
|
const excludeDirs = excludePatterns
|
|
.filter((p) => p.endsWith("/"))
|
|
.map((p) => p.slice(0, -1))
|
|
.join(" ");
|
|
const excludeFiles = excludePatterns
|
|
.filter((p) => !p.endsWith("/"))
|
|
.join(" ");
|
|
|
|
const excludeDirsArg = excludeDirs ? `/XD ${excludeDirs}` : "";
|
|
const excludeFilesArg = excludeFiles ? `/XF ${excludeFiles}` : "";
|
|
|
|
const cmd = `powershell -NoProfile -ExecutionPolicy Bypass -Command "$ErrorActionPreference=\\"Stop\\"; $src=\\"${projectRoot}\\"; $dst=\\"${dst}\\"; if(!(Test-Path $dst)){ throw \\"NAS drive not found. Check Z: mapping.\\" }; $log = Join-Path (Get-Location) \\"robocopy.log\\"; robocopy $src $dst /MIR ${excludeDirsArg} ${excludeFilesArg} /R:1 /W:1 /E /NFL /NDL /NP /V /TEE /LOG:$log; $rc = $LASTEXITCODE; if($rc -ge 8){ Write-Host \\"robocopy failed with code $rc. See $log\\"; exit $rc } else { exit 0 }"`;
|
|
|
|
try {
|
|
execSync(cmd, { stdio: "inherit" });
|
|
console.log("✅ Files copied successfully");
|
|
} catch (err) {
|
|
console.error("❌ File copy failed:", err.message);
|
|
process.exit(1);
|
|
}
|
|
} else if (isMac) {
|
|
console.log("📦 Step 1: Copying files to NAS (macOS - ditto)...");
|
|
|
|
// 쓰기 테스트
|
|
const safety = dst.includes("docker/webpage/saju-web");
|
|
if (!safety) {
|
|
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. Files may be locked or permissions are read-only."
|
|
);
|
|
console.error(
|
|
"💡 Try using SSH deployment instead: NAS_DEPLOY_METHOD=ssh npm run release:nas"
|
|
);
|
|
throw err;
|
|
}
|
|
|
|
try {
|
|
execSync(`ditto ${projectRoot} ${dst}`, { stdio: "inherit" });
|
|
console.log("✅ Files copied successfully");
|
|
} catch (err) {
|
|
console.error("❌ File copy failed:", err.message);
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
// Linux 등 기타 플랫폼
|
|
console.log("📦 Step 1: Copying files to NAS (rsync)...");
|
|
|
|
const excludeArgs = excludePatterns
|
|
.map((pattern) => `--exclude='${pattern}'`)
|
|
.join(" ");
|
|
const cmd = `rsync -avz --delete ${excludeArgs} ${projectRoot}/ ${dst}`;
|
|
|
|
try {
|
|
execSync(cmd, { stdio: "inherit" });
|
|
console.log("✅ Files copied successfully");
|
|
} catch (err) {
|
|
console.error("❌ File copy failed:", err.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
console.log("");
|
|
console.log("⚠️ Files copied to NAS. Manual steps required:");
|
|
console.log("");
|
|
console.log("1. SSH into NAS:");
|
|
console.log(` ssh ${sshTarget}`);
|
|
console.log("");
|
|
console.log("2. Setup environment:");
|
|
console.log(` cd ${sshPath}`);
|
|
console.log(` cp -f .env.nas .env`);
|
|
console.log("");
|
|
console.log("3. Deploy Docker container:");
|
|
console.log(` cd /volume1/docker/webpage`);
|
|
console.log(` docker compose build ${projectName}`);
|
|
console.log(` docker compose up -d ${projectName}`);
|
|
console.log("");
|
|
console.log("💡 Tip: Use SSH deployment for automated process:");
|
|
console.log(" NAS_DEPLOY_METHOD=ssh npm run release:nas");
|
|
console.log("");
|