1 introduction

😈 If you are maintaining multiple applet projects and you are spending a lot of time doing this every day, go to git branch -> Execute compile -> Open applet Developer Tools -> Upload Applet.

🧐 maintains 5 mini programs (two wechat mini programs, two Alipay mini programs, and one Bytedance mini program) at the same time, and I find that I spend a lot of time publishing mini programs every day. To that end, I came up with the idea of building an automated build platform for small programs like Jenkins, handing over the task of releasing small programs to test colleagues (yes, I’m lazy).

It can be deployed to your server in 5 minutes. Click Start if it helps.

2 Go to the project page

  • Click the experience
  • Account: mp
  • Password: 123456

2.1 the login page

2.2 the home page

2.3 Home Page With Remarks

2.4 Release Preview

2.5 Release the Experience version

3 Technical Implementation

The following focuses on the implementation of small program (wechat small program, Alipay small program, bytedance small program) publishing function, other login, preview and other functions can be viewed in my Github project. This function is divided into three parts, namely:

  • Download the Github/GitLab project
  • Compile the project using child processes
  • Upload the compiled code

3.1 First write a configuration table, convenient subsequent extension of other small programs

const ciConfigure = {
  ${project name}_${applets type}
  lzj_wechat: {
    // appID
    appId: 'wxe10f1d56da44430f'.// The application type can be: miniProgram /miniProgramPlugin /miniGame /miniGamePlugin
    type: 'miniProgram'.// There are three types of project download addresses:
    // Github address: 'https://github.com:${username, my username is lizijie123}/${repository name, document repository is uni-mp-study}'
    // v3 version gitlab address: '${gitlab address}/${user name}/${repository name}/repository/archive.zip'
    // ${gitlab/API /v4/projects/${repository id}/repository/archive '
    // tips: '${gitlab project}/ API /v4/projects' return ${gitlab project}/ API /v4/projects' return ${gitlab project}/ API /v4/projects' return ${gitlab project}/ API /v4/projects' return ${gitlab project}/ API /v4/projects' return ${gitlab project
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study'.// for the gitlab project, you need to set the privateToken of gitlab, which can be obtained in the gitlab personal center
    privateToken: ' '.// Applets pack build commands
    buildCommand: 'npm run build:wx'.// The small program is packaged and built, and the output directory is relative to the root directory
    buildProjectChildrenPath: '/dist/build/mp-weixin'.// wechat applet and Alipay applet need asymmetric encryption of the private key, privateKeyPath is the private key file relative to the root directory address, get in the wechat public platform
    privateKeyPath: '/server/utils/CI/private/lzj-wechat.key'.// The same as several Settings in the wechat Small program developer tool
    setting: {
      es7: false.minify: false.autoPrefixWXSS: false,}},lzj_alipay: {
    // Below is the alipay small program to complement and improve
  },
  lzj_toutiao: {
    // Let's talk about bytedance}},export default ciConfigure
Copy the code

3.1 Obtaining the Github/GitLab Project

Download git projects using download-git-repo

#To install the download git -- repo
npm i download-git-repo -S
Copy the code

First encapsulates a function to calculate the project address, with the local path where the project is stored

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

// Get the project address and local storage address
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @parmas branch: branch name
// @params version: version number
// @return: {projectPath: local path where the project is stored, storePath: 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,
    }
  }
Copy the code

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 local path where the project is stored
function download (storePath, projectPath) {
  return new Promise((resolve, reject) = > {
    downloadGit(storePath, projectPath, null.err= > {
      if (err) reject(err)
      resolve()
    })
  })
}
Copy the code

3.2 Compile the project using child processes

Use ShellJS to simplify the operation of the child_process module

#Install shelljs
npm install shelljs -S
Copy the code

Encapsulates a function to execute a shell command

import * as shell from 'shelljs'

// Run the shell command
// @parmas command: shell command to be executed
// @params CWD: directory for executing the shell command
function execPromise (command, cwd) {
  return new Promise(resolve= > {
    shell.exec(command, {
      // If the value is true, a new child process is enabled to execute shell commands. If the value is false, the current process is used to execute shell commands, blocking the node process
      async: true.silent: process.env.NODE_ENV === 'development'.stdio: 'ignore',
      cwd,
    }, (. rest) = >{ resolve(... rest) }) }) }Copy the code

Functions that encapsulate the compiled project, which can be customized for your project

// Download the dependency package and run the compile command
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @params projectPath: the local path where the project is stored
async build (miniprogramType, projectPath) {
  // Download the dependency package
  await execPromise(`npm install`, projectPath)
  await execPromise(`npm install --dev`, projectPath)
   // Execute the compile command
  await execPromise(ciConfigure[miniprogramType].buildCommand, projectPath)
}
Copy the code

