前端项目或者nodejs项目分开发、测试、生产环境,有的没有没有接入 jenkins。每次都需要进行本地打包, 手动压缩上传到服务器目录,ssh 登录服务器后备份旧文件, 手动删除文件再将包解压到指定目录,操作流程比较繁琐,需要提前了解服务器部署目录,不太友好。
根据packeage.json 的scripts 来配置测试和生产环境的脚本。
根据运行脚本获取相关环境的配置
在部署脚本里面处理环境配置,然后实现
SSH登陆服务器
压缩目录
上传文件到服务器
备份并删除服务器上的原文件
解压服务器上的文件
npm install dotenv dotenv-cli node-ssh silly-datetime archiver
以下是对这几个包的简要说明:
dotenv
:dotenv是一个流行的Node.js库,用于从.env
文件中加载环境变量,并使其在应用程序中可用。它可以帮助您在开发过程中轻松管理环境变量,而无需硬编码到代码中。dotenv-cli
:dotenv-cli是dotenv的命令行工具,它允许您在命令行中运行脚本时加载.env
文件中的环境变量。这对于在命令行中运行脚本或执行其他命令时,临时设置环境变量非常有用。node-ssh
:node-ssh是一个用于在Node.js中执行SSH操作的库。它提供了一组API,使您可以连接到远程服务器,执行命令,上传和下载文件等。它对于在Node.js应用程序中与远程服务器进行交互非常有用。silly-datetime
:silly-datetime是一个简单的日期和时间格式化工具。它提供了一些函数,可以帮助您格式化日期和时间,例如将日期格式化为指定的字符串,获取当前时间戳等。archiver
:archiver是一个用于创建和提取归档文件(如zip、tar等)的库。它提供了一组API,使您可以创建压缩文件,添加文件或文件夹到压缩文件中,以及从压缩文件中提取文件。它对于在Node.js中处理归档文件非常有用。环境变量文件 .env.test
SERVER_IP=47.xx.xx.209
SERVER_USER_NAME=root
SERVER_PASSWORD=xxxxxxx
SERVER_PROJECT_PATH=/www/wwwroot/xxxx
DIST_PATH=dist
package.json
"scripts": {
"build-deploy:test": "dotenv -e .env.test node deploy.js",
"build-deploy:pro": "dotenv -e .env.production node deploy.js",
}
部署代码 deploy.js
const fs = require('fs');
const path = require('path');
const archiver = require('archiver');
const {NodeSSH} = require('node-ssh');
const sd = require('silly-datetime');
// 加载配置文件
require('dotenv').config()
// 当前时间
const curTime = sd.format(new Date(), 'YYYYMMDDHH');
const distPath = path.resolve(__dirname, process.env.DIST_PATH);
const ssh = new NodeSSH();
// 远程服务器配置信息
let config = {
host: process.env.SERVER_IP,
username: process.env.SERVER_USER_NAME,
password: process.env.SERVER_PASSWORD,
pathUrl: process.env.SERVER_PROJECT_PATH,
}
// 本地文件上传至远程服务器
async function uploadFile() {
try {
await ssh.connect({
host: config.host,
username: config.username,
password: config.password,
port: 22,
});
console.log('SSH login success');
await ssh.putFile(
`${__dirname}/dist${curTime}.zip`,
`${config.pathUrl}/dist${curTime}.zip`
);
console.log('The zip file is uploaded successfully');
remoteFileUpdate();
} catch (err) {
console.log('The file upload failed:', err);
process.exit(0);
}
}
// 服务端远端文件解压,更新
async function remoteFileUpdate() {
let cmd =`mv dist dist.bak${curTime} && unzip dist${curTime}.zip`;
try {
const result = await ssh.execCommand(cmd, {
cwd: config.pathUrl,
});
console.log(`The update message is: ${result.stdout}`);
if (!result.stderr) {
console.log('Congratulations! Update success!');
process.exit(0);
} else {
console.log('Something went wrong:', result);
process.exit(0);
}
} catch (err) {
console.log('SSH connection failed:', err);
process.exit(0);
}
}
// 本地文件压缩
function zipDirector() {
try {
const output = fs.createWriteStream(`${__dirname}/dist${curTime}.zip`);
const archive = archiver('zip', {
zlib: {level: 9},
});
output.on('close', (err) => {
if (err) {
console.log('Something went wrong with the zip process:', err);
return;
}
uploadFile();//本地文件上传至远程服务器
console.log(`${archive.pointer()} total bytes`);
console.log('Archiver has been finalized and the output file descriptor has closed.');
});
output.on('end', () => {
console.log('Data has been drained');
});
archive.pipe(output);
//要压缩的文件夹路径,和输出路径
// 设置本地 dist 文件路径
console.log(distPath)
// 将指定目录打包压缩到压缩包中,并且重命名为h5
archive.directory(distPath, "dist");
archive.finalize();
} catch (e) {
console.log(e)
}
}
(function() {
// 更新代码
zipDirector();
})()