background

In the previous development of wechat small program version, affected by the release of small program version of the following pain points:

  • The uploaded code is dependent on the developer tools, and the version information and description are manually filled in
  • The process of constructing – uploading – setting up the experience version – issuing two-dimensional code notification test is too tedious
  • When the developer is inconsistent, the need to go to the small program background frequently set the experience version, easy to cause errors

Just saw the front CI/CD thinking article, thinking about how to build small program automation deployment process based on wechat small program CI API.

The CI plug-in officially recommended by Taro is only the encapsulation of the official API of MiniProgram-CI and is not suitable for custom scenarios.

The target

Build an automated deployment process/tool, as shown below:

For this process, there are three ways to implement it:

  1. You can customize CLI tools and deploy them using commands
  2. Gitlab CI, automated deployment via Runner listening code submission
  3. Jenkins, can pull git repository specified branch, can be manually deployed

This paper adopts the first and second ways to complete the automatic deployment process of r&d.

implementation

  1. Wechat public account configuration

Refer to the official document, download the code, upload the key, put it in the root directory of the project, and set the corresponding IP whitelist in “wechat Public Platform – Development – Development Settings”

  1. Configure the nail group robot

Refer to the official document of Nail group, Nail group – Settings – Intelligent group Assistant – Add robot – customize to add robots, pay attention to save the Webhook address, use later

  1. Install several dependency packages in the wechat applets project
Yarn add miniprogram- CI node-fetch [email protected]Copy the code

Create wxci.config.js in the root directory

module.exports = {
  appid: ' '.// wechat small program appid
  privateKeyPath: ' '.// The key address of the first step
  qrcodeImageUrl:' '.// Wechat experience version of the TWO-DIMENSIONAL code picture network address, because the experience version of the picture address is fixed, it is recommended to transfer to the CDN, otherwise it cannot be displayed in Dingding
  dingTalkUrl:' '.// Step 2: webhook URL
}

Copy the code
  1. Create a script folder in the root directory and write the execution logic
// index.js
#!/usr/bin/env node
const path = require('path')
const { checkConfigFile, info } = require('./util')

async function main() {
  // 1. Read the configuration file and merge the configuration
  const filePath = 'wxci.config.js'
  checkConfigFile(filePath)
  const absConfigDir = process.cwd()
  info(`absConfigDir: ${absConfigDir}`)
  const config = require(path.resolve(`${absConfigDir}`, filePath))
  const baseConfig = require(path.resolve(__dirname, './defaultConfig.js'))
  constfullConfig = { ... baseConfig, ... config }const {
    appid,
    type,
    projectPath,
    privateKeyPath,
    version,
    desc,
    robot,
    qrcodeImageUrl,
    uploadImage,
    dingTalkUrl,
  } = fullConfig

  const WxCi = require('./wxCi')
  const DingCi = require('./dingCi')
  // 2. Upload/preview applets
  const wxCi = new WxCi({
    appid,
    type,
    projectPath,
    privateKeyPath,
    outDir,
    uploadImage,
    qrcodeImageUrl,
    version,
    desc,
    robot,
    setting,
    qrcodeFormat,
  })
  const weappQRImgUrl = await wxCi.run()
  // 3. Send a pin notification
  const dingCi = new DingCi({
    absConfigDir,
    weappQRImgUrl,
    dingTalkUrl,
    isExperience,
  })
  await dingCi.run()
}

main()

Copy the code
// wxCi.js
const path = require('path')
const fs = require('fs')
const ci = require('miniprogram-ci')
const { execSync } = require('child_process')
const { checkConfigFile, fail, info, success } = require('./util')

class WxCi {
  constructor(
    options = {
      appid: ' ',
      privateKeyPath: ' ',
      projectPath: ' ',
      qrcodeImageUrl: ' ',
      version: ' ',}) {
    this.options = options
    this.QRImgUrl = ' '
  }

  async run() {
    const {
      projectPath,
      privateKeyPath,
      appid,
      type,
      version,
      desc,
      robot,
      qrcodeImageUrl,
    } = this.options
    // Verify the key
    if (fs.existsSync()) {
      fail(`${privateKeyPath}The key file does not exist)
      process.exit(1)
    }
    info('Uploading... ')
    try {
      const project = new ci.Project({ appid, type, projectPath, privateKeyPath })
      info('Upload experience... ')
      if(! version){const branchName = execSync('git rev-parse --abbrev-ref HEAD', options).toString().trim()
      }
       git rev-parse --abbrev-ref HEAD
      await ci.upload({ project, version, desc, robot })
      // Wechat experience version address will not change, directly write dead
      this.QRImgUrl = qrcodeImageUrl
      success('Upload successful')}catch (error) {
      fail('Upload failed:${error}`)
      process.exit(1)}return this.QRImgUrl
  }
}

