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系列(一)-- 如何配置扩展共享库

流程说明

Jenkins Pipeline 微信小程序 c63d3b7cb57b422d9a0e784fc60593bd.jpg
  • 编译
  • 预览(按需登录)
  • 上传(按需登录)

Pipeline实现

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

/**
配置参数说明:
--------------------------------------------------------------
参数名称: 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填写如下内容,点击保存:

@Library('jenkins-shared-libraries@master') _

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

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


20201126171008.jpg

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


20201126171901.jpg

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

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填写如下内容,点击保存:

@Library('jenkins-shared-libraries@master') _

node {
    script {
        wechat_mini_program_login_tool()
    }
}

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

总结

已经解决问题

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

TODO

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

Reference

微信小程序命令行说明

Jenkins Pipeline系列(一)—— 如何配置扩展共享库
Jenkins Pipeline系列(二)—— 使用扩展共享库构建Maven项目
Jenkins Pipeline系列(三)—— 使用扩展共享库构建微信小程序项目
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354

推荐阅读更多精彩内容