A small program automation construction platform

Time:2021-5-4

1 Preface

If you are maintaining multiple applet projects at the same time, do you spend a lot of time doing such a thing every day, switching git branch, performing compilation, opening applet developer tools, and uploading applet.

At the same time, I maintain 5 small programs (two WeChat applets, two Alipay applets, and one byte beating applet). I find that I spend a lot of time every day on publishing small programs. For this reason, I thought of building a Jenkins like platform for automated construction of small programs, and transferring the task of publishing small programs to my testing colleagues (yes, I’m so lazy).

It can be deployed to your server in 5 minutes. If you think it helps, click start.

2. Go to the project interface first

  • Click experience
  • Account number: MP
  • Password: 123456

2.1 landing page

A small program automation construction platform

2.2 home page

A small program automation construction platform

2.3 main page with remarks

A small program automation construction platform

2.4 release Preview

A small program automation construction platform

2.5 release experience version

A small program automation construction platform

3. Technical realization

The following is mainly about the implementation of the small program (WeChat applet, Alipay applet, byte beating applet), the other functions of login, preview and so on can be viewed in my GitHub project. The function is divided into three parts

  • Download GitHub / gitlab project
  • Compiling projects with child processes
  • Upload the compiled code

3.1 first write a configuration table to facilitate the subsequent expansion of other small programs

const ciConfigure = {
  //Identify the keys of different applets. The naming standard is' ${project name}_${ Applet type}`
  lzj_wechat: {
    //Applet appid
    appId: 'wxe10f1d56da44430f',
    //Application type, optional values are: miniprogram / miniprogram plug-in / minigame / minigame plug-in
    type: 'miniProgram',
    //The project download address is divided into three categories:
    //GitHub address:` https://github.com : ${user name, my user name is lizijie123} / ${code warehouse name, document code warehouse is uni MP study}`
    //V3 version gitlab address: '${gitlab address} / ${user name} / ${code warehouse name} / repository / archive.zip`
    //V4 version gitlab address: '${gitlab address} / API / V4 / projects / ${code warehouse ID} / repository / archive`
    //Tips: '${gitlab address} / API / V3 / projects' with a return value is the V3 version of gitlab, and' ${gitlab address} / API / V4 / projects' with a return value is the V4 version of gitlab, and the ID field in the returned data is the ID of the code warehouse
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study',
    //For the gitlab project, you need to set the gitlab's private token, which can be obtained in the gitlab personal center
    privateToken: '',
    //Applet package build command
    buildCommand: 'npm run build:wx',
    //After the small program is packaged and built, the relative position of the output directory and the root directory
    buildProjectChildrenPath: '/dist/build/mp-weixin',
    // WeChat small program and Alipay applet require asymmetric encryption of private key. PrivateKeyPath is the address of the private key file relative to the root directory, and is obtained from WeChat public platform.
    privateKeyPath: '/server/utils/CI/private/lzj-wechat.key',
    //It is the same as several settings in wechat applet developer tool
    setting: {
      es7: false,
      minify: false,
      autoPrefixWXSS: false,
    },
  },
  lzj_alipay: {
    // below, let's talk about Alipay's small program.
  },
  lzj_toutiao: {
    //The following is about how to add and improve the byte beating applet
  },
}

export default ciConfigure

3.1 get GitHub / gitlab project

Download git project adopts download git repo

#Install download git repo
npm i download-git-repo -S

First, encapsulate a function to calculate the project address and the path of the project stored locally

import ciConfigure from './utils/ci-configure'

//Get project address and local storage address
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ parmas branch: branch name
//@ params version: version number
//@ return: {projectpath: the path where the project is stored locally, storepath: the project address}
function getStorePathAndProjectPath (miniprogramType, branch, version) {
    let storePath = ''
    if (ciConfigure[miniprogramType].storeDownloadPath.includes('github')) {
      storePath = `${ciConfigure[miniprogramType].storeDownloadPath}#${branch}`
    } else {
      storePath = `direct:${ciConfigure[miniprogramType].storeDownloadPath}?private_token=${ciConfigure[miniprogramType].privateToken}`
      if (storePath.includes('v4')) {
        storePath += `&ref=${branch}`
      } else {
        storePath += `&sha=${branch}`
      }
    }
    const projectPath = path.join(process.cwd(), `/miniprogram/${miniprogramType}/${version}`)

    return {
      storePath,
      projectPath,
    }
  }

