多端小程序打包发布脚本

avatar
@古茗科技

作者:汪奇超

一、背景与痛点

手动发布流程的重复性

多平台重复配置

微信、支付宝、抖音等小程序平台各有独立的后台系统,每次发布需分别登录、填写版本号、上传代码包、设置权限等,操作流程高度相似但无法复用

版本信息同步

需手动在多个平台更新版本号、更新日志,易出现不一致(如微信v1.2.0 vs 支付宝v1.1.9)

效率瓶颈与人力消耗

时间成本

据统计,手动发布三端小程序平均耗时30分钟以上

团队协作低效

依赖特定成员操作(如“只有某人会抖音发布”),形成单点故障风险

潜在风险与业务影响

人为错误率高

上传错误代码包

二、技术选型

核心工具依赖

三、自动化脚本设计

整体架构

├── scripts/
│   ├── build/       # 多端发布脚本
│   ├── config/      # 平台差异化配置
│   ├── notify.js    # 消息通知脚本
│   └── publish.js  # 主流程控制

关键模块实现

配置中心

  • 统一版本号同步

通过package.json中的version同步各端的版本号

  • 各端配置文件
{
  "ciConfig": {
    "ddNotifyToken": "", // 用于钉钉通知
    "channelList": ["weapp", "alipay", "tt"],
    "tt": {
      "email": "",
      "password": "",
      "appid": "",
      "projectPath": "./dist/tt",
    },
    "alipay": {
      "appid": "",
      "toolId": "",
      "privateKey": "",
      "projectPath": "./dist/alipay",
    },
    "weapp": {
      "appid": "",
      "projectPath": "./dist/weapp",
      "privateKeyPath": "./config/weapp.key",
    }
  }
}

上传逻辑

  • 微信
const ci = require("miniprogram-ci");
const project = new ci.Project({
    appid: ciConfig.appid,
    type: "miniProgram",
    projectPath: ciConfig.projectPath,
    privateKeyPath: ciConfig.privateKeyPath,
    ignores: ["node_modules/**/*"],
});

// 如果需要预览码 则需要进行preview
await ci.preview({
    project,
    desc,
    robot: 1,
    setting: {
        es6: true,
        minify: true,
        swc: true,
    },
    qrcodeFormat: "image",
    qrcodeOutputDest: "./weappPreview.jpg", // 将预览码进行通知
});

await ci.upload({
    project,
    version,// 本次更新版本
    desc,// 本次更新日志
    setting: {
        es6: true,
        minify: true,
        swc: true,
    },
});
  • 支付宝
import {minidev} from "minidev";
// 授权信息
await minidev.config.useRuntime({
    "alipay.authentication.privateKey": ciConfig.privateKey,
    "alipay.authentication.toolId": ciConfig.toolId,
});

// 获取预览码
const {qrcodeUrl} = await minidev.preview({
    appId: ciConfig.appid,
    project: ciConfig.projectPath,
});

// 例: 获取小程序所有上传版本
const allVersionList = await minidev.app.getUploadedVersionList({
    appId: ciConfig.appid,
});

// 删除版本
const deleteVersion = async (version) => {
    await minidev.app.deleteVersion({
        appId: ciConfig.appid,
        version,
    });
    console.log(`删除版本${version}`);
};
// 支付宝有开发版本数量上限 所以这里可以定义一个规则删除历史开发版本 可以减少人为操作

await minidev.upload({
    appId: ciConfig.appid,
    project: ciConfig.projectPath,
    version,// 本次更新版本
    experience: false,
});
  • 抖音

抖音可以直接把版本到审核完自动上线阶段。

为什么没有这么做?

- 保持三端统一性
- 为了保持可控性和稳定性,不希望自动发布上线
- 如果版本发布频繁,上一个版本未审核通过,下一个版本提交审核会导致发布失败
const tma = require("tt-ide-cli");
await tma.loginByEmail({
    email: ciConfig.email,
    password: ciConfig.password,
});
await tma.upload({
    project: {
        path: ciConfig.projectPath, // 项目地址
    },
    changeLog: desc, // 本次更新日志
    version, // 本次更新版本
    needUploadSourcemap: true, // 是否上传后生成 sourcemap,推荐使用 true,否则开发者后台解析错误时将不能展示原始代码
    qrcode: {
        format: "imageFile", // imageSVG | imageFile | null | terminal
        output: "./ttPreview.jpg", // 只在 imageFile 生效,填写图片输出绝对路径
        options: {
            small: false, // 使用小二维码,主要用于 terminal
        },
    },
});

