场景
进入公司一段时间了。流程还是不太让人省心。就在上个提测版本的质量还是没法保证,总是或多或少出现一些问题。于是就想到了上家公司的一个做法。就是在提测前部署一个预览环境,在提测前,每个人本地验证一遍,再放在预览环境验证一遍。
实施
有了想法,就得去付诸行动。先是用公司的一个测试服务器单独起了一个服务,用nginx来做反向代理。然后把前端代码部署上去。
整个部署流程是:用 xshell 连上服务器,然后用 xftp 链接服务器,然后本地 build 项目,接着把 build 好的文件通过 xftp 上传到服务器上。整个流程感觉稍有繁琐。而且,对于不熟悉 shell 工具的同学不友好啊。重复的工作啊有没有。
改进
既然前端是万能的,那么这种重复的操作在我们前端工作流中是不被允许的。刚好项目进度不是那么忙。于是就准备啪啪啪撸一个 build 完成自动发布预览环境的工具。
要完成这个工具,我们来梳理一下需要实现的功能:
- 本地文件压缩功能
- 链接远程服务器
- 压缩包上传远程服务器
- 远程服务器解压缩
- 本地 build 完毕的一个回调
可能你会说,你傻啊,不会直接上传,还要压缩解压缩。这里用压缩包传输一方面是方便做备份。我考虑的是上传在一个backup的文件夹里,然后用的时候再从这个文件夹取出来,然后解压到置顶目录。提测的时候也可以直接让测试来这个目录去压缩包。
另一方面是不想写递归啊。如果要上传的目录结构不确定少不了递归啊,递归啊,递归啊。我怕我头大。
本地文件压缩
于是创建一个 ftp.js 的文件,来实现上传本地 build 完的 dist 目录下的文件到服务器。
这里主要使用了 archiver 这个模块来实现压缩功能。
用 archiver 压缩本地文件:
let output = fs.createWriteStream(__dirname + '/../dist/' + fileName);
let archive = archiver('zip');
output.on('end', function () {
console.log('Data has been drained');
});
output.on('error', function (err) {
console.log('压缩失败');
throw err;
});
output.on('close', function () {
console.log('压缩成功');
});
archive.pipe(output);
archive.file(__dirname + '/../dist/index.html', {name: 'index.html'});
archive.directory(__dirname + '/../dist/static/', 'static');
archive.finalize();
fileName 是一个加戳的变量:
let fileName = 'dist' + new Date().getTime() + '.zip';
说道这里,其实还可以把 git 的分支或者 tag 信息放在这个戳里边。用相关的 nodejs 模块来获取一下 git 仓库信息就好啦。有空我要来优化一波。
链接远程服务器和上传文件。
主要使用了 ssh2 这个模块来实现和远程服务器的交互。
链接远程服务器:
conn.on('ready', function () {
console.log('连接上了!')
}).on('end', function () {
console.log('完成')
}).connect({
host: 'ip',
port: 端口号,
username: '用户名',
password: '密码'
});
上传文件:
conn.sftp(function (err, sftp) {
if (err) throw err;
// 上传文件测试
sftp.fastPut(__dirname + '/../dist/' + fileName, '/usr/share/nginx/htmlBackup/' + fileName, {}, (err, result) => {
if (err) {
console.log(err);
}
if (result) console.log(result);
});
});
删除上个版本解压缩部署新版本
本来,使用 ssh2 的 shell 命令来实现这两个功能应该是很简单的。可是,没逼格啊。麻烦啊。于是我就想到 shell 脚本。说实话,在此之前,我也不知道 shell 脚本咋玩滴。但是我可以学嘛。于是,学习一波后就有了如下 html.sh 文件:
#!/bin/bash
# arg1:dir name
# arg2:zip name
# delete old file
deleteFile=$1
sudo chmod -R 777 /usr/share/nginx/$deleteFile
sudo rm -rf /usr/share/nginx/$deleteFile/*
# unzip file
unzipFile=$2
sudo unzip -o /usr/share/nginx/htmlBackup/$unzipFile -d /usr/share/nginx/$deleteFile
# exit
其实 shell 脚本也不算复杂,大概一上午吧,写了这么几行。服务器权限设置有点严格,所以先用 chmod 给老文件设置权限,我懒于是就用 777 。然后,rm 删除老文件,然后解压缩 zip 包。其中 $1 和 $2 是调用这个 shell 传入的参数。
好了,说了这么多,shell 怎么用。当然还是要回到 ssh2 上。代码如下:
// 调用远程构建shell
conn.exec('/usr/share/nginx/html.sh html ' + fileName, function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).on('data', function(data) {
let dataStr = ('' + data).trim();
// 列出已经上传过的 zip 包
if (dataStr.length > 5) {
console.log('STDOUT: ' + ('' + data).trim());
}
}).stderr.on('data', function(data) {
console.log('STDERR: ' + ('' + data).trim());
});
stream.on('end', function() {
console.log('预览环境构建完成');
});
});
新增功能基本上大功告成了。
build 回调
首先找到 vue-cli build 完成后的位置,也就是 build/build.js 配置中大概40行的位置。然后把上边我们写的功能导出模块,然后再 build.js 中引入 ftp.js。
let ftp = require('./ftp')
注意,这里我并不想破坏原有的命令的功能,于是就通过判断执行命令时的参数来控制功能的启用与否。process.argv
这个变量存放的就是调用命令的参数。具体是什么用的,console.log 一下就都知道啦。
于是就有了下图:
那么命令输入长了,敲字手疼啊。于是就修改 package.json 的 scripts。
添加如下两行:
"bap": "node build/build.js buildWithPublish",
"p": "node build/ftp.js publish"
这样,我们使用的时候就可以直接 npm run bap 来 build 和 发布预览环境。用 npm run p 来直接把已经 build 好的文件发布到预览环境。
总结
好吧,作为一个能用即可的懒人,代码写的有点乱,整个功能的代码就不贴啦。基本上核心功能的代码就是上述说的那些。
避免重复就是节约时间啊。用了大概一天时间来实现这个功能我觉得很值。这样以后组里的其他同事也都可以用一行命令自己部署预览环境了。不用打开 xshell 打开 xftp。
基于上述功能,还做了一个提测小工具:https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。