小程序Jenkins部署實踐

背景和說明:
  1. 代碼是同一份,根據不一樣業務場景,打包出不一樣渠道的小程序,小程序之間的區別是功能的刪減、底部菜單首頁、業務接口傳參帶上不一樣的渠道號
  2. 該項目使用的是uniapp
  3. 本文章只涉及打包微信小程序
遇到的問題:
  1. 提測以後和每次修改bug,都須要打包給測試同事新包,並且不一樣渠道就要打不一樣的包,平均打一次包就要花費2min~3min,很浪費時間;打包過程佔用本地機器的資源,致使電腦卡頓;打完包以後還要手動發包給測試,每一個包還要重命名標識渠道。
  2. 每次提審小程序,都須要一次性打包多個渠道小程序,而後手動一個個小程序去上傳,而後填寫如出一轍的提審信息,還要手動添加一些本渠道的標識信息,很容易出錯。
問題分析:

問題1javascript

  1. 其實能夠經過寫一個shell腳本,或者node腳本,實現打包、把目標文件夾變成zip包、重命名。
  2. 缺點是:①佔用本地機器的資源,致使工做效率低下;②用腳本打包,須要填寫參數,不方便且不直觀。
  3. 更好的方案:使用Jenkins,且可讓測試自行打包和下載包。

問題2html

  1. 其實能夠用微信小程序cli來上傳包,而後再用腳原本解決批量上傳的問題。
  2. 缺點,除了問題1提到的缺點,還有:①cli的路徑,在每一個開發者的電腦上是不一致的,並且還要考慮是windows仍是mac機器;②使用cli打包,第一次須要掃碼,且還要考慮過時的問題,還要考慮個別開發者沒有權限
  3. 更好的方案:使用miniprogram-ci + Jenkins,免登陸且自動化
規劃&效果展現

問題1,只出如今測試環境下;問題2,只出如今生產環境下。因此要部署兩套Jenkins,並且對應的Jenkinsfile也區分開java

針對問題搭建的Jenkins:test
image
imagenode

針對問題搭建的Jenkins:prod
image
imagegit

配置和代碼

針對問題1,配置的Jenkins
image
針對問題1,測試環境使用的Jenkinsfilees6

//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
imageshell

針對問題2,生產環境使用的Jenkinsfile,我這裏的文件名叫作Jenkinsfile.prod,跟Jenkins配置的Script Path須要一致express

// 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上傳的腳本npm

// 使用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)
})()
補充說明
  1. Jenkinsfile,是用groovy語法,若是想添加一些函數之類的,能夠查一下對應的語法,我用到的isInChannels方法就是用的groovy語法
  2. Jenkins默認不支持多選,須要安裝Extend Choice Parameter插件
相關文章
相關標籤/搜索