背景和阐明:
- 代码是同一份,依据不同业务场景,打包出不同渠道的小程序,小程序之间的区别是性能的删减、底部菜单首页、业务接口传参带上不同的渠道号
- 该我的项目应用的是 uniapp
- 本文章只波及打包微信小程序
遇到的问题:
- 提测之后和每次批改 bug,都须要打包给测试共事新包,而且不同渠道就要打不同的包,均匀打一次包就要破费 2min~3min,很浪费时间;打包过程占用本地机器的资源,导致电脑卡顿;打完包之后还要手动发包给测试,每个包还要重命名标识渠道。
- 每次提审小程序,都须要一次性打包多个渠道小程序,而后手动一个个小程序去上传,而后填写截然不同的提审信息,还要手动增加一些本渠道的标识信息,很容易出错。
问题剖析:
问题 1
- 其实能够通过写一个 shell 脚本,或者 node 脚本,实现打包、把指标文件夹变成 zip 包、重命名。
- 毛病是:①占用本地机器的资源,导致工作效率低下;②用脚本打包,须要填写参数,不不便且不直观。
- 更好的计划:应用 Jenkins,且能够让测试自行打包和下载包。
问题 2
- 其实能够用微信小程序 cli 来上传包,而后再用脚本来解决批量上传的问题。
- 毛病,除了问题 1 提到的毛病,还有:①cli 的门路,在每个开发者的电脑上是不统一的,而且还要思考是 windows 还是 mac 机器;②应用 cli 打包,第一次须要扫码,且还要思考过期的问题,还要思考个别开发者没有权限
- 更好的计划:应用 miniprogram-ci + Jenkins,免登录且自动化
布局 & 成果展现
问题 1,只呈现在测试环境下;问题 2,只呈现在生产环境下。所以要部署两套 Jenkins,而且对应的 Jenkinsfile 也辨别开
针对问题搭建的 Jenkins:test
针对问题搭建的 Jenkins:prod
配置和代码
针对问题 1,配置的 Jenkins
针对问题 1,测试环境应用的 Jenkinsfile
//Jenkinsfile
def isInChannels(String channel) {
Boolean result = false;
String channels = "${params.CHANNELS}";
String[] arr = channels.split(',');
for (String item in arr) {if (item == channel) {result = true}
}
return result;
}
pipeline {
agent any
parameters {gitParameter branchFilter: 'origin/(.*)', defaultValue: 'master', name: 'BRANCH', type: 'PT_BRANCH'
}
stages {stage('Checkout') {
steps {echo ">>>>>>>> Checkout branch ${params.BRANCH}"
git branch: "${params.BRANCH}", url: 'http://1*************.git', credentialsId: '***************'
}
}
stage('Install') {
when {
expression {
return params.INSTALL
return isInChannels('malllive')
}
}
steps {echo ">>>>>>>> Install ${params.INSTALL}..."
sh "cd ${WORKSPACE}"
sh "rm -rf node_modules/"
sh 'npm install'
}
}
stage('单店') {
when {
expression {return isInChannels('mall')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/mall"
echo ">>>>>>>> build"
sh "npm run test:wx:mall:build" // 打包
sh "mv dist/build/mp-weixin dist/build/mall"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/mall/*zip*/ 单店.zip'> 下载单店包 </a><br/>" // 提供下载包的链接,这个包曾经重命名好了,而且是 jenkins 自带能够打包成 zip 包
}
}
}
stage('单店带直播') {
when {
expression {return isInChannels('malllive')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/malllive"
echo ">>>>>>>> build"
sh "npm run test:wx:malllive:build"
sh "mv dist/build/mp-weixin dist/build/malllive"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/malllive/*zip*/ 单店带直播.zip'> 下载单店带直播包 </a><br/>"
}
}
}
stage('品牌') {
when {
expression {return isInChannels('brand')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/brand"
echo ">>>>>>>> build"
sh "npm run test:wx:brand:build"
sh "mv dist/build/mp-weixin dist/build/brand"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/brand/*zip*/ 品牌.zip'> 下载品牌包 </a><br/>"
}
}
}
stage('品牌不带预订') {
when {
expression {return isInChannels('brandnobook')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/brandnobook"
echo ">>>>>>>> build"
sh "npm run test:wx:brandnobook:build"
sh "mv dist/build/mp-weixin dist/build/brandnobook"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/brandnobook/*zip*/ 品牌不带预订.zip'> 下载品牌不带预订包 </a><br/>"
}
}
}
stage('尊享会') {
when {
expression {return isInChannels('group')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/group"
echo ">>>>>>>> build"
sh "npm run test:wx:group:build"
sh "mv dist/build/mp-weixin dist/build/group"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/group/*zip*/ 尊享会.zip'> 下载尊享会包 </a><br/>"
}
}
}
stage('预订') {
when {
expression {return isInChannels('bookmall')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/bookmall"
echo ">>>>>>>> build"
sh "npm run test:wx:bookmall:build"
sh "mv dist/build/mp-weixin dist/build/bookmall"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/bookmall/*zip*/ 预订.zip'> 下载预订包 </a><br/>"
}
}
}
stage('预订铂涛') {
when {
expression {return isInChannels('bookmallbtzh')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/bookmallbtzh"
echo ">>>>>>>> build"
sh "npm run test:wx:bookmall:build:bt"
sh "mv dist/build/mp-weixin dist/build/bookmallbtzh"
script {currentBuild.description = currentBuild.description + "<a href='http://**********/job/*************/${BUILD_NUMBER}/execution/node/3/ws/dist/build/bookmallbtzh/*zip*/ 预订铂涛.zip'> 下载预订铂涛包 </a><br/>"
}
}
}
}
}
针对问题 2,配置的 Jenkins
针对问题 2,生产环境应用的 Jenkinsfile,我这里的文件名叫做 Jenkinsfile.prod,跟 Jenkins 配置的 Script Path
须要统一
// Jenkinsfile.prod
def isInChannels(String channel) {
Boolean result = false;
String channels = "${params.CHANNELS}";
String[] arr = channels.split(',');
for (String item in arr) {if (item == channel) {result = true}
}
return result;
}
pipeline {
agent any
parameters {gitParameter branchFilter: 'origin/(.*)', defaultValue: 'master', name: 'BRANCH', type: 'PT_BRANCH'
}
stages {stage('Checkout') {
steps {echo ">>>>>>>> Checkout branch ${params.BRANCH}"
git branch: "${params.BRANCH}", url: 'http://******************.git', credentialsId: '******************'
}
}
stage('Install') {
when {
expression {return params.INSTALL}
}
steps {echo ">>>>>>>> Install ${params.INSTALL}..."
sh "cd ${WORKSPACE}"
sh "rm -rf node_modules/"
sh 'npm install'
}
}
stage('单店') {
when {
expression {return isInChannels('mall')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/mall"
echo ">>>>>>>> build"
sh "npm run build:wx:mall" // 打包点单的命令,上面的同理,打包对应的渠道包的命令
sh "mv dist/build/mp-weixin dist/build/mall"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(单店)${params.DESC} -i wx*************9560 -p dist/build/mall"
sh "wget https://************************" // 如果没有接入微信第三方平台,可疏忽。因为咱们接入了微信第三方平台,上传代码之后须要把草稿增加到模板,咱们这里做了一个接口主动增加草稿到模板。}
}
stage('单店带直播') {
when {
expression {return isInChannels('malllive')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/malllive"
echo ">>>>>>>> build"
sh "npm run build:wx:malllive"
sh "mv dist/build/mp-weixin dist/build/malllive"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(单店带直播)${params.DESC} -i wx*************9560 -p dist/build/malllive"
sh "wget https://*************"
}
}
stage('品牌') {
when {
expression {return isInChannels('brand')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/brand"
echo ">>>>>>>> build"
sh "npm run build:wx:brand"
sh "mv dist/build/mp-weixin dist/build/brand"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(品牌)${params.DESC} -i wx*************9560 -p dist/build/brand"
sh "wget https://*************"
}
}
stage('品牌不带预订') {
when {
expression {return isInChannels('brandnobook')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/brandnobook"
echo ">>>>>>>> build"
sh "npm run build:wx:brandnobook"
sh "mv dist/build/mp-weixin dist/build/brandnobook"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(品牌不带预订)${params.DESC} -i wx*************9560 -p dist/build/brandnobook"
sh "wget https://*************"
}
}
stage('尊享会') {
when {
expression {return isInChannels('group')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/group"
echo ">>>>>>>> build"
sh "npm run build:wx:group"
sh "mv dist/build/mp-weixin dist/build/group"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(尊享会)${params.DESC} -i wx*************9560 -p dist/build/group"
sh "wget https://*************"
}
}
stage('预订') {
when {
expression {return isInChannels('bookmall')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/bookmall"
echo ">>>>>>>> build"
sh "npm run test:wx:bookmall:build"
sh "mv dist/build/mp-weixin dist/build/bookmall"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(预订)${params.DESC} -i wx*************93a0 -p dist/build/bookmall"
}
}
stage('预订铂涛') {
when {
expression {return isInChannels('bookmallbtzh')
}
}
steps {
echo ">>>>>>>>>> Cleaning...."
sh "cd ${WORKSPACE}"
sh "rm -rf dist/build/bookmallbtzh"
echo ">>>>>>>> build"
sh "npm run test:wx:bookmall:build:bt"
sh "mv dist/build/mp-weixin dist/build/bookmallbtzh"
sh "node build/upload/index.js -v ${params.VER} -d(生产)(预订铂涛)${params.DESC} -i wx*************7974 -p dist/build/bookmallbtzh"
}
}
}
}
应用 miniprogram-ci 上传的脚本
// 应用 miniprogram-ci 上传
const program = require('commander');
const ci = require('miniprogram-ci');
program
.version('1.0.0')
.option('-i, --appid [APPID]', '模板小程序 appid')
.option('-p, --projectpath [PROJECTPATH]', '我的项目门路')
.option('-v, --ver [VER]', '小程序版本')
.option('-d, --desc [DESC]', '版本形容')
.parse(process.argv);
;(async () => {
const project = new ci.Project({appid: `${program.appid}`,
type: 'miniProgram',
projectPath: program.projectpath,
privateKeyPath: `build/upload/privatekeys/private.${program.appid}.key`,
ignores: ['node_modules/**/*'],
})
const uploadResult = await ci.upload({
project,
version: program.ver,
desc: program.desc,
setting: {
es6: true,
es7: true,
autoPrefixWXSS: true,
minify: true
}
})
console.log(uploadResult)
})()
补充阐明
- Jenkinsfile,是用 groovy 语法,如果想增加一些函数之类的,能够查一下对应的语法,我用到的
isInChannels
办法就是用的 groovy 语法 - Jenkins 默认不反对多选,须要装置
Extend Choice Parameter
插件