Then encapsulate a function to download the project

import * as downloadGit from 'download-git-repo'

//Download GitHub / gitlab project
//@ parmas storepath: project address
//@ params projectpath: the path where the project is stored locally
function download (storePath, projectPath) {
  return new Promise((resolve, reject) => {
    downloadGit(storePath, projectPath, null, err => {
      if (err) reject(err)
      resolve()
    })
  })
}

3.2 compiling projects with subprocesses

Using shelljs to simplify the child process_ Process) module operation

#Install shelljs
npm install shelljs -S

Encapsulates a function to execute shell commands

import * as shell from 'shelljs'

//Execute shell command
//@ parmas command: shell command to be executed
//@ params CWD: the execution directory of the shell command to be executed
function execPromise (command, cwd) {
  return new Promise(resolve => {
    shell.exec(command, {
      //If the value is true, a new child process will be started to execute shell commands. If the value is false, the current process will be used to execute shell commands, and the node process will be blocked
      async: true,
      silent: process.env.NODE_ENV === 'development',
      stdio: 'ignore',
      cwd,
    }, (...rest) => {
      resolve(...rest)
    })
  })
}

Encapsulate the function of the compiled project, which can be adjusted according to your own project

//Download the dependency package and execute the compile command
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ params projectpath: the path where the project is stored locally
async build (miniprogramType, projectPath) {
  //Download dependency package
  await execPromise(`npm install`, projectPath)
  await execPromise(`npm install --dev`, projectPath)
   //Execute compile command
  await execPromise(ciConfigure[miniprogramType].buildCommand, projectPath)
}

3.3 upload the compiled code (wechat applet version)

3.3.1 obtain asymmetric encryption private key for uploading code

Log in to the background of the applet, develop it, set it, and generate the secret key in the uploading of the applet code (this is where the privatekeypath field in the configuration file comes from)

3.3.2 continue to realize the function

Use miniprogram Ci to upload code

#Installation
npm install miniprogram-ci -S

Package code upload function

import * as ci from 'miniprogram-ci'

//Wechat applet upload code
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ params projectpath: the path where the project is stored locally
//@ params version: version number
//@ params projectdesc: description
//@ params identification: CI robot identification, this can not be transferred
async function upload ({ miniprogramType, projectPath, version, projectDesc = '', identification }) {
  const project = initProject(projectPath, miniprogramType)

  await ci.upload({
    project,
    version,
    desc: projectDesc,
    setting: ciConfigure[miniprogramType].setting,
    onProgressUpdate: process.env.NODE_ENV === 'development' ? console.log : () => {},
    robot: identification ? identification : null
  })
}

//Create CI projecr object
//@ params projectpath: the path where the project is stored locally
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
function initProject (projectPath, miniprogramType) {
  return new ci.Project({
    appid: ciConfigure[miniprogramType].appId,
    type: ciConfigure[miniprogramType].type,
    projectPath: `${projectPath}${ciConfigure[miniprogramType].buildProjectChildrenPath}`,
    privateKeyPath: path.join(process.cwd(), ciConfigure[miniprogramType].privateKeyPath),
    ignores: ['node_modules/**/*'],
  })
}

3.4 use the above encapsulated functions to complete the process

//Upload applet
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ params version: version number
//@ params branch: Branch
//@ params projectdesc: description
//@ params projectpath: the path where the project is stored locally
//@ params identification: CI robot logo for wechat applet
// @params experience: sets the current version to the experience version, Alipay applet
async upload ({ miniprogramType, version, branch, projectDesc, identification, experience }) {
  //Get project address and local storage address
  const { storePath, projectPath } = getStorePathAndProjectPath(miniprogramType, branch, version)
  //Download project to local
  download(storePath, projectPath)
  //Build project
  build(miniprogramType, projectPath)
  //Upload experience version
  await wechatCi.upload({
    miniprogramType,
    projectPath,
    version,
    projectDesc,
    identification,
    experience,
  })
}

4 other small program upload

4.1 Alipay applet

Use [Alipay dev] () to upload code

#Installation
npm install alipay-dev -S

4.1.1 obtain asymmetric encryption public key and private key for uploading code

#First, the public key and private key of asymmetric encryption are generated locally
npx alipaydev key create -w

