|
|
@@ -0,0 +1,248 @@
|
|
|
+# 小程序批量化代码提交
|
|
|
+
|
|
|
+| 版本号 | 功能 | 描述 |
|
|
|
+|-------|-----------|-----------|
|
|
|
+| 1.0.0 | 批量化同步提交代码 | 批量化代码提交功能 |
|
|
|
+
|
|
|
+## 快速开始:
|
|
|
+1. 复制批量化功能代码文件到项目目录destroy(没有则创建)
|
|
|
+ 2. destroy/upload.wx.js 单个小程序上传
|
|
|
+ ```
|
|
|
+ const ci = require('miniprogram-ci');
|
|
|
+ const fs = require('fs');
|
|
|
+ const path = require('path');
|
|
|
+
|
|
|
+ // 接收主进程传递过来的参数
|
|
|
+ process.on('message', async (data) => {
|
|
|
+ let {appid, name, version, desc, env, appindex} = data
|
|
|
+ await setProjectConfig(appid);
|
|
|
+ await setConfig(appid);
|
|
|
+
|
|
|
+ const project = new ci.Project({
|
|
|
+ appid: appid,
|
|
|
+ type: 'miniProgram',
|
|
|
+ projectPath: path.resolve(__dirname, '../'),
|
|
|
+ privateKeyPath: path.resolve(__dirname,
|
|
|
+ `../destroy/keys/private.${appid}.key`),
|
|
|
+ ignores: ['node_modules/**/*', 'destroy/**/*'],
|
|
|
+ });
|
|
|
+ // 上传
|
|
|
+ const uploadResult = await ci.upload({
|
|
|
+ project,
|
|
|
+ version: version,
|
|
|
+ desc: desc,
|
|
|
+ setting: {
|
|
|
+ es6: true,
|
|
|
+ minifyJS: true,
|
|
|
+ minifyWXML: true,
|
|
|
+ minifyWXSS: true,
|
|
|
+ minify: true
|
|
|
+ },
|
|
|
+ onProgressUpdate: (e) => {
|
|
|
+ // console.log('onProcessUpdate', e)
|
|
|
+ if (e._status == "done" && e._msg == "upload") {
|
|
|
+ console.log(`${appid}--${name}--${env} 上传完成`);
|
|
|
+ // config.appindex += 1;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ process.send('success');
|
|
|
+ process.exit();
|
|
|
+ });
|
|
|
+
|
|
|
+ // 设置 project.config.json
|
|
|
+ const setProjectConfig = async (appid) => {
|
|
|
+ // 读取和替换的文件路径
|
|
|
+ const project_config = '../project.config.json';
|
|
|
+ const promise = new Promise((resolve, reject) => {
|
|
|
+ // 读取 project.config.json
|
|
|
+ fs.readFile(
|
|
|
+ path.join(__dirname, project_config),
|
|
|
+ 'utf8',
|
|
|
+ (err, data) => {
|
|
|
+ if (err) throw err;
|
|
|
+ let json = JSON.parse(data);
|
|
|
+ // 替换appid
|
|
|
+ json.appid = appid;
|
|
|
+ // console.log('project.config.json:', json)
|
|
|
+
|
|
|
+ // 改写 project.config.json 中 appid
|
|
|
+ fs.writeFile(
|
|
|
+ path.join(__dirname, project_config),
|
|
|
+ JSON.stringify(json, null, 4),
|
|
|
+ (err) => {
|
|
|
+ if (err) throw err;
|
|
|
+ resolve();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ return promise;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置config.js
|
|
|
+ const setConfig = async (appid) => {
|
|
|
+ // 读取和替换的文件路径
|
|
|
+ const config_file = '../config/config.js';
|
|
|
+ const promise = new Promise((resolve, reject) => {
|
|
|
+ // 读取 config.js
|
|
|
+ fs.readFile(
|
|
|
+ path.join(__dirname, config_file),
|
|
|
+ 'utf8',
|
|
|
+ (err, data) => {
|
|
|
+ if (err) throw err;
|
|
|
+ let configjs = data;
|
|
|
+ const current_appid = appid;
|
|
|
+
|
|
|
+ // 替换文件中当前小程序的appid
|
|
|
+ const regex = new RegExp('(current_appid\\s*=\\s*["\']).*?(["\'])', 'g');
|
|
|
+ configjs = configjs.replace(regex, 'current_appid = \'' + current_appid + '\'');
|
|
|
+ // console.log(configjs)
|
|
|
+
|
|
|
+ // 改写 config.js 中 current_appid
|
|
|
+ fs.writeFile(
|
|
|
+ path.join(__dirname, config_file),
|
|
|
+ configjs,
|
|
|
+ (err) => {
|
|
|
+ if (err) throw err;
|
|
|
+ resolve();
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
+ return promise;
|
|
|
+ }
|
|
|
+ ```
|
|
|
+ 3. destroy/upload.main.js 批量处理逻辑
|
|
|
+ ```
|
|
|
+ const {fork} = require('child_process');
|
|
|
+ const fs = require('fs');
|
|
|
+ const path = require('path');
|
|
|
+
|
|
|
+ let config = {
|
|
|
+ appIds: [], // 小程序appid
|
|
|
+ version: "",//版本号
|
|
|
+ desc: "",//版本描述
|
|
|
+ env: '正式环境'
|
|
|
+ }
|
|
|
+
|
|
|
+ function publishMiniProgram(options) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const child = fork('./destroy/upload.wx.js');
|
|
|
+ // console.log('入参:',options);
|
|
|
+ child.send(options);
|
|
|
+ child.on('message', (msg) => {
|
|
|
+ if (msg.error) {
|
|
|
+ reject(new Error(msg.error));
|
|
|
+ } else {
|
|
|
+ resolve(msg);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ child.on('error', reject);
|
|
|
+ child.on('exit', (code) => {
|
|
|
+ if (code !== 0) {
|
|
|
+ reject(new Error(`child process exited with code ${code}`));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ exports.start = async () => {
|
|
|
+ try {
|
|
|
+ const file = await fs.promises.readFile(
|
|
|
+ path.join(__dirname, '../destroy/config.json'),
|
|
|
+ 'utf-8'
|
|
|
+ );
|
|
|
+ const fileJson = JSON.parse(file);
|
|
|
+ config = {
|
|
|
+ ...config,
|
|
|
+ appIds: fileJson.appIds,
|
|
|
+ version: fileJson.version,
|
|
|
+ desc: fileJson.desc,
|
|
|
+ env: fileJson.env || '正式环境',
|
|
|
+ };
|
|
|
+ console.log(`本次提交小程序: ${config.appIds.map(item => `${item.name} (${item.appid})`).join(', ')}`);
|
|
|
+ console.log(`版本: ${config.version}`);
|
|
|
+ console.log(`环境: ${config.env}`);
|
|
|
+ console.log(`描述: ${config.desc}`);
|
|
|
+ // 3. 顺序发布(避免并发资源竞争)
|
|
|
+ let successCount = 0;
|
|
|
+ let failedCount = 0;
|
|
|
+ for (const [index, item] of config.appIds.entries()) {
|
|
|
+ try {
|
|
|
+ console.log(`\n开始发布第 ${index + 1} 个小程序: ${item.name} (${item.appid})`);
|
|
|
+ await publishMiniProgram({
|
|
|
+ appid: item.appid,
|
|
|
+ name: item.name,
|
|
|
+ version: config.version,
|
|
|
+ desc: `${config.env}——${config.desc}`,
|
|
|
+ env: config.env
|
|
|
+ });
|
|
|
+ successCount++;
|
|
|
+ console.log(`✅ 发布成功: ${item.name}`);
|
|
|
+ } catch (err) {
|
|
|
+ failedCount++;
|
|
|
+ console.error(`❌ 发布失败: ${item.name}`, err.message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 4. 返回统计结果
|
|
|
+ return {
|
|
|
+ success: successCount,
|
|
|
+ failed: failedCount,
|
|
|
+ };
|
|
|
+ } catch (e) {
|
|
|
+ console.error('❌ 启动发布流程失败:', e.message);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+ 4. destroy/config.json 配置文件(版本号,版本描述,需要处理的小程序)
|
|
|
+ ```
|
|
|
+ {
|
|
|
+ "version": "2.4.2",
|
|
|
+ "desc": "2.4.2 优化一些问题",
|
|
|
+ "env": "正式环境",
|
|
|
+ "appIds":[
|
|
|
+ {
|
|
|
+ "name":"xxxxxx",
|
|
|
+ "appid":"xxxxxx"
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ ```
|
|
|
+ 5. destroy/keys/* 小程序密钥文件(小程序后台“开发与服务”-“开发管理”)
|
|
|
+ 
|
|
|
+ 需要下载小程序代码上传密钥,放到keys文件夹中,并且设置IP白名单(ip请联系研发产品)
|
|
|
+ 6. upload_ci.js 批量上传代码的命令脚本
|
|
|
+ ```
|
|
|
+ const path = require('path');
|
|
|
+ const upload = require(path.join(__dirname, 'destroy/upload.main.js'))
|
|
|
+ ;
|
|
|
+ (
|
|
|
+ async () => {
|
|
|
+ try {
|
|
|
+ const result = await upload.start();
|
|
|
+ console.log(`\n发布完成: 成功 ${result.success} 个, 失败 ${result.failed} 个`);
|
|
|
+ } catch (e) {
|
|
|
+ console.error('全局捕获错误:', e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )();
|
|
|
+ ```
|
|
|
+2. 其他配置项
|
|
|
+ ```
|
|
|
+ package.json (如果没有请新增)
|
|
|
+ {
|
|
|
+ "scripts": {"upload": "node upload_ci.js"}
|
|
|
+ }
|
|
|
+ ```
|
|
|
+## 注意事项:
|
|
|
+1. 本项目中使用到小程序APPID等信息的文件 project.config.json和config.js ,建议项目中减少其他文件直接写死appid。
|
|
|
+
|
|
|
+2. config.js文件中应用的当前appid的参数名改为“current_appid”
|
|
|
+ ```
|
|
|
+ // @config 确定使用的小程序appid
|
|
|
+ const current_appid = 'xxxxxxxxxxxxxxx';
|
|
|
+ ```
|