前言
提示:本文的环境是腾讯云(CODING) + uniapp + 钉钉
自动化构建微信小程序,其实每个平台的方法是不一样的哈,我这里也只是记录下个人完成的情形,如果有人跟我的基础条件一样,那很幸运啦,我把坑都踩完了。
当然不一样,也没关系,可以借鉴或者保存下,毕竟万一以后工作上用到了呢?是吧
最终实现目标,腾讯云中点击构建,自动打包,编译,上传预览,生成二维码后,由机器人发到钉钉群里面。
一、官方文档
最好看下这两篇文档内容啊,虽然大家都知道,官方文档永远都是避重就轻,但一般比较详细,简单看看还是有必要的。
腾讯云自动化构建微信小程序这篇博客对接的是企业微信,还是比较详细的,但是我这里是钉钉
以下内容默认已阅读这两篇文档
二、脚本开发
1.安装miniprogram-ci
npm install miniprogram-ci --save
这是微信官方提供的,核心包,上传,预览都需要通过它执行。
2.编写上传,预览功能
const ci = require('miniprogram-ci') // 引入包
// 注意: new ci.Project 调用时,请确保项目代码已经是完整的,避免编译过程出现找不到文件的报错。
const project = new ci.Project({
appid: 'wxsomeappid', // appid
type: 'miniProgram', // 小程序类型,具体有几种看上面微信官方文档
projectPath: 'the/project/path', // 项目路径
privateKeyPath: 'the/privatekey/path', // 私钥,在获取项目属性和上传时用于鉴权使用,在微信公众平台上登录后下载
ignores: ['node_modules/**/*'], // 指定需要排除的规则
})
私钥获取方式,登录微信公众号平台找到管理>开发管理>开发设置-小程序代码上传-小程序代码上传密钥,点击生成即可获取
预览
const previewResult = await ci.preview({
project,
version: ‘1.0.0’, // 版本信息,这里非必填哈
desc: 'hello', // 此备注将显示在“小程序助手”开发版列表中
robot: 1, // 指定使用哪一个 ci 机器人,可选值:1 ~ 30
setting: {
es6: true,
minifyJS: true,
minifyWXML: true,
minifyWXSS: true,
minify: true
}, // #编译设置
qrcodeFormat: 'image', // 返回二维码文件的格式 "image" 或 "base64", 默认值 "terminal" 供调试用
qrcodeOutputDest: '/path/to/qrcode/file/destination.jpg', // 二维码文件保存路径
onProgressUpdate: console.log,
})
上传
const uploadResult = await ci.upload({
project,
version: '1.1.1',
desc: 'hello',
robot: 1,
setting: {
es6: true,
minifyJS: true,
minifyWXML: true,
minifyWXSS: true,
minify: true
},
onProgressUpdate: console.log,
})
走到这里其实核心的都完成了,是不是很简单,但是我这里还有很多没完成,因为我最终的目的是,点击构建,然后完成后自动有一个预览二维码推到钉钉群里。
3.钉钉群里面添加机器人
我们钉钉先建一个群,里面设置机器人
添加机器人选择自定义,设置关键字,当发送的消息有关键字时,机器人就会把消息发出来,最后添加完成会生成一个Webhook
例如
https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxx
最后对这个地址发送axios请求,就可以在群里把消息发送出来。
可能出现的问题
1,微信上传预览就失败了
这里注意看看要加ip白名单的,不然是上传不了的。
2,钉钉发送消息失败
这里注意下,钉钉发送图片不支持base64,必须要http地址哈,所以我这里其实还要加个把生成的预览图片上传到cos,换取到对应的http地址
全部脚本代码
const ci = require('miniprogram-ci')
const axios = require('axios')
const COS = require('cos-nodejs-sdk-v5')
const dayjs = require('dayjs')
const path = require('path')
const fs = require('fs')
const argv = require('minimist')(process.argv.slice(2))
const robot = argv.r || 1
const mpVersion = argv.v || '1.0.0'
const versionDesc = argv.d || ''
const uploaderWeChat = argv.w || 'false'
const appDirectory = fs.realpathSync(process.cwd()) // __dirname
const previewPath = path.resolve(appDirectory, `./xxx/xxx.jpg`) // 保存预览图片地址
const app_id = 'xxxxxxxxxxxx'
let client: any = null
let ossData: any = null
const uploadMp = async () => {
try {
const project = new ci.Project({
appid: app_id,
type: 'miniProgram',
projectPath: path.resolve(appDirectory, './xxx/xxx'), //项目路径
privateKeyPath: argv.p,
ignores: ['node_modules/**/*']
})
console.log('--------------开发版 preview 开始-------------')
const desc = `机器人 ${robot} 打包,版本:${mpVersion},备注:${versionDesc}`
await ci.preview({
project,
version: mpVersion,
desc,
qrcodeFormat: 'image',
qrcodeOutputDest: previewPath,
robot,
setting: {
es6: true,
minifyJS: true,
minifyWXML: true,
minifyWXSS: true,
minify: true
},
onProgressUpdate: console.log
})
console.log('--------------开发版 preview 结束-------------')
if (uploaderWeChat == 'true') {
console.log('--------------开始上传微信-------------')
await ci.upload({
project,
version: mpVersion,
desc,
robot,
setting: {
es6: true,
minifyJS: true,
minifyWXML: true,
minifyWXSS: true,
minify: true
},
onProgressUpdate: console.log
})
console.log('--------------上传微信完成-------------')
}
} catch (e) {
console.error('error =======> ', e)
process.exit(1) // 执行失败退出
}
}
// 垃圾钉钉 不支持base64发送消息
const sendQrCode = (codeUrl: any) => {
console.log('--------------发送钉钉消息-------------')
let context = `关键字关键字关键字xxxxx<font class="text-color-2" color="#FF7A00">机器人${robot}</font> 打包成功,版本:${mpVersion},`
context += `<font class="text-color-2" color="#FF7A00">备注:${versionDesc}</font> `
context += `请扫码查看!或者前往小程序助手查看! \n 👇👇👇👇👇👇 \n ![二维码](${codeUrl}) \n `
context += `<font class="text-color-2" color="#f44336">开发版二维码将于 ${dayjs(Date.now() + 60 * 24 * 1000).format('MM/DD HH:mm')} 时失效</font>`
return axios({
headers: { 'Content-Type': 'application/json' },
method: 'post',
url: 'https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxxx',
data: {
msgtype: 'markdown',
markdown: {
title: `哈喽~小程序构建成功`,
text: context
}
}
})
}
const initCOS = () => {
client = new COS({
SimpleUploadMethod: 'putObject',
getAuthorization: (options: any, callback: any) => {
const obj = {
TmpSecretId: ossData.data.oss_tmp_secret_id,
TmpSecretKey: ossData.data.oss_tmp_secret_key,
XCosSecurityToken: ossData.data.oss_session_token,
StartTime: ossData.data.oss_start_time,
// 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
ExpiredTime: ossData.data.oss_expired_time,
// 时间戳,单位秒,如:1580000900
ScopeLimit: true // 细粒度控制权限需要设为 true,会限制密钥只在相同请求时重复使用--如果是要单次使用,即每次上传都需要获取一次签名时,那么就需要改为false
}
callback(obj)
}
})
}
const getCosToken = () => {
return new Promise((resolve, reject) => {
const data = { src: 'xxx' }
axios
.post('https://xxx.xxx.com/xxx/xx', data)
.then((response: any) => {
// 请求成功的回调函数
ossData = response.data
initCOS()
resolve(response.data)
})
.catch((error: any) => {
// 请求失败的回调函数
console.log('Error', error)
reject(error)
})
})
}
// 上传二维码
const uploadImage = () => {
console.log('--------------上传二维码开始-------------')
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
try {
const imageData = fs.readFileSync(previewPath)
const base64Code = imageData.toString('base64')
const dataBuffer = Buffer.from(base64Code, 'base64') // base64 -> 二进制Buffer
const fileName = `xxx/xx.jpg`
const data = await client.putObject({
Bucket: ossData.data.oss_bucket, // 存储桶名称
Region: ossData.data.oss_region, // 存储桶所在地域
Key: ossData.data.oss_path + fileName, // 可以理解为图片存储的路径+名称(唯一) 例如:indexImages/1670050961361.png
Body: dataBuffer, // 上传文件的内容,可以为 FileStream、字符串、Buffer, 我们这里接收二进制Buffer
onProgress: (progressData: any) => {
console.log('progress: ', progressData)
}
})
resolve(`https://${data.Location}`)
console.log('--------------上传二维码结束-------------')
} catch (error) {
reject(error)
// console.log(error)
console.log('--------------上传二维码失败-------------')
}
})
}
;(async () => {
try {
// 初始化COS
await getCosToken()
// 上传至微信
await uploadMp()
// 二维码上传cos
const imageUrl = await uploadImage()
console.log('开发版二维码路径:', imageUrl)
// 发送消息
await sendQrCode(imageUrl)
console.log('执行成功 ------------------------------------')
process.exit(0) // 执行成功退出
} catch (e) {
console.error(e)
process.exit(1) // 执行失败退出
}
})()
运行脚本
sh 'ts-node autoUpload.ts -p ${identity} -r ${robot} -w ${uploaderWeChat} -v ${version} -d ${versionDesc}'
注意,不要那么想当然复制就能用,看看情况,有些参数可以写死,让本地先跑起来,我这里还有着腾讯构建计划里面设置的东西,配置的流程,这种公司私密东西就不放出来了,可以借鉴参考一下