如何實現本地項目自動化部署到服務器

本文將介紹如何實現本地項目的自動化部署到服務器的過程。該過程包括以下幾個步驟:打包項目、壓縮打包後的文件、將壓縮文件上傳到服務器、在服務器上解壓文件,並將解壓後的文件部署到指定目錄,最後清理臨時文件。以下是具體的實現代碼。

準備工作

首先,我們需要安裝一些必要的npm包:

Terminal window
1
pnpm add node-ssh ssh2-sftp-client archiver

代碼實現

以下是完整的代碼實現,使用Node.js編寫:

1
import { NodeSSH } from "node-ssh";
2
import fs from "fs";
3
import path from "path";
4
import archiver from "archiver";
5
import { exec } from "child_process";
6
import { fileURLToPath } from "url";
7
import Client from "ssh2-sftp-client";
8
9
const __filename = fileURLToPath(import.meta.url); // 獲取文件的絕對路徑
10
const __dirname = path.dirname(__filename); // 獲取目錄名
11
12
const ssh = new NodeSSH();
13
const sftp = new Client();
14
15
const serverConfig = {
16
host: "你的伺服器IP", // 伺服器IP
17
username: "使用者名稱", // 使用者名稱
18
privateKey: "./private.pem", // 私鑰
19
// 如果你使用密碼登入
20
//password: "密碼", //
21
uploadPath: "/root/tmp/blog", // 檔案上傳臨時目錄
22
path: "/deploy/path", // 目標部署目錄
23
};
24
25
async function buildProject() {
26
console.log("Building project...");
27
28
return new Promise((resolve, reject) => {
29
exec("pnpm build", (error, stdout, stderr) => {
30
if (error) {
31
console.error(`Build error: ${error.message}`);
32
return reject(error);
33
}
34
if (stderr) {
35
console.error(`Build stderr: ${stderr}`);
36
return reject(new Error(stderr));
37
}
38
console.log(`Build stdout: ${stdout}`);
39
resolve();
40
});
41
});
42
}
43
44
async function deleteLocalDist() {
45
console.log("Deleting local dist...");
46
47
return new Promise((resolve, reject) => {
48
exec("rm -rf shell/dist.zip", (error, stdout, stderr) => {
49
if (error) {
50
console.error(`Delete error: ${error.message}`);
51
return reject(error);
52
}
53
if (stderr) {
54
console.error(`Delete stderr: ${stderr}`);
55
return reject(new Error(stderr));
56
}
57
console.log(`Deleted successfully`);
58
resolve();
59
});
60
});
61
}
62
63
async function compressDist() {
64
console.log("Compressing dist directory...");
65
return new Promise((resolve, reject) => {
66
const output = fs.createWriteStream(path.join(__dirname, "dist.zip"));
67
const archive = archiver("zip", {
68
zlib: { level: 9 },
69
});
70
71
output.on("close", () => {
72
console.log(
73
`dist.zip has been created. Total bytes: ${archive.pointer()}`,
74
);
75
resolve();
76
});
77
78
archive.on("error", (err) => {
79
console.error(`Compression error: ${err.message}`);
80
reject(err);
81
});
82
83
archive.pipe(output);
84
archive.directory("dist/", true);
85
archive.finalize();
86
});
87
}
88
89
async function uploadToServer() {
90
console.log("Uploading dist.zip to server...");
91
await sftp.connect({
92
host: serverConfig.host,
93
port: 22,
94
username: serverConfig.username,
95
privateKey: fs.readFileSync(serverConfig.privateKey, "utf8"),
96
});
97
98
await sftp.put(
99
path.join(__dirname, "dist.zip"),
100
path.posix.join(serverConfig.uploadPath, "dist.zip"), // 使用 path.posix.join 處理路徑
101
);
102
103
await sftp.end();
104
console.log("Upload complete.");
105
}
106
107
async function deploy() {
108
try {
109
await buildProject();
110
await compressDist();
111
await uploadToServer();
112
113
console.log("Connecting to server...");
114
await ssh.connect({
115
host: serverConfig.host,
116
username: serverConfig.username,
117
privateKey: fs.readFileSync(serverConfig.privateKey, "utf8"),
118
});
119
120
console.log("Removing old files...");
121
await ssh.execCommand(`rm -rf ${serverConfig.path}/*`);
122
123
console.log("Unzipping uploaded files...");
124
await ssh.execCommand(
125
`unzip ${serverConfig.uploadPath}/dist.zip -d ${serverConfig.uploadPath}`,
126
);
127
128
console.log("Moving files to target directory...");
129
await ssh.execCommand(
130
`mv ${serverConfig.uploadPath}/dist/* ${serverConfig.path}`,
131
);
132
133
console.log("Cleaning up...");
134
await ssh.execCommand(`rm -rf ${serverConfig.uploadPath}/dist`);
135
await ssh.execCommand(`rm ${serverConfig.uploadPath}/dist.zip`);
136
137
console.log("Deployment complete.");
138
ssh.dispose();
139
140
await deleteLocalDist();
141
} catch (error) {
142
console.error(`Deployment error: ${error.message}`);
143
ssh.dispose();
144
}
145
}
146
147
deploy();

代碼解釋

  1. 配置服務器信息

    • serverConfig 對象包含了服務器的IP地址、用戶名、私鑰路徑、上傳目錄和目標目錄。
  2. 打包項目

    • 使用 exec 函數執行 pnpm build 命令來打包項目。
  3. 壓縮打包後的文件

    • 使用 archiver 模塊將 dist 目錄壓縮成 dist.zip
  4. 上傳文件到服務器

    • 使用 ssh2-sftp-client 模塊將 dist.zip 上傳到服務器的臨時目錄。
  5. 部署文件

    • 使用 node-ssh 模塊連接到服務器,刪除舊文件,解壓上傳的文件,並將文件移動到目標目錄。
  6. 清理臨時文件

    • 刪除服務器上的臨時文件和本地的 dist.zip

通過以上步驟,可以實現本地項目的自動化部署到服務器。希望本文對你有所幫助。