首先看一下需要什么依赖?
{
"name": "zdpl",
"version": "1.0.1",
"description": "自动部署",
"main": "./src/index.js",
"bin": {
"zc-deploy": "./src/index.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"deploy"
],
"publicConfig": {
"registry": "https://registry.npmjs.org/"
},
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.0",
"inquirer": "^8.0.0",
"node-ssh": "^11.1.1",
"ora": "^5.3.0",
"shelljs": "^0.8.5",
"zip-local": "^0.3.5"
}
}
脚本的目录结构如下:
1. chalk插件主要用于改变终端输出文字的样式
2. inquirer 用户交互命令行
3. node-ssh connect 用于用户连接服务器,exec 用于执行命令, putFIle用于推送文件到服务器指定位置。
4. ora 用于等待某个指令执行,类似loading效果
5. shelljs 是基于node.js的Unix shell命令的可移植实现。类似shell脚本可以写一下指令,然后执行。shelljs就是可以在js通过其中方法执行指令,在运行代码时依次执行。
6. zip-local用于压缩文件,用法查看npm官网
7. download-git-repo用于从github,gitlab等上拉取摸板。
8. commander 这里主要用来生成不同的指令,zc-deploy init 和 zc-deploy push
主要代码如下
需要在代码中配置deploy.config.ts 文件,使用zc-deploy init 初始化摸板。也可以自己配置,配置格式如下,需要知道公司项目发布的服务器ip,在此文件中声明即可。在执行yarn deploy时,会出现如下图选择部署的服务器。
module.exports = Object.freeze({
PRIVATE_KEY: 'C:/Us/.ssh/id_rsa', // 密钥地址
SERVER: [
{
NAME: 'server-501',
SERVER_PATH: '39.98.82.82',
SSH_USER: 'root', // 服务器用户名
SSH_KEY: ``, // 服务器密码
PORT: 22, // 端口 默认为22
DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
PATH: '/root/501', // 服务器存放静态文件目录
},
{
NAME: 'server-506',
SERVER_PATH: '192.168.117.133',
SSH_USER: 'root', // 服务器用户名
SSH_KEY: 'root', // 服务器密码
PORT: 22, // 端口 默认为22
DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
PATH: '/root/506', // 服务器存放静态文件目录
},
{
NAME: 'server-603',
SERVER_PATH: '192.168.117.133',
SSH_USER: 'root', // 服务器用户名
SSH_KEY: 'root', // 服务器密码
PORT: 22, // 端口 默认为22
DIST: './dist', // 需要部署的打包过后的文件夹 根据项目不同值不同 一般为 build static 或者dist
SCRIPT: 'yarn build:prod', // 打包命令 可能项目由不同的构建命令 如打包指定环境的代码
PATH: '/root/603', // 服务器存放静态文件目录
},
],
});
src/index.js:这是全局的入口。主要配置了两个命令:zc-deploy init 和zc-deploy push
#!/usr/bin/env node
program
.version(packageJson.version, "-v, --version")
.command("init")
.description("初始化部署相关配置")
.action(() => {
require("../lib/init")();
});
program
.version(packageJson.version)
.command("push")
.description("部署项目")
.action(async () => {
if (fs.existsSync(path.resolve(process.cwd(), "./deploy.config.ts"))) {
const CONFIG = require(path.resolve(process.cwd(), "./deploy.config.ts"));
const serverConfig = [...CONFIG.SERVER];
const answers = await inquirer.prompt([
{
type: "confirm",
name: "hasBuild",
message: "是否需要打包",
},
]);
let hasBuild = answers.hasBuild ? true : !existsSync(distZipPath);
const serverName = await inquirer.prompt([
{
type: "list",
name: "server_name",
message: "请选择部署的服务器",
choices: serverConfig.map((item) => item.NAME),
},
]);
runTask(hasBuild, serverName, serverConfig, CONFIG);
} else {
defaultLog(`缺少部署相关的配置,请运行"zc-deploy init"下载部署配置`);
}
});
lib/deploy.js:这个文件是,项目的部署流程文件。
#!/usr/bin/env node
const distZipPath = resolve(process.cwd(), "./dist.zip");
// 打包 npm run build 或yarn build
const compileDist = async (CURCONFIG) => {
// 进入本地文件夹
cd(process.cwd(), "./");
exec(CURCONFIG.SCRIPT || "yarn build:prod");
success("打包完成");
};
// ******压缩dist文件******
const zipDist = async (CURCONFIG) => {
try {
if (existsSync(distZipPath)) {
defaultLog("dist.zip已经存在,即将删除压缩包");
unlinkSync(distZipPath);
} else {
defaultLog("即将开始压缩zip文件");
}
let spinner = ora("文件开始压缩").start();
sync
.zip(path.resolve(process.cwd(), CURCONFIG.DIST || "./dist"))
.compress()
.save(distZipPath);
spinner.succeed("文件压缩成功");
} catch (error) {
errorLog(error);
errorLog("压缩dist文件失败");
}
};
// ********执行清空线上文件夹指令**********
const runCommond = async (commond, CONFIG) => {
const result = await SSH.exec(commond, []);
console.log(warning(result));
};
// ********* 执行清空线上文件夹指令 *********
const runBeforeCommand = async (CONFIG) => {
let spinner = ora("正在删除服务器文件").start();
await runCommond(`rm -rf ${CONFIG.PATH}/dist.zip`);
await runCommond(`rm -rf ${CONFIG.PATH}/dist`);
spinner.succeed("删除服务器文件成功");
};
// 通过ssh上传文件服务器
const uploadZipBySSH = async (CONFIG) => {
// 上传文件
let spinner = ora("准备上传文件").start();
try {
await SSH.putFile(distZipPath, CONFIG.PATH + "/dist.zip");
// await SSH.putFile(distZipPath, '/root/dist.zip');
spinner.succeed("文件上传服务器成功");
let spinner1 = ora("完成上传,开始解压").start();
await runCommond(`unzip ${CONFIG.PATH}"/dist.zip" -d ${CONFIG.PATH}/dist`);
spinner1.succeed("文件解压成功");
success(`${CONFIG.NAME}部署完成`);
process.exit(0);
} catch (err) {
errorLog(err);
errorLog("上传失败");
}
};
const getServerConfig = (serverName, serverConfig) => {
return serverConfig.find((item) => {
return item.NAME == serverName;
});
};
// 连接ssh
const connectSSH = async (CURCONFIG, CONFIG) => {
defaultLog(`尝试连接服务:${CURCONFIG.SERVER_PATH}`);
defaultLog(`密钥路径${CONFIG.PRIVATE_KEY}`);
let spinner = ora("正在连接").start();
try {
SSH.connect({
host: CURCONFIG.SERVER_PATH,
username: CURCONFIG.SSH_USER,
password: CURCONFIG.SSH_KEY,
privateKey: Buffer.from(CONFIG.PRIVATE_KEY).toString() ?? "",
port: CURCONFIG.PORT || 22,
}).then(async () => {
// exec(`sh ${connect} ${CURCONFIG.SERVER_PATH} ${CURCONFIG.PATH}`);
spinner.succeed("服务器连接成功");
await runBeforeCommand(CURCONFIG);
await uploadZipBySSH(CURCONFIG);
});
} catch (err) {
errorLog(err);
errorLog("ssh连接失败");
}
};
async function runTask(hasBuild, serverName, serverConfig, CONFIG) {
const CURCONFIG = getServerConfig(serverName.server_name, serverConfig);
if (hasBuild) {
await compileDist(CURCONFIG);
}
await zipDist(CURCONFIG);
await connectSSH(CURCONFIG, CONFIG);
}
module.exports = runTask;
lib/init.js :这个文件是初始化配置文件,生成一系列配置信息(deploy.config.ts)
const deployTem = "github:success-c/deploy-template";
const checkDeployExists = () => {
if (fs.existsSync(deployConfigPath)) {
defaultLog("根目录下已经存在deploy.config.ts文件,请勿重新下载");
process.exit(1);
}
downloadAndGenerate(deployTem);
};
const downloadAndGenerate = (templateURL) => {
const spinner = ora("开始生成部署模板");
spinner.start();
download(templateURL, process.cwd(), { clone: false }, (err) => {
if (err) {
errorLog(err);
process.exit(1);
}
spinner.stop();
success("模板下载成功,模板位置:/deploy.config.js");
defaultLog("请配置根目录下的deploy.config.js配置文件");
console.log("注意:请删除不必要的环境配置");
process.exit(0);
});
};
module.exports = () => {
checkDeployExists();
};
lib/utils : 打印信息的方法
const chalk = require("chalk");
const { red, blue, green, yellow } = chalk;
const errorLog = (error) => console.log(red(`=========>${error}`));
const defaultLog = (log) => console.log(blue(`=========>${log}`));
const success = (log) => console.log(green(`=========>${log}`));
const warning = (log) => console.log(yellow(`${log}`));
module.exports = {
errorLog,
defaultLog,
success,
warning,
};
源码地址:
deploy: node脚本实现自动化部署https://gitee.com/liu--zicheng/deploy.git