Cómo implementar la automatización del despliegue de un proyecto local en el servidor

Este artículo describe el proceso de automatización del despliegue de un proyecto local en el servidor. El proceso incluye los siguientes pasos: empaquetar el proyecto, comprimir los archivos empaquetados, subir los archivos comprimidos al servidor, descomprimir los archivos en el servidor y desplegar los archivos descomprimidos en el directorio especificado, y finalmente limpiar los archivos temporales. A continuación se muestra la implementación del código.

Preparación

Primero, necesitamos instalar algunos paquetes npm necesarios:

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

Implementación del código

A continuación se muestra la implementación completa del código en 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); // Obtener la ruta absoluta del archivo
10
const __dirname = path.dirname(__filename); // Obtener el nombre del directorio
11
12
const ssh = new NodeSSH();
13
const sftp = new Client();
14
15
const serverConfig = {
16
host: "tu IP de servidor", // IP del servidor
17
username: "nombre de usuario", // Nombre de usuario
18
privateKey: "./private.pem", // Clave privada
19
// Si utilizas contraseña para iniciar sesión
20
//password: "contraseña", //
21
uploadPath: "/root/tmp/blog", // Directorio temporal para la subida de archivos
22
path: "/deploy/path", // Directorio de despliegue objetivo
23
};
24
25
async function buildProject() {
26
console.log("Construyendo el proyecto...");
27
28
return new Promise((resolve, reject) => {
29
exec("pnpm build", (error, stdout, stderr) => {
30
if (error) {
31
console.error(`Error de construcción: ${error.message}`);
32
return reject(error);
33
}
34
if (stderr) {
35
console.error(`Stderr de construcción: ${stderr}`);
36
return reject(new Error(stderr));
37
}
38
console.log(`Stdout de construcción: ${stdout}`);
39
resolve();
40
});
41
});
42
}
43
44
async function deleteLocalDist() {
45
console.log("Eliminando dist local...");
46
47
return new Promise((resolve, reject) => {
48
exec("rm -rf shell/dist.zip", (error, stdout, stderr) => {
49
if (error) {
50
console.error(`Error al eliminar: ${error.message}`);
51
return reject(error);
52
}
53
if (stderr) {
54
console.error(`Stderr al eliminar: ${stderr}`);
55
return reject(new Error(stderr));
56
}
57
console.log(`Eliminado con éxito`);
58
resolve();
59
});
60
});
61
}
62
63
async function compressDist() {
64
console.log("Comprimiendo el directorio dist...");
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 ha sido creado. Total de bytes: ${archive.pointer()}`,
74
);
75
resolve();
76
});
77
78
archive.on("error", (err) => {
79
console.error(`Error de compresión: ${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("Subiendo dist.zip al servidor...");
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"), // Usar path.posix.join para manejar la ruta
101
);
102
103
await sftp.end();
104
console.log("Subida completa.");
105
}
106
107
async function deploy() {
108
try {
109
await buildProject();
110
await compressDist();
111
await uploadToServer();
112
113
console.log("Conectándose al servidor...");
114
await ssh.connect({
115
host: serverConfig.host,
116
username: serverConfig.username,
117
privateKey: fs.readFileSync(serverConfig.privateKey, "utf8"),
118
});
119
120
console.log("Eliminando archivos antiguos...");
121
await ssh.execCommand(`rm -rf ${serverConfig.path}/*`);
122
123
console.log("Descomprimiendo archivos subidos...");
124
await ssh.execCommand(
125
`unzip ${serverConfig.uploadPath}/dist.zip -d ${serverConfig.uploadPath}`,
126
);
127
128
console.log("Moviendo archivos al directorio de destino...");
129
await ssh.execCommand(
130
`mv ${serverConfig.uploadPath}/dist/* ${serverConfig.path}`,
131
);
132
133
console.log("Limpiando...");
134
await ssh.execCommand(`rm -rf ${serverConfig.uploadPath}/dist`);
135
await ssh.execCommand(`rm ${serverConfig.uploadPath}/dist.zip`);
136
137
console.log("Despliegue completo.");
138
ssh.dispose();
139
140
await deleteLocalDist();
141
} catch (error) {
142
console.error(`Error de despliegue: ${error.message}`);
143
ssh.dispose();
144
}
145
}
146
147
deploy();

Explicación del código

  1. Configurar la información del servidor:

    • El objeto serverConfig contiene la dirección IP del servidor, el nombre de usuario, la ruta de la clave privada, el directorio de carga y el directorio de destino.
  2. Construir el proyecto:

    • Se usa la función exec para ejecutar el comando pnpm build y construir el proyecto.
  3. Comprimir los archivos empaquetados:

    • Se usa el módulo archiver para comprimir el directorio dist en un archivo dist.zip.
  4. Subir archivos al servidor:

    • Se usa el módulo ssh2-sftp-client para subir el archivo dist.zip al directorio temporal del servidor.
  5. Desplegar los archivos:

    • Se usa el módulo node-ssh para conectarse al servidor, eliminar los archivos antiguos, descomprimir los archivos subidos y moverlos al directorio de destino.
  6. Limpiar archivos temporales:

    • Se eliminan los archivos temporales en el servidor y el archivo dist.zip local.

Siguiendo estos pasos, se puede automatizar el despliegue de un proyecto local en el servidor. Espero que este artículo te haya sido de ayuda.