3.3 Upload the compiled code (wechat small program version)

3.3.1 Obtaining the Asymmetric Encryption private key for uploading the code

Log in to applet background -> development -> Development Settings -> applet code upload to generate the secret key (the privateKeyPath field in the configuration file is here)

3.3.2 Continuing to implement functions

Use miniprogram-ci to upload the code

#The installation
npm install miniprogram-ci -S
Copy the code

Encapsulate the code upload function

import * as ci from 'miniprogram-ci'

// Wechat small program upload code
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @params projectPath: the local path where the project is stored
// @params version: version number
// @params projectDesc: description
// @params id: id of the robot
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 a CI Projecr object
// @params projectPath: the local path where the project is stored
// @params miniprogramType: Small program type, corresponding to the key value 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/**/*'],})}Copy the code

3.4 Use the above encapsulated functions to do a complete process

// Upload the applet
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @params version: version number
// @params branch
// @params projectDesc: description
// @params projectPath: the local path where the project is stored
// @params identification: CI robot identification, for wechat applet
// @params Experience: Whether to set the current version to experience version, alipay small program
async upload ({ miniprogramType, version, branch, projectDesc, identification, experience }) {
  // Get the project address and local storage address
  const { storePath, projectPath } = getStorePathAndProjectPath(miniprogramType, branch, version)
  // Download the project locally
  download(storePath, projectPath)
  // Build the project
  build(miniprogramType, projectPath)
  // Upload the experience version
  await wechatCi.upload({
    miniprogramType,
    projectPath,
    version,
    projectDesc,
    identification,
    experience,
  })
}
Copy the code

4 upload other small programs

4.1 Alipay applet

Use Alipay -dev to upload the code

#The installation
npm install alipay-dev -S
Copy the code

4.1.1 Obtain asymmetric encryption public and private keys for uploading code

#The public and private keys for asymmetric encryption are generated locally
npx alipaydev key create -w
Copy the code

4.1.2 Set the newly generated public key to the secret key of Alipay development tool

Set the development tool secret key -> paste the public key to the development tool public key -> save to get the toolId (toolId)(place the toolId and private key obtained here in the configuration file)

4.1.3 Continue to implement functions

Perfect the configuration file of Alipay applet

const ciConfigure = {
  lzj_wechat: { 省略 },
  lzj_alipay: {
    / / same as above
    appId: '2021002107681948'.// The id of the tool will be generated when the public key of asymmetric encryption is set by alipay appletool
    toolId: 'b6465befb0a24cbe9b9cf49b4e3b8893'./ / same as above
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study'.// For the gitlab project, you need to set the Gitlab privateToken
    privateToken: ' './ / same as above
    buildCommand: 'npm run build:ap'./ / same as above
    buildProjectChildrenPath: '/dist/build/mp-alipay'./ / same as above
    privateKeyPath: '/server/utils/CI/private/lzj-alipay.key',},lzj_toutiao: {omit},}Copy the code

Then encapsulate pay treasure small program upload code function

// Upload the experience version
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @params projectPath: the local path where the project is stored
// @params version: version number
// @params Experience: whether to set this version to 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 a CI Projecr object
// @params projectPath: the local path where the project is stored
// @params miniprogramType: Small program type, corresponding to the key value 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/**/*'],})}Copy the code

4.2 bytedance small program

Improve bytedance applets configuration

const ciConfigure = {
  lzj_wechat: { 省略 },
  lzj_alipay: { 省略 },
  lzj_toutiao: {
    // Bytedance applet account (the one you logged in to)
    account: ' '.// Bytedance applet password (the one used when logging in)
    password: ' './ / same as above
    storeDownloadPath: 'https://github.com:lizijie123/uni-mp-study'./ / same as above
    privateToken: ' './ / same as above
    buildCommand: 'npm run build:tt'./ / same as above
    buildProjectChildrenPath: '/dist/build/mp-toutiao',}}Copy the code

Use tt-IDE-CLI to upload the code

#The installation
npm install tt-ide-cli -S
Copy the code

Next, encapsulate bytedance applet upload code function. Note: Bytedance applet can only upload code using the command line

// Upload the experience version
// @params miniprogramType: Small program type, corresponding to the key value in the configuration file
// @params projectPath: the local path where the project is stored
// @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 the command
    const up = `npx tma upload -v '${version}' -c '${projectDesc ? projectDesc : 'No description yet'}' ${projectPath}${ciConfigure[miniprogramType].buildProjectChildrenPath}`
    await execPromise(login, currentPath)
    await execPromise(up, currentPath)
  }
Copy the code

5 communicate

The project has been running in a build environment for some time now, and no longer need to be called in the middle of the work to release a small program.