module.exports = WxCi

Copy the code
// Handle the pin message
const { execSync } = require('child_process')
const HOSTNAME = require('os').hostname()
const fetch = require('node-fetch')
const ora = require('ora')
const { info } = require('./util')

class DingCi {
  constructor(
    options = {
      weappQRImgUrl: ' ',
      dingTalkUrl: ' ',}) {
    this.options = options
    this.uploadType = 'Experience Edition'
    this.gitInfo = ' '
    this.template = ' '
  }

  /** * entry */
  async run() {
    // 1. Obtain the git branch and the latest commit record
    this.getGitInfo()
    // 2. Construct the message data structure
    this.buildTemplate()
    // 3. Push the pin message
    await this.sendDingTalk()
  }

  async sendDingTalk() {
    const { isExperience, dingTalkUrl } = this.options
    const postBody = {
      msgtype: 'markdown'.markdown: {
        title: 'Applet build tests completed'.text: this.template,
      },
      at: {
        isAtAll: isExperience,
      },
    }
    const spinner = ora({
      text: 'Pushing a nail message... \n`.spinner: 'moon',
    }).start()
    try {
      await fetch(dingTalkUrl, {
        method: 'POST'.headers: {
          'Content-Type': 'application/json',},body: JSON.stringify(postBody),
      })
      spinner.succeed('Push pin message succeeded \n')}catch (error) {
      console.error('Push pin message error', error)
    }
  }

  getGitInfo() {
    try {
      const options = { cwd : this.options.absConfigDir }
      const branchName = execSync('git rev-parse --abbrev-ref HEAD', options).toString().trim()
      this.gitInfo = '\n Current branch: **${branchName}**  \n`
    } catch (error) {
      console.error('Failed to get Git logs', error)
      this.gitInfo = ' '}}buildTemplate() {
    const { weappQRImgUrl, isExperience } = this.options
    const { uploadType, gitInfo } = this
    const wechatPart = weappQRImgUrl && ` # # WeChat${uploadType}:! [] (${weappQRImgUrl}) `
    this.template = ` #${uploadType}Applets build complete \n-- \n build machine:${HOSTNAME}  \n  ${gitInfo}  \n---\n${wechatPart || ' '}`}}module.exports = DingCi

Copy the code

Add scripts to package.json

"ci": "yarn && yarn run build && node ./script/index.js".Copy the code

Run the YARN CI test

  1. Gitlab configuration

Install Runner on a machine and start registration by referring to using a small program CI to automatically upload code

Create.gitlab-ci.yml in the project root directory

cache:
  key: modules
  paths:
    - npm_cache
    - node_modules/

stages: 
  - deploy

# Package projects
deploy_job:
  stage: deploy
  tags: # CI Runner hashtag
    - test-ci
  # only:
  # variables:
  # - $CI_COMMIT_MESSAGE =~ /^build/
  cache:
    key: modules
    paths:
      - node_modules/
  before_script:
    - node -v && npm -v
    - yarn global add @tarojs/cli
  script:
    - echo "Start building 🔥🔥🔥"
    # - npm ci
    - npm install --registry=https://registry.npm.taobao.org
    - npm run build
    - echo "Completed build 🔥🔥🔥"
    - echo "Start deployment 🚀🚀🚀"
    - npm run ci
    - echo "Deployment completed 🚀🚀🚀"

Copy the code

Modify the script

"ci": "node ./script/index.js".Copy the code

Submitting code to the specified branch, in this case the test-CI branch, is automatically deployed

At this point, the work of deploying the pipeline is basically complete 🎉🎉🎉

The next step is to create a separate script as a separate toolkit and refine the deployment solution with Jenkins.

Refer to the article

Publish taro applets using an taro-deploy automated build

Starless front end journey (4) — continuous integration of small programs

Automatically upload code using small program CI

Gitlab-ci automatic construction and deployment of front-end operation and maintenance