本文内容均只用于支付宝和微信小程序
背景
先说一下为什么要做这个东西把。
- 基于提审
- 原生小程序中漫长的提审流程,合PR,切到主分支,pull代码,点击上传,后台提审
- 原生小程序基于config文件决定抛出环境,提审时项目环境完全靠人工校验
- 基于测试
- 测试bugfix校验或者后端同学需要测试环境时,通常都是直接跟我们要二维码,这时我们可能在开发别的项目,本地代码不纯净,所以普遍我们都是commit本地代码,切到干净的分支,然后切好测试环境,在生成二维码给到他们,然后再切回开发分支。这也太麻烦了吧。所以我们开发了一个生成小程序二维码的平台,可以让他们完成脱离我们
- 测试同学可能对git不熟悉,而且对怎么切换环境也不熟悉
就是基于上面这几点原因让我们基于支付宝和微信提供的SDK开发了属于我们小程序的CI/CD,尽最大可能解放一些繁琐且打断我们思路的工作
功能介绍
- 自动上传小程序后台,PR合并后触发
- 校验环境,version自增1(根据自己的项目自行补充需要的校验和功能)
- 上传完成后叮叮通知,通知信息包括version,体验版二维码,包信息
- 小程序预览平台
- 用户体系:普通(可构建),管理员(可上传),超管(可添加用户)
- 获取所有分支,可切分支
- 获取当前环境,可切换环境
- 定义入口页
- 定义入口页参数
- 构建预览二维码
- 管理员以上可手动上传
本篇文章先说一下自动上传是如何实现的
gitee webHook配置
添加webhook
勾选PR
添加完成
完成如上步骤后发起PR和更新PR后,码云会发起一个post请求到我们的承接接口。
接口实现
注册之前码云上填写的路由来接收WebHook
router.post('/upload/:appKey', async ctx => {
// ...log
}
启动node服务,这时我们去刚刚添加完成的页面点一下测试按钮,接口就能接收到码云发来的请求啦~
既然第一步已经完成,那么我们梳理一下这个接口需要实现的内容:
- 校验PR的目标分支和PR的状态
- clone或者pull目标分支的代码至本地
- 校验是否需要上传,version是否需要+1,是否为正式环境(与自己项目耦合的功能)
- 叮叮通知开始上传
- 上传
- 叮叮通知上传成功
- 结束
梳理完之后是不是非常的清晰,接下来让我们一步步来实现它
校验目标分支和PR状态
因为我们所有的PR目标分支均是master,并且需要提审的版本也是master,所以在接收到请求之后,需要判断一下目标分支是不是master并且PR的状态是不是合并完成。
WebHook推送数据的数据类型文档
PS:PR分为新建,更新,合并,关闭,每次状态更新都会发起一次请求,所以需要过滤不是合并的请求
router.post('/upload/:appKey', async ctx => {
const { body } = ctx.request
if (body.state === 'merged' && body.target_branch === "master") {
// ...
}
}
拉取代码
校验完之后就需要拉取代码,如果项目目录存在就去pull,不存在就去clone(偷个懒,确保你的目录是存在的,不然会报错)
if (fs.existsSync(xcxpath)) {
child_process.execSync('git pull', {
cwd: xcxPath
})
} else {
child_process.execSync(`git clone ${git地址}`, {
cwd: path.join(xcxPath, '..'))
})
}
自定义校验
接下来就需要去做一些与自己项目相关的上传前的校验,比如我们有些时候可能存在多个PR,不想每次合并后都上传一次,这时我们利用PR中的标签来判断是否需要上传,当我们选中aNotUpload标签时,这个PR将不会上传
let arr = body.pull_request.labels.filter(item => item.name === 'aNotUpload')
if( arr.length ) {
// ...存在,所以不上传
ctx.body = {
code: 200,
msg: '自主选择不上传',
success: true
}
return
}
然后我们也维护了一套自己的version用来区分每次的版本,但是每次都需要手动去+1,非常不合理。为了完成这个自动化,在合并PR之后(合并了所有commit)偷偷在master增加version并且push(暂时没有想到更合理的方法)
const parser = require("@babel/parser");
const generator = require("@babel/generator").default;
const traverse = require("@babel/traverse").default;
// 获取ast
function readConfig(root) {
const configFile = path.join(root, '/config/config.js')
const context = fs.readFileSync(configFile, "utf-8")
const ast = parser.parse(context)
return {
ast,
file: configFile
}
}
// version是否+1
function prodHooks({
root,
appKey
}) {
let {
ast,
file
} = readConfig(root)
let isVersionUp = false
// 是否上升了一个版本号
const diffStr = child_process.execSync(`git diff HEAD^ config/config.js`, {
cwd: '项目路径',
encoding: 'utf-8'
})
// 获取不同的version,大概得到['- version: "10.0.0"', '+ version: "10.0.1"']
let diffArr = diffStr.match(/(\+|-)(.*)version:[^\r\n]*[\r\n]/g)
if (diffStr && diffArr.length) {
// 提取数字
diffArr = diffArr.map(i => i.replace(/\D*/g, ''))
// +的version是否比-的version大,是的话就完成了version的提升
isVersionUp = diffArr[1] - diffArr[0] > 0
}
// 没有增加version,自动+1
if(!isVersionUp) {
traverse(ast, {
enter(p) {
if (p.isObjectProperty()) {
let name = p.node.key.name
if (name === 'version' || name === 'versionCode') {
let val = p.node.value.value
if (typeof val === 'string') {
val = versionUpdate(val)
} else {
val += 1
}
p.node.value.value = val
}
}
}
})
const {
code
} = generator(ast)
fs.writeFileSync(file, code, "utf-8")
child_process.execSync(`git add . && git commit -m 'version up' && git pull && git push`, {
cwd: '项目目录'
})
}
return true
}
/**
* 升级版本号
* @param {string} val '2.09.09' | '99.99.99'
* @return {string} '2.09.10' | '100.00.00'
*
*/
function versionUpdate(val) {
let num = [...(1 + Number(val.replace(/\D/g, '')) + '')]
for(let i=val.length-1; i>=0; i--){
if(val[i] === '.') {
num.splice(i - val.length + 1 , 0, '.')
}
}
return num.join('')
}
到这里我们完成了所有上传前的校验和自定义处理,接下来就到了最重要的通知和上传阶段啦~
钉钉群通知
平常上班主要使用钉钉,所以接入钉钉群机器人通知上传进度
PS:钉钉群机器人SDK
先创建一个钉钉群并设置自定义机器人获取accessToken和secret
点击完成后打开机器人
就可以得到accessToken和secret
然后利用sdk向钉钉群发送一个推送吧
const Robot = require('dingtalk-robot-sdk');
const robot = new Robot({
accessToken: 'xxxx',
secret: 'xxxx'
});
const text = new Robot.Text('走起~')
robot.send(text)
上传
支付宝和微信小程序利用sdk上传前的准备工作这边就不说啦,大家跟着官方文档去配置吧
PS:微信上传文档,支付宝上传文档
支付宝
alipaydev.setConfig({
toolId,
privateKey,
})
const uploadResult = await alipaydev.miniUpload({
appId: '支付宝appid',
clientType: 'alipay',
project: '项目地址',
experience: true // 设置为体验版
})
微信
const project = new ci.Project({
appid,
type: 'miniProgram',
projectPath,
privateKeyPath
})
const year = new Date().getFullYear() - 2000
let month = new Date().getMonth() + 1
const day = new Date().getDate()
// 根据年月日当版本号
const version = '2.5.' + year + (month < 10 ? '0' + month : month) + (day < 10 ? '0' + day : day);
// 上传
const previewResult = await ci.upload({
project,
version,
desc: body.title,
setting: {
es6: true,
minify: true,
autoPrefixWXSS: true,
minifyWXML: true,
minifyWXSS: true,
minifyJS: true
}
})
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!