0%

Jenkins Pipeline系列(三)—— 使用扩展共享库构建微信小程序项目

Jenkins Pipeline 微信小程序

手动上传不是挺方便的吗

在初期,小程序开发者1-2人时,通过Win和Mac小程序开发者工具,进行上传确实比较省时省力,但是随着小程序业务代码增多,并行开发任务增多,开发者增多, 在管理各种版本上,都需要牵扯精力,而且上传发布很可能是多个人都会进行的事情了。我们并不能保证每个上传人的环境统一、AppID每次替换都不出错误等等。随着这些问题的发生,自然而然,我们会把频繁做且流程化的事情,做成自动化。

环境说明

硬件

  • Mac Mini(OR Windows本,这里我使用的是Mac Mini)

软件

  • Jenkins(Docker):Pipeline构建平台
  • Node(v12.13.0) & Npm(6.12.0):编译工具
  • 微信开发者工具:主要依赖工具
  • Nginx(Docker):内网图片服务
  • Gitlab(Docker):代码库

关于Jenkins、Gitlab,以及Jenkins的扩展共享库,请参考下面的链接
Jenkins Pipeline系列(一)– 如何配置扩展共享库

流程说明

  • 编译
  • 预览(按需登录)
  • 上传(按需登录)

Pipeline实现

这里还是使用的是Jenkins的扩展共享库来实现的,在上面有共享扩展库的文章说明,请参考
在共享扩展库的Git Repo的vars目录下新建wechat_mini_program_build.groovy,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/**
配置参数说明:
--------------------------------------------------------------
参数名称: REPO_URL
参数类型: 文本参数
默认值: ssh://git@gitlab.xxx.com/group_name/repo_name.git
描述: 项目代码仓库地址
--------------------------------------------------------------
参数名称: BUILD_BRANCH
参数类型: 字符参数
默认值: some-branch
描述: 构建分支<br>
some-branch<br>
develop<br>
release<br>
master
--------------------------------------------------------------
参数名称: BUILD_ENV
参数类型: Active Choices Parameter
Groovy Script: return["请选择","dev","test","prod"]
描述: dev开发环境<br>
test测试环境<br>
prod生产环境
--------------------------------------------------------------
参数名称: BUILD_TYPE
参数类型: 选型参数
选型: preview
upload
描述: preview: 预览生成二维码<br>
upload: 上传代码
--------------------------------------------------------------
参数名称: UPLOAD_VERSION
参数类型: 字符参数
默认值:
描述: 上传代码版本号: 如 2.0.0<br>
只有upload任务需要
*/
def call(Map config) {
node('front-end') {
properties([
buildDiscarder(
logRotator(
daysToKeepStr: '30',
numToKeepStr: '50'
)
)
])
stage('清理工作区') {
log.info '清理工作区'
deleteDir()
}
stage('获取代码') {
log.info "获取代码地址:${REPO_URL},获取代码分支: ${BUILD_BRANCH}"
fetch_code "${REPO_URL}"
}
stage('编译') {
log.info "构建:${BUILD_ENV}"
sh "source ~/.bash_profile && npm install && npm run build:${BUILD_ENV}"
}
stage('替换APPID'){
miniProgramAppId = getMiniProgramAppId("${BUILD_ENV}",config.projectName)
log.info "替换数据:${BUILD_ENV},小程序APPID:${miniProgramAppId}"
sh "sed -i '' 's/\"appid\":.*/\"appid\": \"${miniProgramAppId}\",/g' ${WORKSPACE}/dist/build/mp-weixin/project.config.json"
}
stage('预览') {
if(BUILD_TYPE == 'preview'){
log.info "构建分支: ${BUILD_BRANCH}"
wechatBinPath = '/Applications/wechatwebdevtools.app/Contents/MacOS'
sh "${wechatBinPath}/cli preview --project ${WORKSPACE}/dist/build/mp-weixin -f image -o ${WORKSPACE}/${BUILD_ID}.jpg > ${WORKSPACE}/build.log"
errlog = sh(returnStdout: true, script: "grep -i error: ${WORKSPACE}/build.log || echo").trim()
log.info "errlog:${errlog}"
if ("${errlog}"==""){
log.info '执行成功'
sh "cp ${WORKSPACE}/${BUILD_ID}.jpg /Users/guoguo/workspace/nginx/image"
buildDesc = "代码分支: ${BUILD_BRANCH}<br>构建环境: ${BUILD_ENV}<br>构建类型: ${BUILD_TYPE}<br>请使用微信扫描以下二维码进行预览: <br><img src=\"http://图片服务器地址/${BUILD_ID}.jpg\" width=\"200\" height=\"200\">"
}else{
log.error '执行失败'
closeTool()
buildDesc = '微信开发者工具登录失效,请登录后再执行<br>登录工具:<a href=\"${JENKINS_URL}/job/WECHAT_LOGIN_TOOL\">'
sh "exit 1"
}
}else{
log.info '不是预览任务,跳过'
}
}
stage('上传') {
if(BUILD_TYPE == 'upload'){
if(BUILD_ENV == 'prod' && BUILD_BRANCH != 'master'){
log.error 'PROD环境只能允许master分支上传'
sh "exit 1"
}
log.info "构建分支: ${BUILD_BRANCH}"
wechatBinPath = '/Applications/wechatwebdevtools.app/Contents/MacOS'
now = getTimestamp()
uploadDesc = "CI 在 ${now} 提交上传"
sh "${wechatBinPath}/cli upload --project ${WORKSPACE}/dist/build/mp-weixin -v '${UPLOAD_VERSION}' -d '${uploadDesc}' > ${WORKSPACE}/upload.log"
errlog = sh(returnStdout: true, script: "grep -i error: ${WORKSPACE}/upload.log || echo").trim()
log.info "errlog:${errlog}"
if ("${errlog}"==""){
log.info '上传成功'
buildDesc = "代码分支: ${BUILD_BRANCH}<br>构建环境: ${BUILD_ENV}<br>构建类型: ${BUILD_TYPE}"
}else{
log.error '上传失败'
closeTool()
buildDesc = '微信开发者工具登录失效,请登录后再执行<br>登录工具:<a href=\"${JENKINS_URL}/job/登录工具JOB\">'
sh "exit 1"
}
}else{
log.info '不是上传任务,跳过'
}
}
stage('关闭工具'){
closeTool()
}
stage('通知') {
dingding.notice("${BUILD_BRANCH}")
}
currentBuild.description = "${buildDesc}"
}
}

