最近因为修改自己的文件上传系统有点频繁,想想能不能做一个自动化部署系统:只要我push github然后它就自动打包部署了
基于我这样的需求,说干就干!!!
webhooks
探究了一翻,发现一个好东西啊!
它是干嘛的?
下面三种类型你选择一种
- 第一种:git提交push
- 第二种:git干的一切
- 第三种:自定义git哪些操作
如果你干了你选择类型的操作,他就会自动请求你定义的请求地址(Payload URL)
那不就是我所需要的吗!!!nice~
功能定义
- 一个node服务,接受请求
- 打包进程
- 钉钉通知
代码其实很简单,因为仅仅只是为了 懒 而已!
接受请求
http.createServer((req, res) => {
const params = querystring.parse(req.url.split('?')[1])
if (params.projectName && params.projectName === 'ossUpload') {
autobuild()
}
res.end('开始打包了')
})
自动打包部署
autobuild:创建一个进程来跑 shell命令执行打包部署
const notice = require('./notice') // 钉钉通知
const PM2_NAME = 'upload'
const PROJECTNAME = 'ossUpload'
function write (cp) {
notice('### 准备打包')
cp.stdout.on('data', (chunk) => {
console.log('' + chunk)
})
cp.on('exit', (code) => {
notice(`### 部署成功!`)
});
}
// autobuild
module.exports = () => {
const { exec } = require('child_process');
function cpExec (shell, update = true) {
// 是否已经是pm2部署过的
let shellConnect = update ? `pm2 delete ${PM2_NAME} && ` + shell : shell
return exec(shellConnect, { maxBuffer: 200 * 1024 * 1024 }, (error, stdout, stderr) => {
if (error) {
let cp = cpExec(shell, false)
write(cp)
}
});
}
let cp = cpExec(`git pull && cnpm i && cd .. && cd ${PROJECTNAME} && npm run start`)
write(cp)
}
整个核心其实是下面这句:
cpExec(`git pull && cnpm i && cd .. && cd ${PROJECTNAME} && npm run start`)
然后一步步执行脚本语句
- 拉取git
- 装载npm
- 返回上一级
- 选择文件上传系统文件夹
- 跑文件内package.json script start命令
start命令如下:
'start': 'nuxt build && cross-env NODE_ENV=production && pm2 start --name upload server/index.js',
也是一步步执行脚本语句
- 执行打包操作
- 定义环境变量为生产环境
- pm2 启动上线及进程守护
至此就完成部署上线的过程
消息通知
这里我用钉钉通知我,打包开始以及部署结束
notice
参考钉钉的以下两个文档
定义请求函数(可忽略)
注:用https模块
const https = require('https');
const querystring = require('querystring')
// 请求函数
const request = (path, params, method = 'GET') => {
let postData = ''
if (method === 'GET') {
postData = `?${querystring.stringify(params)}`
path += postData
} else {
postData = JSON.stringify(params)
}
const options = {
hostname: 'oapi.dingtalk.com',
port: 443,
path: path,
method: method,
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData)
}
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let dataJSON = ''
res.setEncoding('utf8');
res.on('data', (chunk) => {
dataJSON += chunk
});
res.on('end', () => {
const data = JSON.parse(dataJSON)
if (!data.errcode) {
resolve(data)
} else {
console.error(data)
process.exit(1)
}
});
}).on('error', (e) => {
console.error(`出现错误: ${e.message}`);
reject()
});
// 将数据写入请求主体。
req.write(postData);
req.end();
})
}
调用钉钉api逻辑
// 获取token
let token = ''
const getToken = () => {
return request('/gettoken', {
appkey: '申请的钉钉应用的key',
appsecret: '申请的钉钉的密码'
}).then(res => {
token = res.access_token
})
}
const sendMessage = (markdown) => {
return request(`/topapi/message/corpconversation/asyncsend_v2?access_token=${token}`, {
agent_id : '799270047', // 应用id
msg: {
msgtype: 'markdown',
markdown :{
title: `${new Date().valueOf()}`,
text: markdown
}
},
to_all_user: 'false', // 设置为true 则下面的部门不用填
dept_id_list: '364849020' // 部门id
}, 'POST')
}
module.exports = async (markdown) => {
!token && await getToken()
await sendMessage(markdown)
}
简单逻辑:
用一个变量存储token,因为就为了我一个文件上传使用系统,如果已经存在token那么直接发送消息,否则先文件上传获取token
至此简单自动化部署结束!再次推荐一下我的这个文件上传系统(开源在完善)
结语
其实还有很多问题,比如下次打包时他会杀掉当前的进程然后部署,那么就会导致当前线上就无法访问了等等一些问题,目前在写的时候我已经有想法了,嗯~等我优化完之后,下一篇补上。