通知

通过OSS将本地图片转换成网络图片以便于钉钉通知小程序预览码

const OSS = require("ali-oss");
const client = new OSS({
  region: "",
  accessKeyId: "",
  accessKeySecret: "",
  bucket: "",
});

const put = (ossFilePath, localFilePath) => {
  // 'object'填写上传至OSS的object名称,即不包括Bucket名称在内的Object的完整路径。
  // 'localfile'填写上传至OSS的本地文件完整路径。
  const fullPath = `https://*****.aliyuncs.com/${ossFilePath}`;
  return new Promise((res) => {
    client.put(ossFilePath, localFilePath).then(() => {
      res(fullPath);
    });
  });
};

调用钉钉API将发布信息以及预览二维码通知到钉钉中

export const notify = async (props) => {
  const {
    token,
    version,
    desc,
    type,
    typeStr,
    icon,
    publishUrl,
    name,
    previewUrl,
    packageTip
  } = props;

  const dev = `https://oapi.dingtalk.com/robot/send?access_token=${token}`;

  async function sendDDMsg(preview, resolve) {
    const da = {
      msgtype: "actionCard",
      actionCard: {
        title: "",
        text: `### **${name}** \n ![icon](${icon}) \n ### **${typeStr}小程序上传成功** \n  ### **版本号 -> ${version}** \n  ### **版本描述 -> ${
          desc || "-"
        }** \n ${packageTip} \n ### **验证二维码** \n![验证二维码](${preview})`,
        singleTitle: "点我去发布~",
        singleURL: `dingtalk://dingtalkclient/page/link?pc_slide=false&url=${encodeURIComponent(
          publishUrl
        )}`, // 各端小程序开放平台后台地址
      },
    };
    await http.post(dev, da);
    resolve();
  }

  function saveImg() {
    const date = new Date().getTime();
    return new Promise((resolve) => {
      if (previewUrl) {
        sendDDMsg(previewUrl, resolve);
        return previewUrl;
      } else {
        return put(
          `******/${type}Preview${date}.jpg`,
          `./${type}Preview.jpg`
        ).then((data) => {
          sendDDMsg(data, resolve);
          return data;
        });
      }
    });
  }

  await saveImg();
};

四、脚本执行

执行步骤

  • 拉取工程代码
  • 安装依赖包
  • 编译
  • 发布
  • 通知

注意事项

  • 不要清空发布文件夹

一般jenkins发布脚本一般会执行完成后清空发布文件夹以减少对磁盘的压力,但是前端项目依赖包比较庞大,所以每次安装会比较耗时。所以建议不删除依赖包,加快整体发布速度。

  • 发布机器性能足够的情况下,进行三端同步编译发布,提升效率(出现过机器降配导致发布中断)
stage('编译程序'){
    parallel {
        stage('支付宝') {
            steps {
                sh '''
                    echo "微信"
                    yarn build weapp
                    npm run kone-upload -- -c weapp
                '''
            }
        }
        stage('抖音') {
            steps {
                sh '''
                    echo "抖音"
                    yarn build tt
                    npm run kone-upload -- -c tt
                '''
            }
        }
        stage('支付宝') {
            steps {
                sh '''
                    echo "支付宝"
                    yarn build alipay
                    npm run kone-upload -- -c alipay 
                '''
            }
        }
    }
}

通过jenkins并发脚本进行三端并发打包发布,加快整体发布速度。

五、实现成果

  • 一键发布,无技术壁垒
  • 之前需要一名人员持续操作,现完全自动完成
  • 发布时间从30分钟以上降低到10分钟以内(支付宝的发布会相对久一点)
  • 多端版本号完全统一