uni-miniapp-ci 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 dinghui
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # uni-miniapp-ci
2
+
3
+ > 多平台小程序 CI/CD 上传工具,支持微信、支付宝、抖音、百度智能小程序。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/uni-miniapp-ci)](https://www.npmjs.com/package/uni-miniapp-ci)
6
+ [![license](https://img.shields.io/npm/l/uni-miniapp-ci)](./LICENSE)
7
+ [![node](https://img.shields.io/node/v/uni-miniapp-ci)](https://nodejs.org)
8
+
9
+ 轻量级 CLI 工具与 Node.js 库,用于将小程序上传到各平台开发者后台,可无缝集成到任意 CI/CD 流程中。
10
+
11
+ ## 支持平台
12
+
13
+ | 平台 | 上传 | 预览 | SDK |
14
+ |---|---|---|---|
15
+ | 微信小程序 | ✅ | ✅ | `miniprogram-ci` |
16
+ | 支付宝小程序 | ✅ | ✅ | `minidev` |
17
+ | 抖音小程序 | ✅ | ✅ | `tt-ide-cli` |
18
+ | 百度智能小程序 | ✅ | ✅ | `swan-toolkit` |
19
+
20
+ ## 安装
21
+
22
+ 安装 `uni-miniapp-ci` 时,各平台 CI SDK(`miniprogram-ci`、`minidev`、`tt-ide-cli`、`swan-toolkit`)会**自动一并安装**,无需额外操作。
23
+
24
+ 各平台 SDK 会随小版本/补丁版本自动更新,如需获取最新版本,执行:
25
+
26
+ ```bash
27
+ npm update uni-miniapp-ci
28
+ ```
29
+
30
+ ### 方式一:全局安装(推荐用于手动执行)
31
+
32
+ ```bash
33
+ npm install -g uni-miniapp-ci
34
+ ```
35
+
36
+ 安装后可在任意目录直接使用 `miniapp-ci` 命令:
37
+
38
+ ```bash
39
+ miniapp-ci upload --platform weixin
40
+ miniapp-ci preview --platform weixin
41
+ ```
42
+
43
+ ### 方式二:项目内安装(推荐用于 CI/CD 集成)
44
+
45
+ ```bash
46
+ npm install -D uni-miniapp-ci
47
+ ```
48
+
49
+ 在 `package.json` 中配置 scripts,通过 `npm run` 调用:
50
+
51
+ ```json
52
+ {
53
+ "scripts": {
54
+ "upload:weixin": "miniapp-ci upload --platform weixin",
55
+ "upload:alipay": "miniapp-ci upload --platform alipay",
56
+ "upload:douyin": "miniapp-ci upload --platform douyin",
57
+ "upload:baidu": "miniapp-ci upload --platform baidu",
58
+ "preview:weixin": "miniapp-ci preview --platform weixin --output ./preview.jpg",
59
+ "preview:alipay": "miniapp-ci preview --platform alipay --output ./preview.jpg",
60
+ "preview:douyin": "miniapp-ci preview --platform douyin --output ./preview.jpg",
61
+ "preview:baidu": "miniapp-ci preview --platform baidu --output ./preview.jpg"
62
+ }
63
+ }
64
+ ```
65
+
66
+ 然后执行:
67
+
68
+ ```bash
69
+ npm run upload:weixin
70
+ npm run preview:weixin
71
+ ```
72
+
73
+ > 项目内安装不依赖全局环境,适合团队统一版本、在 CI 服务器上使用。
74
+
75
+ ## 快速开始
76
+
77
+ **1. 在项目根目录创建 `.minicirc`**(包含密钥,切勿提交到 git):
78
+
79
+ ```json
80
+ {
81
+ "version": "1.0.0",
82
+ "desc": "发布说明",
83
+
84
+ "weixin": {
85
+ "appid": "wx__YOUR_APPID__",
86
+ "privateKeyPath": "./private.wx.key",
87
+ "projectPath": "./dist"
88
+ }
89
+ }
90
+ ```
91
+
92
+ 完整配置示例见 [`.minicirc.example`](./.minicirc.example)。
93
+
94
+ **2. 上传:**
95
+
96
+ ```bash
97
+ # 全局安装方式
98
+ miniapp-ci upload --platform weixin
99
+
100
+ # 项目内安装方式(package.json 已配置 scripts)
101
+ npm run upload:weixin
102
+ ```
103
+
104
+ **3. 生成预览二维码:**
105
+
106
+ ```bash
107
+ # 全局安装方式
108
+ miniapp-ci preview --platform weixin --output ./qr.jpg
109
+
110
+ # 项目内安装方式
111
+ npm run preview:weixin
112
+ ```
113
+
114
+ ## CLI 参数说明
115
+
116
+ ```
117
+ miniapp-ci upload -p <platform> [选项]
118
+ miniapp-ci preview -p <platform> [选项]
119
+
120
+ 选项:
121
+ -p, --platform <platform> 平台名称:weixin | alipay | douyin | baidu(必填)
122
+ -v, --version <version> 覆盖 .minicirc 中的版本号
123
+ -d, --desc <desc> 上传备注 / 发布说明
124
+ -o, --output <path> 预览二维码保存路径(仅 preview,默认:./preview.jpg)
125
+ --cwd <path> 工作目录(默认:当前目录)
126
+ -h, --help 显示帮助
127
+ --version 显示版本号
128
+ ```
129
+
130
+ ## Node.js API(编程式调用)
131
+
132
+ ```ts
133
+ import { weixin, alipay, douyin, baidu, readConfig } from 'uni-miniapp-ci'
134
+
135
+ const config = readConfig() // 读取当前目录下的 .minicirc
136
+
137
+ // 上传
138
+ await weixin.upload(config.weixin!, { version: '1.2.0', desc: '功能更新' })
139
+ await alipay.upload(config.alipay!, { version: '1.2.0', desc: '功能更新' })
140
+ await douyin.upload(config.douyin!, { version: '1.2.0', desc: '功能更新' })
141
+ await baidu.upload(config.baidu!, { version: '1.2.0', desc: '功能更新' })
142
+
143
+ // 预览
144
+ await weixin.preview(config.weixin!, { version: '1.2.0', desc: '预览', outputPath: './qr.jpg' })
145
+ await alipay.preview(config.alipay!, { version: '1.2.0', desc: '预览', outputPath: './qr.jpg' })
146
+ await douyin.preview(config.douyin!, { version: '1.2.0', desc: '预览', outputPath: './qr.jpg' })
147
+ await baidu.preview(config.baidu!, { version: '1.2.0', desc: '预览', outputPath: './qr.jpg' })
148
+ ```
149
+
150
+ ## `.minicirc` 配置说明
151
+
152
+ ```jsonc
153
+ {
154
+ "version": "1.0.0", // 必填,默认上传版本号
155
+ "desc": "发布说明", // 可选,默认上传备注
156
+
157
+ // 微信小程序 — SDK: miniprogram-ci
158
+ // privateKeyPath: 从微信公众平台下载的 .key 文件
159
+ "weixin": {
160
+ "appid": "wx...",
161
+ "privateKeyPath": "./private.wx.key",
162
+ "projectPath": "./dist/weixin",
163
+ "robot": 1, // 可选,CI 机器人编号 1-30
164
+ "setting": { // 可选,编译设置
165
+ "minify": true,
166
+ "uploadWithSourceMap": false
167
+ },
168
+ "pagePath": "pages/index/index", // 可选,预览入口页
169
+ "searchQuery": "a=1&b=2" // 可选,预览页参数
170
+ },
171
+
172
+ // 支付宝小程序 — SDK: minidev
173
+ // 鉴权方式二选一:identityKeyPath(密钥文件) 或 toolId + privateKey(适合 CI)
174
+ "alipay": {
175
+ "appId": "...",
176
+ "identityKeyPath": "./alipay-identity.json",
177
+ "projectPath": "./dist/alipay",
178
+ "clientType": "alipay", // 可选,默认 alipay
179
+ "experience": false // 可选,上传后自动设为体验版
180
+ },
181
+
182
+ // 抖音小程序 — SDK: tt-ide-cli
183
+ // token: 在抖音开放平台 → 开发管理 → CI Token 处获取
184
+ "douyin": {
185
+ "appid": "tt...",
186
+ "token": "...",
187
+ "projectPath": "./dist/douyin",
188
+ "channel": "1", // 可选,测试通道
189
+ "pagePath": "pages/index/index", // 可选,预览入口页
190
+ "pageQuery": "name=foo" // 可选,预览页参数
191
+ },
192
+
193
+ // 百度智能小程序 — SDK: swan-toolkit(>= 3.22.1)
194
+ // token: 在 https://smartprogram.baidu.com/mappconsole/api/secret 获取
195
+ "baidu": {
196
+ "token": "...",
197
+ "projectPath": "./dist/baidu",
198
+ "minSwanVersion": "3.150.14", // 必填,最低基础库版本
199
+ "sourcemap": false // 可选,自动上传 sourcemap
200
+ }
201
+ }
202
+ ```
203
+
204
+ ## CI/CD 集成示例
205
+
206
+ ### GitHub Actions
207
+
208
+ ```yaml
209
+ name: 上传小程序
210
+
211
+ on:
212
+ push:
213
+ tags: ['v*']
214
+
215
+ jobs:
216
+ upload:
217
+ runs-on: ubuntu-latest
218
+ steps:
219
+ - uses: actions/checkout@v4
220
+
221
+ - uses: actions/setup-node@v4
222
+ with:
223
+ node-version: 20
224
+
225
+ - run: npm ci
226
+
227
+ - name: 写入 .minicirc
228
+ run: echo '${{ secrets.MINIRC }}' > .minicirc
229
+
230
+ - name: 上传微信小程序
231
+ run: npm run upload:weixin
232
+
233
+ - name: 上传支付宝小程序
234
+ run: npm run upload:alipay
235
+ ```
236
+
237
+ 将完整的 `.minicirc` JSON(包含所有密钥)存入仓库 Secret,命名为 `MINIRC`。
238
+
239
+ ### 版本号自动从 package.json 读取示例
240
+
241
+ ```json
242
+ {
243
+ "scripts": {
244
+ "upload:weixin": "miniapp-ci upload --platform weixin --version $(node -p \"require('./package.json').version\")"
245
+ }
246
+ }
247
+ ```
248
+
249
+ ## 贡献
250
+
251
+ 欢迎提交 Pull Request。请先开 Issue 讨论你想做的改动。
252
+
253
+ ## 许可证
254
+
255
+ [MIT](./LICENSE)
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/platforms/alipay.ts
4
+ import fs from "fs";
5
+ import https from "https";
6
+ import http from "http";
7
+ import path from "path";
8
+ async function loadMinidev() {
9
+ try {
10
+ const mod = await import("minidev");
11
+ const minidev = mod.minidev ?? mod.default ?? mod;
12
+ return { minidev };
13
+ } catch (err) {
14
+ const notFound = err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND";
15
+ if (notFound) {
16
+ throw new Error("minidev \u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5\uFF1Anpm install mp-ci");
17
+ }
18
+ throw err;
19
+ }
20
+ }
21
+ async function applyAuth(minidev, config) {
22
+ if (config.toolId && config.privateKey) {
23
+ await minidev.config.useRuntime({
24
+ "alipay.authentication.privateKey": config.privateKey,
25
+ "alipay.authentication.toolId": config.toolId
26
+ });
27
+ }
28
+ }
29
+ function buildBaseParams(config, cwd) {
30
+ const params = {
31
+ appId: config.appId,
32
+ project: path.resolve(cwd, config.projectPath)
33
+ };
34
+ if (config.identityKeyPath) {
35
+ params.identityKeyPath = path.resolve(cwd, config.identityKeyPath);
36
+ }
37
+ if (config.clientType) {
38
+ params.clientType = config.clientType;
39
+ }
40
+ return params;
41
+ }
42
+ function downloadFile(url, dest) {
43
+ return new Promise((resolve, reject) => {
44
+ const file = fs.createWriteStream(dest);
45
+ const protocol = url.startsWith("https") ? https : http;
46
+ protocol.get(url, (res) => {
47
+ res.pipe(file);
48
+ file.on("finish", () => file.close(() => resolve()));
49
+ }).on("error", (err) => {
50
+ fs.unlink(dest, () => {
51
+ });
52
+ reject(err);
53
+ });
54
+ });
55
+ }
56
+ async function upload(config, options, cwd = process.cwd()) {
57
+ const { minidev } = await loadMinidev();
58
+ const projectPath = path.resolve(cwd, config.projectPath);
59
+ if (!fs.existsSync(projectPath)) {
60
+ throw new Error(`\u6784\u5EFA\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${projectPath}
61
+ \u8BF7\u5148\u6267\u884C\u6784\u5EFA\u547D\u4EE4`);
62
+ }
63
+ await applyAuth(minidev, config);
64
+ const params = {
65
+ ...buildBaseParams(config, cwd),
66
+ version: options.version,
67
+ ...config.experience !== void 0 ? { experience: config.experience } : {}
68
+ };
69
+ await minidev.upload(params, { onLog: () => {
70
+ } });
71
+ return {};
72
+ }
73
+ async function preview(config, options, cwd = process.cwd()) {
74
+ const { minidev } = await loadMinidev();
75
+ const projectPath = path.resolve(cwd, config.projectPath);
76
+ if (!fs.existsSync(projectPath)) {
77
+ throw new Error(`\u6784\u5EFA\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${projectPath}
78
+ \u8BF7\u5148\u6267\u884C\u6784\u5EFA\u547D\u4EE4`);
79
+ }
80
+ await applyAuth(minidev, config);
81
+ const result = await minidev.preview(
82
+ buildBaseParams(config, cwd),
83
+ { onLog: () => {
84
+ } }
85
+ );
86
+ if (result?.qrcodeUrl) {
87
+ const outputDest = options.outputPath ?? path.join(cwd, "preview.jpg");
88
+ await downloadFile(result.qrcodeUrl, outputDest);
89
+ }
90
+ }
91
+ export {
92
+ preview,
93
+ upload
94
+ };
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/platforms/baidu.ts
4
+ import fs from "fs";
5
+ import path from "path";
6
+ async function loadCI() {
7
+ try {
8
+ const mod = await import("swan-toolkit");
9
+ const resolved = mod.default ?? mod;
10
+ return { upload: resolved.upload, preview: resolved.preview };
11
+ } catch (err) {
12
+ const notFound = err.code === "MODULE_NOT_FOUND" || err.code === "ERR_MODULE_NOT_FOUND";
13
+ if (notFound) {
14
+ throw new Error("swan-toolkit \u7F3A\u5931\uFF0C\u8BF7\u91CD\u65B0\u5B89\u88C5\uFF1Anpm install mp-ci");
15
+ }
16
+ throw err;
17
+ }
18
+ }
19
+ async function upload(config, options, cwd = process.cwd()) {
20
+ const { upload: swanUpload } = await loadCI();
21
+ const projectPath = path.resolve(cwd, config.projectPath);
22
+ if (!fs.existsSync(projectPath)) {
23
+ throw new Error(`\u6784\u5EFA\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${projectPath}
24
+ \u8BF7\u5148\u6267\u884C\u6784\u5EFA\u547D\u4EE4`);
25
+ }
26
+ await swanUpload({
27
+ projectPath,
28
+ token: config.token,
29
+ releaseVersion: options.version,
30
+ minSwanVersion: config.minSwanVersion,
31
+ desc: options.desc,
32
+ sourcemap: config.sourcemap ?? false
33
+ });
34
+ return {};
35
+ }
36
+ async function preview(config, options, cwd = process.cwd()) {
37
+ const { preview: swanPreview } = await loadCI();
38
+ const projectPath = path.resolve(cwd, config.projectPath);
39
+ if (!fs.existsSync(projectPath)) {
40
+ throw new Error(`\u6784\u5EFA\u4EA7\u7269\u4E0D\u5B58\u5728\uFF1A${projectPath}
41
+ \u8BF7\u5148\u6267\u884C\u6784\u5EFA\u547D\u4EE4`);
42
+ }
43
+ const result = await swanPreview({
44
+ projectPath,
45
+ token: config.token,
46
+ minSwanVersion: config.minSwanVersion,
47
+ base64: true
48
+ });
49
+ const first = result?.list?.[0];
50
+ if (first?.urlBase64) {
51
+ const outputDest = options.outputPath ?? path.join(cwd, "preview.jpg");
52
+ const base64Data = first.urlBase64.replace(/^data:image\/\w+;base64,/, "");
53
+ fs.writeFileSync(outputDest, Buffer.from(base64Data, "base64"));
54
+ }
55
+ }
56
+ export {
57
+ preview,
58
+ upload
59
+ };
package/dist/cli.js ADDED
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli.ts
4
+ import { Command } from "commander";
5
+
6
+ // src/config.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ function readConfig(cwd = process.cwd()) {
10
+ const configPath = path.join(cwd, ".minicirc");
11
+ if (!fs.existsSync(configPath)) {
12
+ throw new Error(
13
+ `\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6 .minicirc\uFF08\u67E5\u627E\u8DEF\u5F84\uFF1A${cwd}\uFF09
14
+ \u8BF7\u521B\u5EFA .minicirc\uFF0C\u6216\u53C2\u8003 .minicirc.example`
15
+ );
16
+ }
17
+ let raw;
18
+ try {
19
+ raw = fs.readFileSync(configPath, "utf-8");
20
+ } catch (err) {
21
+ throw new Error(`\u8BFB\u53D6 .minicirc \u5931\u8D25\uFF1A${err.message}`);
22
+ }
23
+ let config;
24
+ try {
25
+ config = JSON.parse(raw);
26
+ } catch (err) {
27
+ throw new Error(`.minicirc \u4E0D\u662F\u5408\u6CD5\u7684 JSON\uFF1A${err.message}`);
28
+ }
29
+ if (!config.version || typeof config.version !== "string") {
30
+ throw new Error('.minicirc \u5FC5\u987B\u5305\u542B\u5B57\u7B26\u4E32\u7C7B\u578B\u7684 "version" \u5B57\u6BB5');
31
+ }
32
+ return config;
33
+ }
34
+ function getPlatformConfig(config, platform) {
35
+ const pc = config[platform];
36
+ if (!pc) {
37
+ const available = Object.keys(config).filter((k) => !["version", "desc"].includes(k));
38
+ throw new Error(
39
+ `.minicirc \u4E2D\u672A\u627E\u5230\u5E73\u53F0 "${platform}" \u7684\u914D\u7F6E\u3002
40
+ ` + (available.length > 0 ? `\u5DF2\u914D\u7F6E\u7684\u5E73\u53F0\uFF1A${available.join("\u3001")}` : "\u5F53\u524D\u6CA1\u6709\u4EFB\u4F55\u5E73\u53F0\u914D\u7F6E\u3002")
41
+ );
42
+ }
43
+ return pc;
44
+ }
45
+
46
+ // src/logger.ts
47
+ var ESC = "\x1B";
48
+ var c = {
49
+ reset: `${ESC}[0m`,
50
+ bold: `${ESC}[1m`,
51
+ green: `${ESC}[32m`,
52
+ yellow: `${ESC}[33m`,
53
+ red: `${ESC}[31m`,
54
+ cyan: `${ESC}[36m`,
55
+ gray: `${ESC}[90m`
56
+ };
57
+ var paint = (color, msg) => `${color}${msg}${c.reset}`;
58
+ var log = {
59
+ info(msg) {
60
+ console.log(`${paint(c.cyan, "\u2139")} ${msg}`);
61
+ },
62
+ success(msg) {
63
+ console.log(`${paint(c.green, "\u2713")} ${paint(c.bold, msg)}`);
64
+ },
65
+ warn(msg) {
66
+ console.warn(`${paint(c.yellow, "\u26A0")} ${msg}`);
67
+ },
68
+ error(msg) {
69
+ console.error(`${paint(c.red, "\u2717")} ${msg}`);
70
+ },
71
+ dim(msg) {
72
+ console.log(paint(c.gray, msg));
73
+ },
74
+ /** Print a key-value info block before the action starts */
75
+ meta(fields) {
76
+ console.log("");
77
+ const keyLen = Math.max(...Object.keys(fields).map((k) => k.length));
78
+ for (const [key, val] of Object.entries(fields)) {
79
+ if (val == null) continue;
80
+ const pad = " ".repeat(keyLen - key.length);
81
+ console.log(` ${paint(c.gray, key + pad)} ${val}`);
82
+ }
83
+ console.log("");
84
+ },
85
+ /** Spinner-style dots while waiting */
86
+ progress(msg) {
87
+ process.stdout.write(`${paint(c.cyan, "\u2026")} ${msg}`);
88
+ const timer = setInterval(() => process.stdout.write("."), 800);
89
+ return () => {
90
+ clearInterval(timer);
91
+ process.stdout.write("\n");
92
+ };
93
+ }
94
+ };
95
+
96
+ // src/cli.ts
97
+ var SUPPORTED_PLATFORMS = ["weixin", "alipay", "douyin", "baidu"];
98
+ var PLATFORM_NAMES = {
99
+ weixin: "\u5FAE\u4FE1\u5C0F\u7A0B\u5E8F",
100
+ alipay: "\u652F\u4ED8\u5B9D\u5C0F\u7A0B\u5E8F",
101
+ douyin: "\u6296\u97F3\u5C0F\u7A0B\u5E8F",
102
+ baidu: "\u767E\u5EA6\u667A\u80FD\u5C0F\u7A0B\u5E8F"
103
+ };
104
+ var now = () => (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
105
+ var toKB = (bytes) => `${(bytes / 1024).toFixed(1)} KB`;
106
+ var program = new Command();
107
+ program.name("miniapp-ci").description("\u591A\u5E73\u53F0\u5C0F\u7A0B\u5E8F CI/CD \u4E0A\u4F20\u5DE5\u5177").version("__VERSION__");
108
+ program.command("upload").description("\u5C06\u5C0F\u7A0B\u5E8F\u6784\u5EFA\u4EA7\u7269\u4E0A\u4F20\u5230\u5BF9\u5E94\u5E73\u53F0\u5F00\u53D1\u8005\u540E\u53F0").requiredOption("-p, --platform <platform>", `\u76EE\u6807\u5E73\u53F0\uFF08${SUPPORTED_PLATFORMS.join(" | ")}\uFF09`).option("-v, --version <version>", "\u8986\u76D6\u7248\u672C\u53F7\uFF08\u9ED8\u8BA4\u8BFB\u53D6 .minicirc\uFF09").option("-d, --desc <desc>", "\u4E0A\u4F20\u5907\u6CE8 / \u53D1\u5E03\u8BF4\u660E").option("--cwd <path>", "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").action(async (opts) => {
109
+ const cwd = opts.cwd ?? process.cwd();
110
+ const platform = opts.platform;
111
+ if (!SUPPORTED_PLATFORMS.includes(platform)) {
112
+ log.error(`\u4E0D\u652F\u6301\u7684\u5E73\u53F0 "${opts.platform}"\uFF0C\u53EF\u9009\uFF1A${SUPPORTED_PLATFORMS.join("\u3001")}`);
113
+ process.exit(1);
114
+ }
115
+ let config;
116
+ try {
117
+ config = readConfig(cwd);
118
+ } catch (err) {
119
+ log.error(err.message);
120
+ process.exit(1);
121
+ }
122
+ const version = opts.version ?? config.version;
123
+ const desc = opts.desc ?? config.desc ?? `v${version}`;
124
+ const uploadOpts = { version, desc };
125
+ const platformName = PLATFORM_NAMES[platform];
126
+ log.meta({
127
+ \u5E73\u53F0: platformName,
128
+ \u7248\u672C: version,
129
+ \u5907\u6CE8: desc
130
+ });
131
+ const stopProgress = log.progress(`\u6B63\u5728\u4E0A\u4F20 ${platformName}`);
132
+ let result = {};
133
+ try {
134
+ if (platform === "weixin") {
135
+ const { upload } = await import("./weixin-COXBODYP.js");
136
+ result = await upload(getPlatformConfig(config, "weixin"), uploadOpts, cwd) ?? {};
137
+ } else if (platform === "alipay") {
138
+ const { upload } = await import("./alipay-M5WECWQ7.js");
139
+ result = await upload(getPlatformConfig(config, "alipay"), uploadOpts, cwd) ?? {};
140
+ } else if (platform === "douyin") {
141
+ const { upload } = await import("./douyin-DZDRXGFD.js");
142
+ result = await upload(getPlatformConfig(config, "douyin"), uploadOpts, cwd) ?? {};
143
+ } else if (platform === "baidu") {
144
+ const { upload } = await import("./baidu-J4F6WMQL.js");
145
+ result = await upload(getPlatformConfig(config, "baidu"), uploadOpts, cwd) ?? {};
146
+ }
147
+ stopProgress();
148
+ log.success(`\u7248\u672C ${version} \u4E0A\u4F20\u6210\u529F ${now()}`);
149
+ if (result.totalSize) {
150
+ const sizeInfo = result.mainSize ? `\u672C\u6B21\u4E0A\u4F20 ${toKB(result.totalSize)}\uFF0C\u5176\u4E2D\u4E3B\u5305 ${toKB(result.mainSize)}` : `\u672C\u6B21\u4E0A\u4F20 ${toKB(result.totalSize)}`;
151
+ log.dim(` ${sizeInfo}`);
152
+ }
153
+ } catch (err) {
154
+ stopProgress();
155
+ log.error(`\u4E0A\u4F20\u5931\u8D25 ${now()}`);
156
+ log.error(err.message);
157
+ process.exit(1);
158
+ }
159
+ });
160
+ program.command("preview").description("\u751F\u6210\u5C0F\u7A0B\u5E8F\u9884\u89C8\u4E8C\u7EF4\u7801").requiredOption("-p, --platform <platform>", `\u76EE\u6807\u5E73\u53F0\uFF08${SUPPORTED_PLATFORMS.join(" | ")}\uFF09`).option("-v, --version <version>", "\u8986\u76D6\u7248\u672C\u53F7\uFF08\u9ED8\u8BA4\u8BFB\u53D6 .minicirc\uFF09").option("-d, --desc <desc>", "\u9884\u89C8\u5907\u6CE8").option("-o, --output <path>", "\u4E8C\u7EF4\u7801\u56FE\u7247\u4FDD\u5B58\u8DEF\u5F84\uFF08\u9ED8\u8BA4\uFF1A./preview.jpg\uFF09").option("--cwd <path>", "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\uFF1A\u5F53\u524D\u76EE\u5F55\uFF09").action(async (opts) => {
161
+ const cwd = opts.cwd ?? process.cwd();
162
+ const platform = opts.platform;
163
+ if (!SUPPORTED_PLATFORMS.includes(platform)) {
164
+ log.error(`\u4E0D\u652F\u6301\u7684\u5E73\u53F0 "${opts.platform}"\uFF0C\u53EF\u9009\uFF1A${SUPPORTED_PLATFORMS.join("\u3001")}`);
165
+ process.exit(1);
166
+ }
167
+ let config;
168
+ try {
169
+ config = readConfig(cwd);
170
+ } catch (err) {
171
+ log.error(err.message);
172
+ process.exit(1);
173
+ }
174
+ const version = opts.version ?? config.version;
175
+ const desc = opts.desc ?? config.desc ?? `v${version}`;
176
+ const outputPath = opts.output ?? "./preview.jpg";
177
+ const previewOpts = { version, desc, outputPath };
178
+ const platformName = PLATFORM_NAMES[platform];
179
+ log.meta({
180
+ \u5E73\u53F0: platformName,
181
+ \u7248\u672C: version,
182
+ \u5907\u6CE8: desc,
183
+ \u4E8C\u7EF4\u7801: outputPath
184
+ });
185
+ const stopProgress = log.progress(`\u6B63\u5728\u751F\u6210 ${platformName} \u9884\u89C8\u4E8C\u7EF4\u7801`);
186
+ try {
187
+ if (platform === "weixin") {
188
+ const { preview } = await import("./weixin-COXBODYP.js");
189
+ await preview(getPlatformConfig(config, "weixin"), previewOpts, cwd);
190
+ } else if (platform === "alipay") {
191
+ const { preview } = await import("./alipay-M5WECWQ7.js");
192
+ await preview(getPlatformConfig(config, "alipay"), previewOpts, cwd);
193
+ } else if (platform === "douyin") {
194
+ const { preview } = await import("./douyin-DZDRXGFD.js");
195
+ await preview(getPlatformConfig(config, "douyin"), previewOpts, cwd);
196
+ } else if (platform === "baidu") {
197
+ const { preview } = await import("./baidu-J4F6WMQL.js");
198
+ await preview(getPlatformConfig(config, "baidu"), previewOpts, cwd);
199
+ }
200
+ stopProgress();
201
+ log.success(`\u9884\u89C8\u4E8C\u7EF4\u7801\u5DF2\u751F\u6210 ${now()}`);
202
+ log.dim(` \u5DF2\u4FDD\u5B58\u81F3 ${outputPath}`);
203
+ } catch (err) {
204
+ stopProgress();
205
+ log.error(`\u751F\u6210\u9884\u89C8\u5931\u8D25 ${now()}`);
206
+ log.error(err.message);
207
+ process.exit(1);
208
+ }
209
+ });
210
+ program.parse();