前言
当在前端开发中涉及部署时,自动化工具和脚本可以大大简化和改善部署流程。node.js 的 node-ssh 库为开发人员提供了一种便捷的方式来执行远程服务器上的 ssh 操作,使得前端部署脚本的开发变得更加高效。本文将介绍如何使用 node-ssh 开发前端部署脚本,以简化部署流程并提高效率。
什么是 node-ssh?
node-ssh 是一个 node.js 库,用于在 node.js 环境中执行 ssh 操作。它为开发人员提供了通过 ssh 协议连接远程服务器的功能,可以执行诸如上传文件、执行命令等操作。
初始化项目
确定结构
创建项目目录,并使用 npm init
或者 yarn init
初始化项目。
bash
ssh-publish/
├── config.js # 配置文件
├── index.js # 入口文件
├── package.json
└── ...
安装插件
安装 node-ssh、命令行插件 commander 、命令行交互插件 inquirer、文件和目录操作插件 glob、控制台字符样式插件 chalk。
注意 inquirer v9 使用了 esm 模块,如果使用 commonjs 需要使用 v8 版本。
perl
yarn add node-ssh commander inquirer@^8.0.0 glob chalk
编写脚本
配置文件
javascript
// config.js
module.exports = {
option: {
dev: {
remoteDir: "服务器项目目录",
host: "服务器 ip 地址",
port: "端口",
username: "服务器用户名",
passdword: "服务器密码",
localDir: "本地项目目录",
},
},
};
获取配置信息
javascript
// index.js
const process = require("process");
const dirArr = glob.sync(`${process.cwd()}/config.js`); // 加载工作目录的配置文件
const getConf = (types = [], config = []) => {
if (dirArr.length) {
const src = dirArr[0];
const { option } = require(src);
if (option) {
config = option;
types = Object.keys(option);
}
}
return { types, config };
};
const { types, config } = getConf();
命令行交互代码
javascript
// index.js
const { program } = require("commander");
const inquirer = require("inquirer");
program
.command("publish")
.description("发布项目")
.action(() => {
(async () => {
const { devType } = await inquirer.prompt([
{
name: "devType",
type: "list",
message: `请选择发布的版本`,
choices: "版本列表",
default: "默认选择的版本",
},
]);
// 获取选中的配置信息
const selectConfig = config[devType];
// ssh 连接并上传文件
...
})();
});
ssh 操作代码
javascript
// index.js
ssh
.connect({
host: selectConfig.host,
username: selectConfig.username,
port: selectConfig.port,
password: selectConfig.passdword,
tryKeyboard: true,
})
.then(() => {
// 连接成功开始上传文件
ssh
.putDirectory(selectConfig.localDir, selectConfig.remoteDir, {
// 文件上传回调
tick: (localPath, remotePath, error) => {},
})
.then(() => {
// 上传成功
});
});
增加检查配置和美化输出的函数
javascript
// index.js
const process = require("process");
const chalk = require("chalk"); /
const dirArr = glob.sync(`${process.cwd()}/config.js`);
// chalk 输出函数
const log = (str, color = "white") => {
console.log(chalk[color](str));
};
// 检查配置文件
const checkConf = (flag = true) => {
if (!dirArr.length) {
log(`${process.cwd()} 中没有 config.js`, "red");
return;
}
const target = [
"remoteDir",
"host",
"port",
"username",
"passdword",
"localDir",
];
if (config.length === 0) {
log("配置文件中缺少参数 options", "red");
return;
}
for (let item in config) {
const arr = Object.keys(config[item]);
target.forEach((e) => {
if (arr.indexOf(e) == -1) {
log(`${item} 中缺少参数 ${e}`, "red");
flag = false;
}
});
}
return flag;
};
index.js 完整代码
javascript
#!/usr/bin/env node
const { program } = require("commander");
const chalk = require("chalk");
const inquirer = require("inquirer");
const process = require("process");
const glob = require("glob");
const { NodeSSH } = require("node-ssh");
const ssh = new NodeSSH();
const dirArr = glob.sync(`${process.cwd()}/config.js`);
// 打印函数
const log = (str, color = "white") => {
console.log(chalk[color](str));
};
// 获取配置信息
const getConf = (types = [], config = []) => {
if (dirArr.length) {
const src = dirArr[0];
const { option } = require(src);
if (option) {
config = option;
types = Object.keys(option);
}
}
return { types, config };
};
const { types, config } = getConf();
// 检查配置文件
const checkConf = (flag = true) => {
if (!dirArr.length) {
log(`${process.cwd()} 中没有 config.js`, "red");
return;
}
const target = [
"remoteDir",
"host",
"port",
"username",
"passdword",
"localDir",
];
if (config.length === 0) {
log("配置文件中缺少参数 options", "red");
return;
}
for (let item in config) {
const arr = Object.keys(config[item]);
target.forEach((e) => {
if (arr.indexOf(e) == -1) {
log(`${item} 中缺少参数 ${e}`, "red");
flag = false;
}
});
}
return flag;
};
// 定义当前版本,通过 command 设置 -v 和 --version 参数输出版本号
const package = require("./package.json");
program.option("-v, --version").action(() => {
console.log(`v${package.version}`);
});
// publish 操作
program
.command("publish")
.description("发布项目")
.action(() => {
if (!checkConf()) return;
(async () => {
const { devType } = await inquirer.prompt([
{
name: "devType",
type: "list",
message: `请选择发布的版本`,
choices: types.filter((e) => {
return { name: e, value: e };
}),
default: types[0],
},
]);
const selectConfig = config[devType];
log(`配置: ${JSON.stringify(selectConfig, null, 2)}`);
//ssh连接服务器
ssh
.connect({
host: selectConfig.host,
username: selectConfig.username,
port: selectConfig.port || 22,
password: selectConfig.passdword,
tryKeyboard: true,
})
.then(() => {
log(`连接服务器成功`, "green");
log(`开始上传文件`, "green");
// 上传文件
ssh
.putDirectory(selectConfig.localDir, selectConfig.remoteDir, {
// 文件上传回调
tick: (localPath, remotePath, error) => {
if (error) {
log(error, "red");
} else {
log(`传输:${localPath} -> ${remotePath}`, "green");
}
},
})
.then(() => {
log(`✨ 上传成功`, "green");
process.exit(0);
});
});
})();
});
// 注册 -h
program.on("--help", () => {});
// 解析用户执行命令传入参数
program.parse(process.argv);
开发完成后可以发布到 npm 仓库或者直接在项目中封装脚本。
总结
node-ssh 可以帮助开发人员简化前端部署流程。通过使用它,你可以编写自定义的部署脚本,以自动化部署任务,提高效率并减少人为错误。