How to Automate Local Project Deployment to a Server

This article will describe the process of automating the deployment of a local project to a server. The process includes the following steps: packaging the project, compressing the packaged files, uploading the compressed files to the server, extracting the files on the server, deploying the extracted files to the specified directory, and finally cleaning up temporary files. Below is the specific implementation code.

Preparations

First, we need to install some necessary npm packages:

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

Code Implementation

Here is the complete code implementation, written in 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); // Get the absolute path of the file
10
const __dirname = path.dirname(__filename); // Get the directory name
11
12
const ssh = new NodeSSH();
13
const sftp = new Client();
14
15
const serverConfig = {
16
host: "your server IP", // Server IP
17
username: "username", // Username
18
privateKey: "./private.pem", // Private key
19
// If you use password login
20
//password: "password",
21
uploadPath: "/root/tmp/blog", // Temporary upload directory
22
path: "/deploy/path", // Target deployment directory
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"), // Use path.posix.join for paths
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();

Code Explanation

  1. Configure Server Information:

    • The serverConfig object contains the server’s IP address, username, private key path, upload directory, and target directory.
  2. Build Project:

    • Use the exec function to run the pnpm build command to build the project.
  3. Compress the Built Files:

    • Use the archiver module to compress the dist directory into dist.zip.
  4. Upload Files to the Server:

    • Use the ssh2-sftp-client module to upload dist.zip to the temporary directory on the server.
  5. Deploy Files:

    • Use the node-ssh module to connect to the server, remove old files, extract the uploaded files, and move them to the target directory.
  6. Clean Up Temporary Files:

    • Delete the temporary files on the server and the local dist.zip.

By following these steps, you can achieve automated deployment of local projects to a server. I hope this article is helpful to you.