def getMiniProgramAppId(buildEnv,projectEnglishName){
if(projectEnglishName == '应用A'){
if(buildEnv == 'dev'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'test'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'prod'){
miniProgramAppId = 'xxx'
}
}
if(projectEnglishName == '应用B'){
if(buildEnv == 'dev'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'test'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'prod'){
miniProgramAppId = 'xxx'
}
}
if(projectEnglishName == '应用C'){
if(buildEnv == 'dev'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'test'){
miniProgramAppId = 'xxx'
}else if(buildEnv == 'prod'){
miniProgramAppId = 'xxx'
}
}
return miniProgramAppId
}

def closeTool(){
log.info "关闭工具"
sh "${wechatBinPath}/cli quit"
}

def getTimestamp() {
return new Date().format('yyyy-MM-dd HH:mm:ss')
}

在Jenkins新建JOB,类型选择Pipeline,按照上面脚本注释内的“配置参数说明”,录入参数,并在最下面,Pipeline script填写如下内容,点击保存:

1
2
3
4
5
6
7
8
9
@Library('jenkins-shared-libraries@master') _

node {
script {
wechat_mini_program_build(
projectName:'你的项目名称'
)
}
}

打开任务执行,参数如下:

预览任务执行后的样子如下图,打包任务,基本一致,就是没有二维码而已:

小程序的自动化任务,都是需要有登录态的(上面的任务,如果登录失效,会提示,用登录工具先登录),这里登录的动作必须由人工完成,下面是登录工具的Pipeline,vars 下面新建wechat_mini_program_login_tool.groovy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def call() {
node('front-end') {
properties([
buildDiscarder(
logRotator(
daysToKeepStr: '30',
numToKeepStr: '50'
)
)
])
stage('登录') {
log.info "登录"
imagePath = '/你的图片Nginx目录'
wechatBinPath = '/Applications/wechatwebdevtools.app/Contents/MacOS'
sh "${wechatBinPath}/cli login -f image -o ${imagePath}/login-${BUILD_ID}.jpg > ${WORKSPACE}/run.log 2>&1 &"
log.info "请在60秒内打开链接并扫码(请使用专用微信): http://图片服务器地址/login-${BUILD_ID}.jpg"
sh 'sleep 60'
}
}
}

在Jenkins新建JOB,类型选择Pipeline,并在最下面,Pipeline script填写如下内容,点击保存:

1
2
3
4
5
6
7
@Library('jenkins-shared-libraries@master') _

node {
script {
wechat_mini_program_login_tool()
}
}

构建过程中,点击控制台打印的 http://图片服务器地址/login-${BUILD_ID}.jpg链接,进行扫描动作

总结

已经解决问题

  • 自动化编译
  • 自动化预览
  • 自动化上传代码

TODO

  • 自动从package.json读取版本号并做自动新增
  • 自动MR / 自动Tag

Reference

微信小程序命令行说明

微信关注我,及时接收最新技术文章