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:
- You can customize CLI tools and deploy them using commands
- Gitlab CI, automated deployment via Runner listening code submission
- 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
- 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”
- 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
- 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
- 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
- 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