hank 1704937006 1 1 år sedan
..
keys 9e2d0e2538 1. 去除隐私协议 1 år sedan
config.json 9e2d0e2538 1. 去除隐私协议 1 år sedan
img.png 6164dd671e 批量上传代码 1 år sedan
readme.md 1704937006 1 1 år sedan
upload.main.js 1704937006 1 1 år sedan
upload.wx.js 6164dd671e 批量上传代码 1 år sedan

readme.md

小程序批量化代码提交

版本号 功能 描述
1.0.0 批量化同步提交代码 批量化代码提交功能

快速开始:

// 单个小程序执行
npm run upload --  --t=2 --a=wx8bd40e8ce9deb2b6 --v=1.6.2 --d=测试
// 批量化执行
npm run upload --  --t=1  --v=1.6.2 --d=测试
  1. 复制批量化功能代码文件到项目目录destroy(没有则创建)

    1. 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;
      }
      
    2. 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;
      }
      }
      
    3. destroy/config.json 配置文件(版本号,版本描述,需要处理的小程序)

      {
      "version": "2.4.2",
      "desc": "2.4.2 优化一些问题",
      "env": "正式环境",
      "appIds":[
          {
              "name":"xxxxxx",
              "appid":"xxxxxx"
          }
      ]
      }
      
    4. destroy/keys/* 小程序密钥文件(小程序后台“开发与服务”-“开发管理”) img.png 需要下载小程序代码上传密钥,放到keys文件夹中,并且设置IP白名单(ip请联系研发产品)

    5. 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"}
    }
    

    注意事项:

  3. 本项目中使用到小程序APPID等信息的文件 project.config.json和config.js ,建议项目中减少其他文件直接写死appid。

  4. config.js文件中应用的当前appid的参数名改为“current_appid”

    // @config 确定使用的小程序appid
    const current_appid = 'xxxxxxxxxxxxxxx';