4.1.2 sets the newly generated public key to the Alipay development tools secret key.

Set the development tool secret key, paste the public key to the development tool public key, and save to get the tool ID (put the tool ID and private key in the configuration file)

4.1.3 continue to realize the function

Perfecting the configuration file of Alipay applet

const ciConfigure = {
  lzj_ Wechat: {omitted},
  lzj_alipay: {
    //Ditto
    appId: '2021002107681948',
    // tool ID, the Alipay applet will be generated after the asymmetric encryption of the public key is set.
    toolId: 'b6465befb0a24cbe9b9cf49b4e3b8893',
    //Ditto
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study',
    //For gitlab project, you need to set the privatetoken of gitlab
    privateToken: '',
    //Ditto
    buildCommand: 'npm run build:ap',
    //Ditto
    buildProjectChildrenPath: '/dist/build/mp-alipay',
    //Ditto
    privateKeyPath: '/server/utils/CI/private/lzj-alipay.key',
  },
  lzj_ Toutiao: {omitted},
}

Then encapsulate Alipay applet upload code function.

//Upload experience version
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ params projectpath: the path where the project is stored locally
//@ params version: version number
//@ params experience: do you want to set this version as the experience version
async function upload ({ miniprogramType, projectPath, version, experience }) {
  initProject(miniprogramType)

  const res = await ci.miniUpload({
    project: `${projectPath}${ciConfigure[miniprogramType].buildProjectChildrenPath}`,
    appId: ciConfigure[miniprogramType].appId,
    packageVersion: version,
    onProgressUpdate: process.env.NODE_ENV === 'development' ? console.log : () => {},
    experience: experience ? experience : false,
  })
  if (res.qrCodeUrl) {
    return res.qrCodeUrl
  }
}

//Create CI projecr object
//@ params projectpath: the path where the project is stored locally
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
function initProject (projectPath: string, miniprogramType: string) {
  return new ci.Project({
    appid: ciConfigure[miniprogramType].appId,
    type: ciConfigure[miniprogramType].type,
    projectPath: `${projectPath}${ciConfigure[miniprogramType].buildProjectChildrenPath}`,
    privateKeyPath: path.join(process.cwd(), ciConfigure[miniprogramType].privateKeyPath),
    ignores: ['node_modules/**/*'],
  })
}

4.2 byte skipping applet

Improve the configuration of byte skipping applet

const ciConfigure = {
  lzj_ Wechat: {omitted},
  lzj_ Alipay: {omitted},
  lzj_toutiao: {
    //Byte skipping applet account (the one at login)
    account: '',
    //Byte skipping applet password (the one at login)
    password: '',
    //Ditto
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study',
    //Ditto
    privateToken: '',
    //Ditto
    buildCommand: 'npm run build:tt',
    //Ditto
    buildProjectChildrenPath: '/dist/build/mp-toutiao',
  },
}

Use TT ide cli to upload code

#Installation
npm install tt-ide-cli -S

Then encapsulate the function of uploading code by byte skipping applet. Note: at present, byte skipping applet can only upload code by command line

//Upload experience version
//@ params miniprogramtype: applet type, corresponding to the value of key in the configuration file
//@ params projectpath: the path where the project is stored locally
//@ params version: version number
//@ params projectdesc: description
async upload ({ miniprogramType, projectPath, version, projectDesc }) {
    const currentPath = process.cwd()
    //Login command
    const login = `npx tma login-e '${ciConfigure[miniprogramType].account}' '${ciConfigure[miniprogramType].password}'`
    //Upload command
    const up = `npx tma upload -v '${version}' -c '${projectDesc ?  Projectdesc: 'no description'} '${projectpath} ${ciconfigure [miniprogramtype]. Buildprojectchildrenpath}`
    await execPromise(login, currentPath)
    await execPromise(up, currentPath)
  }

5 communication

The project has been running in the build environment for a period of time, and no longer need to be called to publish small programs when half of the work is finished. The following is the project GitHub address. Welcome to clone. If you think it’s good, please click star.

Recommended Today

Looking for frustration 1.0

I believe you have a basic understanding of trust in yesterday’s article. Today we will give a complete introduction to trust. Why choose rust It’s a language that gives everyone the ability to build reliable and efficient software. You can’t write unsafe code here (unsafe block is not in the scope of discussion). Most of […]