@huoye/app-ship 0.1.1 → 0.2.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 +57 -0
- package/README.md +332 -298
- package/dist/cli.js +1602 -547
- package/dist/index.cjs +1524 -539
- package/dist/index.d.cts +278 -55
- package/dist/index.d.ts +278 -55
- package/dist/index.js +1504 -529
- package/package.json +44 -12
- package/dist/cli.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -34,42 +34,546 @@ __export(index_exports, {
|
|
|
34
34
|
AppleJWTGenerator: () => AppleJWTGenerator,
|
|
35
35
|
AppleStoreUploader: () => AppleStoreUploader,
|
|
36
36
|
ConfigValidator: () => ConfigValidator,
|
|
37
|
+
DEFAULT_RELEASE_RECEIPT_LIMIT: () => DEFAULT_RELEASE_RECEIPT_LIMIT,
|
|
37
38
|
DEFAULT_RETRY_CONFIG: () => DEFAULT_RETRY_CONFIG,
|
|
38
39
|
EnvValidator: () => EnvValidator,
|
|
39
40
|
ErrorCategory: () => ErrorCategory,
|
|
41
|
+
GooglePlayUploader: () => GooglePlayUploader,
|
|
40
42
|
HarmonyUploader: () => HarmonyUploader,
|
|
41
43
|
HuaweiUploader: () => HuaweiUploader,
|
|
42
44
|
OppoUploader: () => OppoUploader,
|
|
43
45
|
PgyerUploader: () => PgyerUploader,
|
|
46
|
+
RELEASE_RECEIPT_SCHEMA_VERSION: () => RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
47
|
+
ReleaseReceiptStore: () => ReleaseReceiptStore,
|
|
44
48
|
RetryStrategy: () => RetryStrategy,
|
|
45
49
|
TencentUploader: () => TencentUploader,
|
|
46
50
|
TestFlightUploader: () => TestFlightUploader,
|
|
47
51
|
UploadManager: () => UploadManager,
|
|
48
52
|
VivoUploader: () => VivoUploader,
|
|
49
53
|
XiaomiUploader: () => XiaomiUploader,
|
|
54
|
+
createProjectKey: () => createProjectKey,
|
|
55
|
+
createReleaseReceipt: () => createReleaseReceipt,
|
|
50
56
|
createUploadManager: () => createUploadManager,
|
|
51
57
|
createUploadRetryStrategy: () => createUploadRetryStrategy,
|
|
52
|
-
|
|
58
|
+
createUploadTargets: () => createUploadTargets,
|
|
59
|
+
formatReleaseReceipt: () => formatReleaseReceipt,
|
|
60
|
+
parsePackageInfo: () => parsePackageInfo,
|
|
61
|
+
resolveReleaseProjectRoot: () => resolveReleaseProjectRoot,
|
|
62
|
+
sanitizeReleaseMessage: () => sanitizeReleaseMessage
|
|
53
63
|
});
|
|
54
64
|
module.exports = __toCommonJS(index_exports);
|
|
55
65
|
|
|
66
|
+
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
67
|
+
var ANSI_BACKGROUND_OFFSET = 10;
|
|
68
|
+
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
69
|
+
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
70
|
+
var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
|
|
71
|
+
var styles = {
|
|
72
|
+
modifier: {
|
|
73
|
+
reset: [0, 0],
|
|
74
|
+
// 21 isn't widely supported and 22 does the same thing
|
|
75
|
+
bold: [1, 22],
|
|
76
|
+
dim: [2, 22],
|
|
77
|
+
italic: [3, 23],
|
|
78
|
+
underline: [4, 24],
|
|
79
|
+
overline: [53, 55],
|
|
80
|
+
inverse: [7, 27],
|
|
81
|
+
hidden: [8, 28],
|
|
82
|
+
strikethrough: [9, 29]
|
|
83
|
+
},
|
|
84
|
+
color: {
|
|
85
|
+
black: [30, 39],
|
|
86
|
+
red: [31, 39],
|
|
87
|
+
green: [32, 39],
|
|
88
|
+
yellow: [33, 39],
|
|
89
|
+
blue: [34, 39],
|
|
90
|
+
magenta: [35, 39],
|
|
91
|
+
cyan: [36, 39],
|
|
92
|
+
white: [37, 39],
|
|
93
|
+
// Bright color
|
|
94
|
+
blackBright: [90, 39],
|
|
95
|
+
gray: [90, 39],
|
|
96
|
+
// Alias of `blackBright`
|
|
97
|
+
grey: [90, 39],
|
|
98
|
+
// Alias of `blackBright`
|
|
99
|
+
redBright: [91, 39],
|
|
100
|
+
greenBright: [92, 39],
|
|
101
|
+
yellowBright: [93, 39],
|
|
102
|
+
blueBright: [94, 39],
|
|
103
|
+
magentaBright: [95, 39],
|
|
104
|
+
cyanBright: [96, 39],
|
|
105
|
+
whiteBright: [97, 39]
|
|
106
|
+
},
|
|
107
|
+
bgColor: {
|
|
108
|
+
bgBlack: [40, 49],
|
|
109
|
+
bgRed: [41, 49],
|
|
110
|
+
bgGreen: [42, 49],
|
|
111
|
+
bgYellow: [43, 49],
|
|
112
|
+
bgBlue: [44, 49],
|
|
113
|
+
bgMagenta: [45, 49],
|
|
114
|
+
bgCyan: [46, 49],
|
|
115
|
+
bgWhite: [47, 49],
|
|
116
|
+
// Bright color
|
|
117
|
+
bgBlackBright: [100, 49],
|
|
118
|
+
bgGray: [100, 49],
|
|
119
|
+
// Alias of `bgBlackBright`
|
|
120
|
+
bgGrey: [100, 49],
|
|
121
|
+
// Alias of `bgBlackBright`
|
|
122
|
+
bgRedBright: [101, 49],
|
|
123
|
+
bgGreenBright: [102, 49],
|
|
124
|
+
bgYellowBright: [103, 49],
|
|
125
|
+
bgBlueBright: [104, 49],
|
|
126
|
+
bgMagentaBright: [105, 49],
|
|
127
|
+
bgCyanBright: [106, 49],
|
|
128
|
+
bgWhiteBright: [107, 49]
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var modifierNames = Object.keys(styles.modifier);
|
|
132
|
+
var foregroundColorNames = Object.keys(styles.color);
|
|
133
|
+
var backgroundColorNames = Object.keys(styles.bgColor);
|
|
134
|
+
var colorNames = [...foregroundColorNames, ...backgroundColorNames];
|
|
135
|
+
function assembleStyles() {
|
|
136
|
+
const codes = /* @__PURE__ */ new Map();
|
|
137
|
+
for (const [groupName, group] of Object.entries(styles)) {
|
|
138
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
139
|
+
styles[styleName] = {
|
|
140
|
+
open: `\x1B[${style[0]}m`,
|
|
141
|
+
close: `\x1B[${style[1]}m`
|
|
142
|
+
};
|
|
143
|
+
group[styleName] = styles[styleName];
|
|
144
|
+
codes.set(style[0], style[1]);
|
|
145
|
+
}
|
|
146
|
+
Object.defineProperty(styles, groupName, {
|
|
147
|
+
value: group,
|
|
148
|
+
enumerable: false
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
Object.defineProperty(styles, "codes", {
|
|
152
|
+
value: codes,
|
|
153
|
+
enumerable: false
|
|
154
|
+
});
|
|
155
|
+
styles.color.close = "\x1B[39m";
|
|
156
|
+
styles.bgColor.close = "\x1B[49m";
|
|
157
|
+
styles.color.ansi = wrapAnsi16();
|
|
158
|
+
styles.color.ansi256 = wrapAnsi256();
|
|
159
|
+
styles.color.ansi16m = wrapAnsi16m();
|
|
160
|
+
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
|
|
161
|
+
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
|
|
162
|
+
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
|
|
163
|
+
Object.defineProperties(styles, {
|
|
164
|
+
rgbToAnsi256: {
|
|
165
|
+
value(red, green, blue) {
|
|
166
|
+
if (red === green && green === blue) {
|
|
167
|
+
if (red < 8) {
|
|
168
|
+
return 16;
|
|
169
|
+
}
|
|
170
|
+
if (red > 248) {
|
|
171
|
+
return 231;
|
|
172
|
+
}
|
|
173
|
+
return Math.round((red - 8) / 247 * 24) + 232;
|
|
174
|
+
}
|
|
175
|
+
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
176
|
+
},
|
|
177
|
+
enumerable: false
|
|
178
|
+
},
|
|
179
|
+
hexToRgb: {
|
|
180
|
+
value(hex) {
|
|
181
|
+
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
|
182
|
+
if (!matches) {
|
|
183
|
+
return [0, 0, 0];
|
|
184
|
+
}
|
|
185
|
+
let [colorString] = matches;
|
|
186
|
+
if (colorString.length === 3) {
|
|
187
|
+
colorString = [...colorString].map((character) => character + character).join("");
|
|
188
|
+
}
|
|
189
|
+
const integer = Number.parseInt(colorString, 16);
|
|
190
|
+
return [
|
|
191
|
+
/* eslint-disable no-bitwise */
|
|
192
|
+
integer >> 16 & 255,
|
|
193
|
+
integer >> 8 & 255,
|
|
194
|
+
integer & 255
|
|
195
|
+
/* eslint-enable no-bitwise */
|
|
196
|
+
];
|
|
197
|
+
},
|
|
198
|
+
enumerable: false
|
|
199
|
+
},
|
|
200
|
+
hexToAnsi256: {
|
|
201
|
+
value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
|
|
202
|
+
enumerable: false
|
|
203
|
+
},
|
|
204
|
+
ansi256ToAnsi: {
|
|
205
|
+
value(code) {
|
|
206
|
+
if (code < 8) {
|
|
207
|
+
return 30 + code;
|
|
208
|
+
}
|
|
209
|
+
if (code < 16) {
|
|
210
|
+
return 90 + (code - 8);
|
|
211
|
+
}
|
|
212
|
+
let red;
|
|
213
|
+
let green;
|
|
214
|
+
let blue;
|
|
215
|
+
if (code >= 232) {
|
|
216
|
+
red = ((code - 232) * 10 + 8) / 255;
|
|
217
|
+
green = red;
|
|
218
|
+
blue = red;
|
|
219
|
+
} else {
|
|
220
|
+
code -= 16;
|
|
221
|
+
const remainder = code % 36;
|
|
222
|
+
red = Math.floor(code / 36) / 5;
|
|
223
|
+
green = Math.floor(remainder / 6) / 5;
|
|
224
|
+
blue = remainder % 6 / 5;
|
|
225
|
+
}
|
|
226
|
+
const value = Math.max(red, green, blue) * 2;
|
|
227
|
+
if (value === 0) {
|
|
228
|
+
return 30;
|
|
229
|
+
}
|
|
230
|
+
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
231
|
+
if (value === 2) {
|
|
232
|
+
result += 60;
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
},
|
|
236
|
+
enumerable: false
|
|
237
|
+
},
|
|
238
|
+
rgbToAnsi: {
|
|
239
|
+
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
|
|
240
|
+
enumerable: false
|
|
241
|
+
},
|
|
242
|
+
hexToAnsi: {
|
|
243
|
+
value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
|
|
244
|
+
enumerable: false
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return styles;
|
|
248
|
+
}
|
|
249
|
+
var ansiStyles = assembleStyles();
|
|
250
|
+
var ansi_styles_default = ansiStyles;
|
|
251
|
+
|
|
252
|
+
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
253
|
+
var import_node_process = __toESM(require("process"), 1);
|
|
254
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
255
|
+
var import_node_tty = __toESM(require("tty"), 1);
|
|
256
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) {
|
|
257
|
+
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
258
|
+
const position = argv.indexOf(prefix + flag);
|
|
259
|
+
const terminatorPosition = argv.indexOf("--");
|
|
260
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
261
|
+
}
|
|
262
|
+
var { env } = import_node_process.default;
|
|
263
|
+
var flagForceColor;
|
|
264
|
+
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
265
|
+
flagForceColor = 0;
|
|
266
|
+
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
267
|
+
flagForceColor = 1;
|
|
268
|
+
}
|
|
269
|
+
function envForceColor() {
|
|
270
|
+
if ("FORCE_COLOR" in env) {
|
|
271
|
+
if (env.FORCE_COLOR === "true") {
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
if (env.FORCE_COLOR === "false") {
|
|
275
|
+
return 0;
|
|
276
|
+
}
|
|
277
|
+
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function translateLevel(level) {
|
|
281
|
+
if (level === 0) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
level,
|
|
286
|
+
hasBasic: true,
|
|
287
|
+
has256: level >= 2,
|
|
288
|
+
has16m: level >= 3
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
292
|
+
const noFlagForceColor = envForceColor();
|
|
293
|
+
if (noFlagForceColor !== void 0) {
|
|
294
|
+
flagForceColor = noFlagForceColor;
|
|
295
|
+
}
|
|
296
|
+
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
297
|
+
if (forceColor === 0) {
|
|
298
|
+
return 0;
|
|
299
|
+
}
|
|
300
|
+
if (sniffFlags) {
|
|
301
|
+
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
302
|
+
return 3;
|
|
303
|
+
}
|
|
304
|
+
if (hasFlag("color=256")) {
|
|
305
|
+
return 2;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if ("TF_BUILD" in env && "AGENT_NAME" in env) {
|
|
309
|
+
return 1;
|
|
310
|
+
}
|
|
311
|
+
if (haveStream && !streamIsTTY && forceColor === void 0) {
|
|
312
|
+
return 0;
|
|
313
|
+
}
|
|
314
|
+
const min = forceColor || 0;
|
|
315
|
+
if (env.TERM === "dumb") {
|
|
316
|
+
return min;
|
|
317
|
+
}
|
|
318
|
+
if (import_node_process.default.platform === "win32") {
|
|
319
|
+
const osRelease = import_node_os.default.release().split(".");
|
|
320
|
+
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
321
|
+
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
322
|
+
}
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
if ("CI" in env) {
|
|
326
|
+
if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) {
|
|
327
|
+
return 3;
|
|
328
|
+
}
|
|
329
|
+
if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
|
|
330
|
+
return 1;
|
|
331
|
+
}
|
|
332
|
+
return min;
|
|
333
|
+
}
|
|
334
|
+
if ("TEAMCITY_VERSION" in env) {
|
|
335
|
+
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
|
|
336
|
+
}
|
|
337
|
+
if (env.COLORTERM === "truecolor") {
|
|
338
|
+
return 3;
|
|
339
|
+
}
|
|
340
|
+
if (env.TERM === "xterm-kitty") {
|
|
341
|
+
return 3;
|
|
342
|
+
}
|
|
343
|
+
if (env.TERM === "xterm-ghostty") {
|
|
344
|
+
return 3;
|
|
345
|
+
}
|
|
346
|
+
if (env.TERM === "wezterm") {
|
|
347
|
+
return 3;
|
|
348
|
+
}
|
|
349
|
+
if ("TERM_PROGRAM" in env) {
|
|
350
|
+
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
351
|
+
switch (env.TERM_PROGRAM) {
|
|
352
|
+
case "iTerm.app": {
|
|
353
|
+
return version >= 3 ? 3 : 2;
|
|
354
|
+
}
|
|
355
|
+
case "Apple_Terminal": {
|
|
356
|
+
return 2;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (/-256(color)?$/i.test(env.TERM)) {
|
|
361
|
+
return 2;
|
|
362
|
+
}
|
|
363
|
+
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
|
|
364
|
+
return 1;
|
|
365
|
+
}
|
|
366
|
+
if ("COLORTERM" in env) {
|
|
367
|
+
return 1;
|
|
368
|
+
}
|
|
369
|
+
return min;
|
|
370
|
+
}
|
|
371
|
+
function createSupportsColor(stream, options = {}) {
|
|
372
|
+
const level = _supportsColor(stream, {
|
|
373
|
+
streamIsTTY: stream && stream.isTTY,
|
|
374
|
+
...options
|
|
375
|
+
});
|
|
376
|
+
return translateLevel(level);
|
|
377
|
+
}
|
|
378
|
+
var supportsColor = {
|
|
379
|
+
stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }),
|
|
380
|
+
stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) })
|
|
381
|
+
};
|
|
382
|
+
var supports_color_default = supportsColor;
|
|
383
|
+
|
|
384
|
+
// node_modules/chalk/source/utilities.js
|
|
385
|
+
function stringReplaceAll(string, substring, replacer) {
|
|
386
|
+
let index = string.indexOf(substring);
|
|
387
|
+
if (index === -1) {
|
|
388
|
+
return string;
|
|
389
|
+
}
|
|
390
|
+
const substringLength = substring.length;
|
|
391
|
+
let endIndex = 0;
|
|
392
|
+
let returnValue = "";
|
|
393
|
+
do {
|
|
394
|
+
returnValue += string.slice(endIndex, index) + substring + replacer;
|
|
395
|
+
endIndex = index + substringLength;
|
|
396
|
+
index = string.indexOf(substring, endIndex);
|
|
397
|
+
} while (index !== -1);
|
|
398
|
+
returnValue += string.slice(endIndex);
|
|
399
|
+
return returnValue;
|
|
400
|
+
}
|
|
401
|
+
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
402
|
+
let endIndex = 0;
|
|
403
|
+
let returnValue = "";
|
|
404
|
+
do {
|
|
405
|
+
const gotCR = string[index - 1] === "\r";
|
|
406
|
+
returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
|
|
407
|
+
endIndex = index + 1;
|
|
408
|
+
index = string.indexOf("\n", endIndex);
|
|
409
|
+
} while (index !== -1);
|
|
410
|
+
returnValue += string.slice(endIndex);
|
|
411
|
+
return returnValue;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// node_modules/chalk/source/index.js
|
|
415
|
+
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
416
|
+
var GENERATOR = /* @__PURE__ */ Symbol("GENERATOR");
|
|
417
|
+
var STYLER = /* @__PURE__ */ Symbol("STYLER");
|
|
418
|
+
var IS_EMPTY = /* @__PURE__ */ Symbol("IS_EMPTY");
|
|
419
|
+
var levelMapping = [
|
|
420
|
+
"ansi",
|
|
421
|
+
"ansi",
|
|
422
|
+
"ansi256",
|
|
423
|
+
"ansi16m"
|
|
424
|
+
];
|
|
425
|
+
var styles2 = /* @__PURE__ */ Object.create(null);
|
|
426
|
+
var applyOptions = (object, options = {}) => {
|
|
427
|
+
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
|
|
428
|
+
throw new Error("The `level` option should be an integer from 0 to 3");
|
|
429
|
+
}
|
|
430
|
+
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
431
|
+
object.level = options.level === void 0 ? colorLevel : options.level;
|
|
432
|
+
};
|
|
433
|
+
var chalkFactory = (options) => {
|
|
434
|
+
const chalk2 = (...strings) => strings.join(" ");
|
|
435
|
+
applyOptions(chalk2, options);
|
|
436
|
+
Object.setPrototypeOf(chalk2, createChalk.prototype);
|
|
437
|
+
return chalk2;
|
|
438
|
+
};
|
|
439
|
+
function createChalk(options) {
|
|
440
|
+
return chalkFactory(options);
|
|
441
|
+
}
|
|
442
|
+
Object.setPrototypeOf(createChalk.prototype, Function.prototype);
|
|
443
|
+
for (const [styleName, style] of Object.entries(ansi_styles_default)) {
|
|
444
|
+
styles2[styleName] = {
|
|
445
|
+
get() {
|
|
446
|
+
const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
|
|
447
|
+
Object.defineProperty(this, styleName, { value: builder });
|
|
448
|
+
return builder;
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
styles2.visible = {
|
|
453
|
+
get() {
|
|
454
|
+
const builder = createBuilder(this, this[STYLER], true);
|
|
455
|
+
Object.defineProperty(this, "visible", { value: builder });
|
|
456
|
+
return builder;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
var getModelAnsi = (model, level, type, ...arguments_) => {
|
|
460
|
+
if (model === "rgb") {
|
|
461
|
+
if (level === "ansi16m") {
|
|
462
|
+
return ansi_styles_default[type].ansi16m(...arguments_);
|
|
463
|
+
}
|
|
464
|
+
if (level === "ansi256") {
|
|
465
|
+
return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
|
|
466
|
+
}
|
|
467
|
+
return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
|
|
468
|
+
}
|
|
469
|
+
if (model === "hex") {
|
|
470
|
+
return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
|
|
471
|
+
}
|
|
472
|
+
return ansi_styles_default[type][model](...arguments_);
|
|
473
|
+
};
|
|
474
|
+
var usedModels = ["rgb", "hex", "ansi256"];
|
|
475
|
+
for (const model of usedModels) {
|
|
476
|
+
styles2[model] = {
|
|
477
|
+
get() {
|
|
478
|
+
const { level } = this;
|
|
479
|
+
return function(...arguments_) {
|
|
480
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
|
|
481
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
|
|
486
|
+
styles2[bgModel] = {
|
|
487
|
+
get() {
|
|
488
|
+
const { level } = this;
|
|
489
|
+
return function(...arguments_) {
|
|
490
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
|
|
491
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
var proto = Object.defineProperties(() => {
|
|
497
|
+
}, {
|
|
498
|
+
...styles2,
|
|
499
|
+
level: {
|
|
500
|
+
enumerable: true,
|
|
501
|
+
get() {
|
|
502
|
+
return this[GENERATOR].level;
|
|
503
|
+
},
|
|
504
|
+
set(level) {
|
|
505
|
+
this[GENERATOR].level = level;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
});
|
|
509
|
+
var createStyler = (open, close, parent) => {
|
|
510
|
+
let openAll;
|
|
511
|
+
let closeAll;
|
|
512
|
+
if (parent === void 0) {
|
|
513
|
+
openAll = open;
|
|
514
|
+
closeAll = close;
|
|
515
|
+
} else {
|
|
516
|
+
openAll = parent.openAll + open;
|
|
517
|
+
closeAll = close + parent.closeAll;
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
open,
|
|
521
|
+
close,
|
|
522
|
+
openAll,
|
|
523
|
+
closeAll,
|
|
524
|
+
parent
|
|
525
|
+
};
|
|
526
|
+
};
|
|
527
|
+
var createBuilder = (self, _styler, _isEmpty) => {
|
|
528
|
+
const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
|
|
529
|
+
Object.setPrototypeOf(builder, proto);
|
|
530
|
+
builder[GENERATOR] = self;
|
|
531
|
+
builder[STYLER] = _styler;
|
|
532
|
+
builder[IS_EMPTY] = _isEmpty;
|
|
533
|
+
return builder;
|
|
534
|
+
};
|
|
535
|
+
var applyStyle = (self, string) => {
|
|
536
|
+
if (self.level <= 0 || !string) {
|
|
537
|
+
return self[IS_EMPTY] ? "" : string;
|
|
538
|
+
}
|
|
539
|
+
let styler = self[STYLER];
|
|
540
|
+
if (styler === void 0) {
|
|
541
|
+
return string;
|
|
542
|
+
}
|
|
543
|
+
const { openAll, closeAll } = styler;
|
|
544
|
+
if (string.includes("\x1B")) {
|
|
545
|
+
while (styler !== void 0) {
|
|
546
|
+
string = stringReplaceAll(string, styler.close, styler.open);
|
|
547
|
+
styler = styler.parent;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const lfIndex = string.indexOf("\n");
|
|
551
|
+
if (lfIndex !== -1) {
|
|
552
|
+
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
553
|
+
}
|
|
554
|
+
return openAll + string + closeAll;
|
|
555
|
+
};
|
|
556
|
+
Object.defineProperties(createChalk.prototype, styles2);
|
|
557
|
+
var chalk = createChalk();
|
|
558
|
+
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
559
|
+
var source_default = chalk;
|
|
560
|
+
|
|
56
561
|
// src/utils/logger.ts
|
|
57
|
-
var import_chalk = __toESM(require("chalk"), 1);
|
|
58
562
|
var ConsoleLogger = class {
|
|
59
563
|
info(message) {
|
|
60
|
-
console.log(
|
|
564
|
+
console.log(source_default.blue("\u2139\uFE0F " + message));
|
|
61
565
|
}
|
|
62
566
|
success(message) {
|
|
63
|
-
console.log(
|
|
567
|
+
console.log(source_default.green("\u2705 " + message));
|
|
64
568
|
}
|
|
65
569
|
warn(message) {
|
|
66
|
-
console.log(
|
|
570
|
+
console.log(source_default.yellow("\u26A0\uFE0F " + message));
|
|
67
571
|
}
|
|
68
572
|
error(message) {
|
|
69
|
-
console.log(
|
|
573
|
+
console.log(source_default.red("\u274C " + message));
|
|
70
574
|
}
|
|
71
575
|
title(message) {
|
|
72
|
-
console.log(
|
|
576
|
+
console.log(source_default.bold.cyan("\n" + message + "\n"));
|
|
73
577
|
}
|
|
74
578
|
newLine() {
|
|
75
579
|
console.log("");
|
|
@@ -106,16 +610,18 @@ var ConfigValidator = class {
|
|
|
106
610
|
logger.error(`Config file not found: ${filePath}`);
|
|
107
611
|
return null;
|
|
108
612
|
}
|
|
109
|
-
let content = import_fs_extra.default.readFileSync(filePath, "utf8");
|
|
110
|
-
const envRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
111
|
-
const matches = [...content.matchAll(envRegex)];
|
|
112
|
-
const requiredEnvs = Array.from(new Set(matches.map((m) => m[1])));
|
|
113
|
-
if (!EnvValidator.validate(requiredEnvs)) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
content = content.replace(envRegex, (_, varName) => process.env[varName] || "");
|
|
117
613
|
try {
|
|
118
|
-
const
|
|
614
|
+
const content = import_fs_extra.default.readFileSync(filePath, "utf8");
|
|
615
|
+
const rawConfig = import_js_yaml.default.load(content);
|
|
616
|
+
if (!rawConfig || typeof rawConfig !== "object") {
|
|
617
|
+
logger.error("Config file must contain a YAML object");
|
|
618
|
+
return null;
|
|
619
|
+
}
|
|
620
|
+
const requiredEnvs = this.collectRequiredEnvVars(rawConfig);
|
|
621
|
+
if (!EnvValidator.validate(requiredEnvs)) {
|
|
622
|
+
return null;
|
|
623
|
+
}
|
|
624
|
+
const config = this.resolveEnvVars(rawConfig);
|
|
119
625
|
if (!this.validateSchema(config)) {
|
|
120
626
|
return null;
|
|
121
627
|
}
|
|
@@ -125,15 +631,71 @@ var ConfigValidator = class {
|
|
|
125
631
|
return null;
|
|
126
632
|
}
|
|
127
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* 只收集本次实际启用配置中的环境变量,避免禁用平台阻塞其它上传任务。
|
|
636
|
+
*/
|
|
637
|
+
static collectRequiredEnvVars(config) {
|
|
638
|
+
const activeConfig = {
|
|
639
|
+
...config,
|
|
640
|
+
publish: Object.fromEntries(
|
|
641
|
+
Object.entries(config.publish || {}).filter(([, value]) => value?.enable === true)
|
|
642
|
+
)
|
|
643
|
+
};
|
|
644
|
+
const requiredEnvs = /* @__PURE__ */ new Set();
|
|
645
|
+
const envRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
646
|
+
const visit = (value) => {
|
|
647
|
+
if (typeof value === "string") {
|
|
648
|
+
for (const match of value.matchAll(envRegex)) {
|
|
649
|
+
requiredEnvs.add(match[1]);
|
|
650
|
+
}
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
if (Array.isArray(value)) {
|
|
654
|
+
value.forEach(visit);
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (value && typeof value === "object") {
|
|
658
|
+
Object.values(value).forEach(visit);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
visit(activeConfig);
|
|
662
|
+
return Array.from(requiredEnvs);
|
|
663
|
+
}
|
|
664
|
+
/** 在内存中递归解析环境变量,不重新拼接 YAML,避免密钥字符破坏 YAML 结构。 */
|
|
665
|
+
static resolveEnvVars(value) {
|
|
666
|
+
if (typeof value === "string") {
|
|
667
|
+
return value.replace(
|
|
668
|
+
/\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g,
|
|
669
|
+
(_, varName) => process.env[varName] || ""
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
if (Array.isArray(value)) {
|
|
673
|
+
return value.map((item) => this.resolveEnvVars(item));
|
|
674
|
+
}
|
|
675
|
+
if (value && typeof value === "object") {
|
|
676
|
+
return Object.fromEntries(
|
|
677
|
+
Object.entries(value).map(([key, item]) => [key, this.resolveEnvVars(item)])
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
return value;
|
|
681
|
+
}
|
|
128
682
|
/**
|
|
129
683
|
* 验证配置 Schema
|
|
130
684
|
* @param config 配置对象
|
|
131
685
|
*/
|
|
132
686
|
static validateSchema(config) {
|
|
133
687
|
if (config.publish?.app_store?.enable) {
|
|
688
|
+
const mode = config.publish.app_store.mode || "app_store";
|
|
134
689
|
const auth = config.publish.app_store.auth;
|
|
135
|
-
|
|
136
|
-
|
|
690
|
+
const hasCliAuth = Boolean(auth?.appleId && auth?.appPassword);
|
|
691
|
+
const hasApiAuth = Boolean(
|
|
692
|
+
auth?.apiKeyId && auth?.apiIssuerId && (auth?.privateKeyPath || auth?.privateKeyContent)
|
|
693
|
+
);
|
|
694
|
+
const hasRequiredAuth = mode === "adhoc" || (mode === "testflight" ? hasApiAuth : hasCliAuth || hasApiAuth);
|
|
695
|
+
if (!hasRequiredAuth) {
|
|
696
|
+
logger.error(
|
|
697
|
+
mode === "testflight" ? "TestFlight enabled but missing complete API key credentials" : "App Store enabled but missing Apple ID credentials or complete API key credentials"
|
|
698
|
+
);
|
|
137
699
|
return false;
|
|
138
700
|
}
|
|
139
701
|
}
|
|
@@ -179,6 +741,20 @@ var ConfigValidator = class {
|
|
|
179
741
|
return false;
|
|
180
742
|
}
|
|
181
743
|
}
|
|
744
|
+
if (config.publish?.google_play?.enable) {
|
|
745
|
+
const googlePlay = config.publish.google_play;
|
|
746
|
+
const auth = googlePlay.auth;
|
|
747
|
+
if (!googlePlay.packageName || !auth?.serviceAccountJsonPath && !auth?.serviceAccountJsonContent) {
|
|
748
|
+
logger.error(
|
|
749
|
+
"Google Play enabled but missing packageName or service account JSON credentials"
|
|
750
|
+
);
|
|
751
|
+
return false;
|
|
752
|
+
}
|
|
753
|
+
if (googlePlay.track && googlePlay.track !== "internal") {
|
|
754
|
+
logger.error("Google Play first release only supports the internal track");
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
182
758
|
return true;
|
|
183
759
|
}
|
|
184
760
|
};
|
|
@@ -293,12 +869,7 @@ var RetryStrategy = class {
|
|
|
293
869
|
logger.warn(`${operationName} failed (Attempt ${attempt}): ${errorMessage} [${category}]`);
|
|
294
870
|
if (!this.shouldRetry(error, attempt)) {
|
|
295
871
|
logger.error(`${operationName} failed with non-retryable error: ${errorMessage}`);
|
|
296
|
-
|
|
297
|
-
success: false,
|
|
298
|
-
error,
|
|
299
|
-
totalAttempts: attempt,
|
|
300
|
-
lastError: errorMessage
|
|
301
|
-
};
|
|
872
|
+
throw error;
|
|
302
873
|
}
|
|
303
874
|
if (attempt <= this.config.maxRetries) {
|
|
304
875
|
const delayMs = this.calculateDelay(attempt - 1);
|
|
@@ -309,12 +880,7 @@ var RetryStrategy = class {
|
|
|
309
880
|
}
|
|
310
881
|
}
|
|
311
882
|
}
|
|
312
|
-
|
|
313
|
-
success: false,
|
|
314
|
-
error: lastError || new Error(`${operationName} failed after all retries`),
|
|
315
|
-
totalAttempts: attempt,
|
|
316
|
-
lastError: lastError?.message
|
|
317
|
-
};
|
|
883
|
+
throw lastError || new Error(`${operationName} failed after all retries`);
|
|
318
884
|
}
|
|
319
885
|
};
|
|
320
886
|
var DEFAULT_RETRY_CONFIG = {
|
|
@@ -508,6 +1074,7 @@ async function parseHap(filePath) {
|
|
|
508
1074
|
}
|
|
509
1075
|
|
|
510
1076
|
// src/utils/upload-manager.ts
|
|
1077
|
+
var GROUPED_ARTIFACT_PLATFORMS = /* @__PURE__ */ new Set(["huawei", "xiaomi", "oppo", "vivo", "tencent"]);
|
|
511
1078
|
var UploadManager = class {
|
|
512
1079
|
uploaders;
|
|
513
1080
|
config;
|
|
@@ -529,6 +1096,7 @@ var UploadManager = class {
|
|
|
529
1096
|
this.registerUploader("oppo", new OppoUploader());
|
|
530
1097
|
this.registerUploader("vivo", new VivoUploader());
|
|
531
1098
|
this.registerUploader("tencent", new TencentUploader());
|
|
1099
|
+
this.registerUploader("google_play", new GooglePlayUploader());
|
|
532
1100
|
}
|
|
533
1101
|
/**
|
|
534
1102
|
* 注册上传器
|
|
@@ -547,6 +1115,8 @@ var UploadManager = class {
|
|
|
547
1115
|
if (config?.publish?.oppo?.enable) enabled.push("oppo");
|
|
548
1116
|
if (config?.publish?.vivo?.enable) enabled.push("vivo");
|
|
549
1117
|
if (config?.publish?.tencent?.enable) enabled.push("tencent");
|
|
1118
|
+
if (config?.publish?.harmony?.enable) enabled.push("harmony");
|
|
1119
|
+
if (config?.publish?.google_play?.enable) enabled.push("google_play");
|
|
550
1120
|
if (config?.publish?.app_store?.enable) {
|
|
551
1121
|
const mode = config.publish.app_store.mode || "app_store";
|
|
552
1122
|
enabled.push(mode);
|
|
@@ -614,9 +1184,22 @@ var UploadManager = class {
|
|
|
614
1184
|
if (matchedFiles.length === 0) {
|
|
615
1185
|
logger.warn(`[${platform}] No compatible file found in provided files`);
|
|
616
1186
|
} else {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1187
|
+
if (this.shouldGroupArtifacts(platform)) {
|
|
1188
|
+
const artifactSelection = this.resolveArtifactSelection(platform, matchedFiles, metadata);
|
|
1189
|
+
if (!artifactSelection) {
|
|
1190
|
+
logger.warn(`[${platform}] Failed to build grouped artifact selection`);
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
tasks.push({
|
|
1194
|
+
platform,
|
|
1195
|
+
file: this.getRepresentativeFilePath(artifactSelection),
|
|
1196
|
+
artifactSelection
|
|
1197
|
+
});
|
|
1198
|
+
} else {
|
|
1199
|
+
matchedFiles.forEach((file) => {
|
|
1200
|
+
tasks.push({ platform, file, artifactSelection: file });
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
620
1203
|
}
|
|
621
1204
|
});
|
|
622
1205
|
if (tasks.length === 0) {
|
|
@@ -635,7 +1218,13 @@ var UploadManager = class {
|
|
|
635
1218
|
logger.info(`Scheduled ${tasks.length} upload task(s) for ${validPlatforms.length} platform(s)`);
|
|
636
1219
|
const uploadPromises = tasks.map((task) => {
|
|
637
1220
|
logger.info(`[${task.platform}] Matched file: ${import_path2.default.basename(task.file)}`);
|
|
638
|
-
return this.uploadToPlatform(
|
|
1221
|
+
return this.uploadToPlatform(
|
|
1222
|
+
task.platform,
|
|
1223
|
+
task.file,
|
|
1224
|
+
config,
|
|
1225
|
+
metadata,
|
|
1226
|
+
task.artifactSelection
|
|
1227
|
+
);
|
|
639
1228
|
});
|
|
640
1229
|
const results = await Promise.all(uploadPromises);
|
|
641
1230
|
const report = this.aggregateResults(results, tasks.length, startTime);
|
|
@@ -645,7 +1234,7 @@ var UploadManager = class {
|
|
|
645
1234
|
/**
|
|
646
1235
|
* 上传到单个平台
|
|
647
1236
|
*/
|
|
648
|
-
async uploadToPlatform(platform, filePath, config, metadata) {
|
|
1237
|
+
async uploadToPlatform(platform, filePath, config, metadata, artifactSelection = filePath) {
|
|
649
1238
|
if (this.config.cancellationToken?.isCancelled) {
|
|
650
1239
|
logger.warn(`[${platform.toUpperCase()}] Upload skipped due to cancellation`);
|
|
651
1240
|
return {
|
|
@@ -666,25 +1255,30 @@ var UploadManager = class {
|
|
|
666
1255
|
if (this.config.verbose) {
|
|
667
1256
|
logger.info(`[${platform.toUpperCase()}] Starting upload...`);
|
|
668
1257
|
}
|
|
669
|
-
const
|
|
670
|
-
|
|
1258
|
+
const effectiveMetadata = {
|
|
1259
|
+
...metadata,
|
|
1260
|
+
artifactPath: artifactSelection
|
|
1261
|
+
};
|
|
1262
|
+
await this.validateArtifactSelection(platform, artifactSelection, effectiveMetadata);
|
|
1263
|
+
const pkgInfo = import_path2.default.extname(filePath).toLowerCase() === ".aab" ? null : await parsePackageInfo(filePath, {
|
|
1264
|
+
androidSdkPath: effectiveMetadata.globalSettings?.androidSdkPath
|
|
671
1265
|
});
|
|
672
1266
|
if (pkgInfo) {
|
|
673
1267
|
if (pkgInfo.versionName) {
|
|
674
|
-
|
|
1268
|
+
effectiveMetadata.version = pkgInfo.versionName;
|
|
675
1269
|
}
|
|
676
1270
|
if (pkgInfo.versionCode) {
|
|
677
|
-
|
|
1271
|
+
effectiveMetadata.buildNumber = pkgInfo.versionCode;
|
|
678
1272
|
}
|
|
679
1273
|
if (pkgInfo.packageName) {
|
|
680
1274
|
;
|
|
681
|
-
|
|
1275
|
+
effectiveMetadata.packageName = pkgInfo.packageName;
|
|
682
1276
|
}
|
|
683
1277
|
logger.info(
|
|
684
|
-
`[${platform.toUpperCase()}] \u5DF2\u6821\u51C6\u7248\u672C\u4FE1\u606F: v${
|
|
1278
|
+
`[${platform.toUpperCase()}] \u5DF2\u6821\u51C6\u7248\u672C\u4FE1\u606F: v${effectiveMetadata.version}+${effectiveMetadata.buildNumber}`
|
|
685
1279
|
);
|
|
686
1280
|
}
|
|
687
|
-
const result = await uploader.upload(filePath, config,
|
|
1281
|
+
const result = await uploader.upload(filePath, config, effectiveMetadata);
|
|
688
1282
|
return result;
|
|
689
1283
|
} catch (error) {
|
|
690
1284
|
const errorMessage = error.message || String(error);
|
|
@@ -697,6 +1291,85 @@ var UploadManager = class {
|
|
|
697
1291
|
};
|
|
698
1292
|
}
|
|
699
1293
|
}
|
|
1294
|
+
shouldGroupArtifacts(platform) {
|
|
1295
|
+
return GROUPED_ARTIFACT_PLATFORMS.has(platform);
|
|
1296
|
+
}
|
|
1297
|
+
resolveArtifactSelection(platform, matchedFiles, metadata) {
|
|
1298
|
+
const explicitArtifacts = metadata.platformArtifacts?.[platform];
|
|
1299
|
+
if (explicitArtifacts) {
|
|
1300
|
+
if (typeof explicitArtifacts === "string") {
|
|
1301
|
+
return explicitArtifacts;
|
|
1302
|
+
}
|
|
1303
|
+
if (Object.keys(explicitArtifacts).length > 0) {
|
|
1304
|
+
return explicitArtifacts;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (matchedFiles.length === 0) {
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1310
|
+
if (matchedFiles.length === 1) {
|
|
1311
|
+
return matchedFiles[0];
|
|
1312
|
+
}
|
|
1313
|
+
return this.buildArtifactMapFromFiles(matchedFiles);
|
|
1314
|
+
}
|
|
1315
|
+
buildArtifactMapFromFiles(files) {
|
|
1316
|
+
return files.reduce(
|
|
1317
|
+
(acc, filePath, index) => {
|
|
1318
|
+
acc[this.inferArtifactKey(filePath, index)] = filePath;
|
|
1319
|
+
return acc;
|
|
1320
|
+
},
|
|
1321
|
+
{}
|
|
1322
|
+
);
|
|
1323
|
+
}
|
|
1324
|
+
inferArtifactKey(filePath, index) {
|
|
1325
|
+
const fileName = import_path2.default.basename(filePath).toLowerCase();
|
|
1326
|
+
if (fileName.includes("arm64-v8a")) return "arm64-v8a";
|
|
1327
|
+
if (fileName.includes("armeabi-v7a")) return "armeabi-v7a";
|
|
1328
|
+
if (fileName.includes("x86_64")) return "x86_64";
|
|
1329
|
+
if (fileName.includes("universal")) return "universal";
|
|
1330
|
+
return `artifact_${index + 1}`;
|
|
1331
|
+
}
|
|
1332
|
+
getRepresentativeFilePath(artifactSelection) {
|
|
1333
|
+
if (typeof artifactSelection === "string") {
|
|
1334
|
+
return artifactSelection;
|
|
1335
|
+
}
|
|
1336
|
+
return Object.values(artifactSelection)[0];
|
|
1337
|
+
}
|
|
1338
|
+
async validateArtifactSelection(platform, artifactSelection, metadata) {
|
|
1339
|
+
if (typeof artifactSelection === "string") {
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
const artifactPaths = Object.values(artifactSelection).filter(Boolean);
|
|
1343
|
+
if (artifactPaths.length < 2) {
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
const parsedInfos = await Promise.all(
|
|
1347
|
+
artifactPaths.map(
|
|
1348
|
+
(artifactPath) => parsePackageInfo(artifactPath, {
|
|
1349
|
+
androidSdkPath: metadata.globalSettings?.androidSdkPath
|
|
1350
|
+
})
|
|
1351
|
+
)
|
|
1352
|
+
);
|
|
1353
|
+
const availableInfos = parsedInfos.filter((info) => !!info);
|
|
1354
|
+
if (availableInfos.length < 2) {
|
|
1355
|
+
logger.warn(
|
|
1356
|
+
`[${platform.toUpperCase()}] Unable to fully validate multi-artifact package info; continuing with upload.`
|
|
1357
|
+
);
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
const [baseInfo, ...restInfos] = availableInfos;
|
|
1361
|
+
for (const info of restInfos) {
|
|
1362
|
+
if (baseInfo?.packageName && info?.packageName && baseInfo.packageName !== info.packageName) {
|
|
1363
|
+
throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 packageName \u4E0D\u4E00\u81F4");
|
|
1364
|
+
}
|
|
1365
|
+
if (baseInfo?.versionName && info?.versionName && baseInfo.versionName !== info.versionName) {
|
|
1366
|
+
throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 versionName \u4E0D\u4E00\u81F4");
|
|
1367
|
+
}
|
|
1368
|
+
if (baseInfo?.versionCode && info?.versionCode && baseInfo.versionCode !== info.versionCode) {
|
|
1369
|
+
throw new Error("\u53CC\u5305\u4E0A\u4F20\u5931\u8D25\uFF1A\u6240\u9009\u5B89\u88C5\u5305\u7684 versionCode \u4E0D\u4E00\u81F4");
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
700
1373
|
/**
|
|
701
1374
|
* 聚合上传结果
|
|
702
1375
|
*/
|
|
@@ -944,6 +1617,280 @@ var AppleJWTGenerator = class _AppleJWTGenerator {
|
|
|
944
1617
|
}
|
|
945
1618
|
};
|
|
946
1619
|
|
|
1620
|
+
// src/utils/release-receipt.ts
|
|
1621
|
+
var import_crypto3 = require("crypto");
|
|
1622
|
+
var import_fs2 = require("fs");
|
|
1623
|
+
var import_promises = require("fs/promises");
|
|
1624
|
+
var import_os = require("os");
|
|
1625
|
+
var import_path3 = __toESM(require("path"), 1);
|
|
1626
|
+
var RELEASE_RECEIPT_SCHEMA_VERSION = 1;
|
|
1627
|
+
var DEFAULT_RELEASE_RECEIPT_LIMIT = 100;
|
|
1628
|
+
var REMOTE_IDENTIFIER_KEYS = [
|
|
1629
|
+
"id",
|
|
1630
|
+
"appId",
|
|
1631
|
+
"buildId",
|
|
1632
|
+
"versionId",
|
|
1633
|
+
"releaseId",
|
|
1634
|
+
"editId",
|
|
1635
|
+
"versionCode",
|
|
1636
|
+
"track",
|
|
1637
|
+
"apiTrack",
|
|
1638
|
+
"status",
|
|
1639
|
+
"committed"
|
|
1640
|
+
];
|
|
1641
|
+
function resolveReleaseProjectRoot(projectRoot, configPath) {
|
|
1642
|
+
return import_path3.default.resolve(projectRoot || import_path3.default.dirname(import_path3.default.resolve(configPath)));
|
|
1643
|
+
}
|
|
1644
|
+
function sanitizeReleaseMessage(value, maxLength = 500) {
|
|
1645
|
+
let message = typeof value === "string" ? value : String(value ?? "");
|
|
1646
|
+
message = message.replace(/-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi, "[\u5DF2\u8131\u654F]").replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [\u5DF2\u8131\u654F]").replace(
|
|
1647
|
+
/(["']?(?:access[_-]?token|refresh[_-]?token|client[_-]?secret|private[_-]?key|api[_-]?key|app[_-]?password|authorization|credential|service[_-]?account(?:[_-]?json)?[_-]?content)["']?\s*[:=]\s*)["']?[^\s,"'}]+["']?/gi,
|
|
1648
|
+
"$1[\u5DF2\u8131\u654F]"
|
|
1649
|
+
).replace(/[\r\n\t]+/g, " ").replace(/\s{2,}/g, " ").trim();
|
|
1650
|
+
return message.length > maxLength ? `${message.slice(0, maxLength - 1)}\u2026` : message;
|
|
1651
|
+
}
|
|
1652
|
+
function createUploadTargets(results) {
|
|
1653
|
+
return results.map((result) => {
|
|
1654
|
+
const platform = sanitizeReleaseMessage(result.platform, 80) || "unknown";
|
|
1655
|
+
const data = isRecord(result.data) ? result.data : void 0;
|
|
1656
|
+
const isDraft = result.success && platform === "google_play" && String(data?.status || "").toLowerCase() === "draft";
|
|
1657
|
+
const remote = createRemoteReceipt(result, data);
|
|
1658
|
+
return {
|
|
1659
|
+
platform,
|
|
1660
|
+
state: result.success ? isDraft ? "draft_created" : "uploaded" : "failed",
|
|
1661
|
+
outcome: result.success ? "success" : "failed",
|
|
1662
|
+
message: sanitizeReleaseMessage(result.message) || (result.success ? "\u4E0A\u4F20\u6210\u529F" : "\u4E0A\u4F20\u5931\u8D25"),
|
|
1663
|
+
...remote ? { remote } : {}
|
|
1664
|
+
};
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
async function createReleaseReceipt(input) {
|
|
1668
|
+
const projectRoot = await canonicalizeProjectRoot(input.projectRoot);
|
|
1669
|
+
const startedAt = toDate(input.startedAt ?? Date.now());
|
|
1670
|
+
const finishedAt = toDate(input.finishedAt ?? Date.now());
|
|
1671
|
+
const targets = input.targets.map(sanitizeTarget);
|
|
1672
|
+
return {
|
|
1673
|
+
schemaVersion: RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
1674
|
+
id: (0, import_crypto3.randomUUID)(),
|
|
1675
|
+
projectKey: createProjectKey(projectRoot),
|
|
1676
|
+
projectRoot,
|
|
1677
|
+
kind: input.kind,
|
|
1678
|
+
...cleanOptionalText(input.appName, 160) ? { appName: cleanOptionalText(input.appName, 160) } : {},
|
|
1679
|
+
...cleanOptionalText(input.packageName, 240) ? { packageName: cleanOptionalText(input.packageName, 240) } : {},
|
|
1680
|
+
...cleanOptionalText(input.versionName, 80) ? { versionName: cleanOptionalText(input.versionName, 80) } : {},
|
|
1681
|
+
...input.buildNumber !== void 0 ? { buildNumber: cleanOptionalText(String(input.buildNumber), 80) } : {},
|
|
1682
|
+
...cleanOptionalText(input.changelog, 4e3) ? { changelog: cleanOptionalText(input.changelog, 4e3) } : {},
|
|
1683
|
+
artifacts: await inspectArtifacts(input.artifactPaths),
|
|
1684
|
+
targets,
|
|
1685
|
+
outcome: input.outcome ?? inferOutcome(targets),
|
|
1686
|
+
startedAt: startedAt.toISOString(),
|
|
1687
|
+
finishedAt: finishedAt.toISOString(),
|
|
1688
|
+
durationMs: Math.max(0, finishedAt.getTime() - startedAt.getTime())
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
function formatReleaseReceipt(receipt) {
|
|
1692
|
+
const lines = [
|
|
1693
|
+
`${receipt.appName || receipt.packageName || "\u5E94\u7528"} \xB7 ${receipt.kind === "build" ? "\u6784\u5EFA\u56DE\u6267" : "\u4E0A\u4F20\u56DE\u6267"}`,
|
|
1694
|
+
`\u7ED3\u679C\uFF1A${receipt.outcome}`,
|
|
1695
|
+
`\u7248\u672C\uFF1A${receipt.versionName || "-"} (${receipt.buildNumber || "-"})`,
|
|
1696
|
+
`\u5B8C\u6210\u65F6\u95F4\uFF1A${receipt.finishedAt}`
|
|
1697
|
+
];
|
|
1698
|
+
if (receipt.packageName) lines.push(`\u5305\u540D\uFF1A${receipt.packageName}`);
|
|
1699
|
+
if (receipt.changelog) lines.push(`\u66F4\u65B0\u65E5\u5FD7\uFF1A${receipt.changelog}`);
|
|
1700
|
+
for (const artifact of receipt.artifacts) {
|
|
1701
|
+
lines.push(
|
|
1702
|
+
`\u4EA7\u7269\uFF1A${artifact.path}${artifact.sizeBytes !== void 0 ? ` \xB7 ${artifact.sizeBytes} bytes` : ""}${artifact.sha256 ? ` \xB7 SHA-256 ${artifact.sha256}` : ""}`
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1705
|
+
for (const target of receipt.targets) {
|
|
1706
|
+
lines.push(`${target.platform}\uFF1A${target.state} \xB7 ${target.message}`);
|
|
1707
|
+
if (target.remote?.consoleUrl) lines.push(`\u540E\u53F0\uFF1A${target.remote.consoleUrl}`);
|
|
1708
|
+
}
|
|
1709
|
+
return lines.join("\n");
|
|
1710
|
+
}
|
|
1711
|
+
var ReleaseReceiptStore = class {
|
|
1712
|
+
storageRoot;
|
|
1713
|
+
maxEntries;
|
|
1714
|
+
constructor(options = {}) {
|
|
1715
|
+
this.storageRoot = import_path3.default.resolve(options.storageRoot || import_path3.default.join((0, import_os.homedir)(), ".flu-cli", "release-receipts"));
|
|
1716
|
+
this.maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_RELEASE_RECEIPT_LIMIT);
|
|
1717
|
+
}
|
|
1718
|
+
async append(receipt) {
|
|
1719
|
+
await (0, import_promises.mkdir)(this.storageRoot, { recursive: true, mode: 448 });
|
|
1720
|
+
await (0, import_promises.chmod)(this.storageRoot, 448);
|
|
1721
|
+
const filePath = this.getFilePath(receipt.projectKey);
|
|
1722
|
+
const current = await this.readFile(filePath, receipt.projectKey);
|
|
1723
|
+
const receipts = [receipt, ...current.receipts.filter((item) => item.id !== receipt.id)].slice(
|
|
1724
|
+
0,
|
|
1725
|
+
this.maxEntries
|
|
1726
|
+
);
|
|
1727
|
+
const tempPath = `${filePath}.${process.pid}.${(0, import_crypto3.randomUUID)()}.tmp`;
|
|
1728
|
+
await (0, import_promises.writeFile)(
|
|
1729
|
+
tempPath,
|
|
1730
|
+
`${JSON.stringify({
|
|
1731
|
+
schemaVersion: RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
1732
|
+
projectKey: receipt.projectKey,
|
|
1733
|
+
receipts
|
|
1734
|
+
}, null, 2)}
|
|
1735
|
+
`,
|
|
1736
|
+
{ encoding: "utf8", mode: 384 }
|
|
1737
|
+
);
|
|
1738
|
+
await (0, import_promises.rename)(tempPath, filePath);
|
|
1739
|
+
await (0, import_promises.chmod)(filePath, 384);
|
|
1740
|
+
}
|
|
1741
|
+
async list(projectRoot, limit = 20) {
|
|
1742
|
+
const canonicalRoot = await canonicalizeProjectRoot(projectRoot);
|
|
1743
|
+
const projectKey = createProjectKey(canonicalRoot);
|
|
1744
|
+
const data = await this.readFile(this.getFilePath(projectKey), projectKey);
|
|
1745
|
+
return data.receipts.slice(0, Math.max(0, limit));
|
|
1746
|
+
}
|
|
1747
|
+
async save(input) {
|
|
1748
|
+
const receipt = await createReleaseReceipt(input);
|
|
1749
|
+
await this.append(receipt);
|
|
1750
|
+
return receipt;
|
|
1751
|
+
}
|
|
1752
|
+
getFilePath(projectKey) {
|
|
1753
|
+
return import_path3.default.join(this.storageRoot, `${projectKey}.json`);
|
|
1754
|
+
}
|
|
1755
|
+
async readFile(filePath, projectKey) {
|
|
1756
|
+
try {
|
|
1757
|
+
const raw = JSON.parse(await (0, import_promises.readFile)(filePath, "utf8"));
|
|
1758
|
+
return {
|
|
1759
|
+
schemaVersion: RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
1760
|
+
projectKey,
|
|
1761
|
+
receipts: Array.isArray(raw.receipts) ? raw.receipts : []
|
|
1762
|
+
};
|
|
1763
|
+
} catch (error) {
|
|
1764
|
+
if (error?.code === "ENOENT") {
|
|
1765
|
+
return { schemaVersion: RELEASE_RECEIPT_SCHEMA_VERSION, projectKey, receipts: [] };
|
|
1766
|
+
}
|
|
1767
|
+
throw new Error(`\u8BFB\u53D6\u53D1\u5E03\u56DE\u6267\u5931\u8D25: ${sanitizeReleaseMessage(error?.message || error)}`);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
function createProjectKey(projectRoot) {
|
|
1772
|
+
return (0, import_crypto3.createHash)("sha256").update(import_path3.default.resolve(projectRoot)).digest("hex").slice(0, 24);
|
|
1773
|
+
}
|
|
1774
|
+
function createRemoteReceipt(result, data) {
|
|
1775
|
+
const identifiers = {};
|
|
1776
|
+
for (const key of REMOTE_IDENTIFIER_KEYS) {
|
|
1777
|
+
const value = data?.[key];
|
|
1778
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1779
|
+
identifiers[key] = typeof value === "string" ? sanitizeReleaseMessage(value, 240) : value;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
const consoleUrl = cleanHttpUrl(result.consoleUrl);
|
|
1783
|
+
const downloadUrl = cleanHttpUrl(result.url || result.link);
|
|
1784
|
+
if (!consoleUrl && !downloadUrl && Object.keys(identifiers).length === 0) return void 0;
|
|
1785
|
+
return {
|
|
1786
|
+
...consoleUrl ? { consoleUrl } : {},
|
|
1787
|
+
...downloadUrl ? { downloadUrl } : {},
|
|
1788
|
+
...Object.keys(identifiers).length > 0 ? { identifiers } : {}
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function sanitizeTarget(target) {
|
|
1792
|
+
return {
|
|
1793
|
+
platform: sanitizeReleaseMessage(target.platform, 80) || "unknown",
|
|
1794
|
+
state: target.state,
|
|
1795
|
+
outcome: target.outcome,
|
|
1796
|
+
message: sanitizeReleaseMessage(target.message),
|
|
1797
|
+
...target.remote ? { remote: sanitizeRemote(target.remote) } : {},
|
|
1798
|
+
...cleanOptionalText(target.lastKnownRemoteStatus, 120) ? { lastKnownRemoteStatus: cleanOptionalText(target.lastKnownRemoteStatus, 120) } : {},
|
|
1799
|
+
...cleanOptionalText(target.checkedAt, 80) ? { checkedAt: cleanOptionalText(target.checkedAt, 80) } : {}
|
|
1800
|
+
};
|
|
1801
|
+
}
|
|
1802
|
+
function sanitizeRemote(remote) {
|
|
1803
|
+
const identifiers = {};
|
|
1804
|
+
for (const [key, value] of Object.entries(remote.identifiers || {})) {
|
|
1805
|
+
if (!REMOTE_IDENTIFIER_KEYS.includes(key)) continue;
|
|
1806
|
+
if (typeof value === "string") identifiers[key] = sanitizeReleaseMessage(value, 240);
|
|
1807
|
+
if (typeof value === "number" || typeof value === "boolean") identifiers[key] = value;
|
|
1808
|
+
}
|
|
1809
|
+
const consoleUrl = cleanHttpUrl(remote.consoleUrl);
|
|
1810
|
+
const downloadUrl = cleanHttpUrl(remote.downloadUrl);
|
|
1811
|
+
return {
|
|
1812
|
+
...consoleUrl ? { consoleUrl } : {},
|
|
1813
|
+
...downloadUrl ? { downloadUrl } : {},
|
|
1814
|
+
...Object.keys(identifiers).length > 0 ? { identifiers } : {}
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
async function inspectArtifacts(value) {
|
|
1818
|
+
const paths = Array.from(new Set(flattenArtifactPaths(value).map((item) => import_path3.default.resolve(item))));
|
|
1819
|
+
return Promise.all(paths.map(inspectArtifact));
|
|
1820
|
+
}
|
|
1821
|
+
async function inspectArtifact(filePath) {
|
|
1822
|
+
const base = { path: filePath, name: import_path3.default.basename(filePath) };
|
|
1823
|
+
try {
|
|
1824
|
+
const info = await (0, import_promises.stat)(filePath);
|
|
1825
|
+
if (!info.isFile()) return { ...base, available: false };
|
|
1826
|
+
return {
|
|
1827
|
+
...base,
|
|
1828
|
+
sizeBytes: info.size,
|
|
1829
|
+
sha256: await hashFile(filePath),
|
|
1830
|
+
available: true
|
|
1831
|
+
};
|
|
1832
|
+
} catch {
|
|
1833
|
+
return { ...base, available: false };
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
function hashFile(filePath) {
|
|
1837
|
+
return new Promise((resolve, reject) => {
|
|
1838
|
+
const hash = (0, import_crypto3.createHash)("sha256");
|
|
1839
|
+
const stream = (0, import_fs2.createReadStream)(filePath);
|
|
1840
|
+
stream.on("error", reject);
|
|
1841
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
1842
|
+
stream.on("end", () => resolve(hash.digest("hex")));
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
function flattenArtifactPaths(value) {
|
|
1846
|
+
if (typeof value === "string" && value.trim()) return [value];
|
|
1847
|
+
if (Array.isArray(value)) return value.flatMap(flattenArtifactPaths);
|
|
1848
|
+
if (isRecord(value)) return Object.values(value).flatMap(flattenArtifactPaths);
|
|
1849
|
+
return [];
|
|
1850
|
+
}
|
|
1851
|
+
function inferOutcome(targets) {
|
|
1852
|
+
if (targets.length === 0) return "failed";
|
|
1853
|
+
if (targets.every((target) => target.outcome === "cancelled")) return "cancelled";
|
|
1854
|
+
const successCount = targets.filter((target) => target.outcome === "success").length;
|
|
1855
|
+
if (successCount === targets.length) return "success";
|
|
1856
|
+
if (successCount > 0) return "partial";
|
|
1857
|
+
return "failed";
|
|
1858
|
+
}
|
|
1859
|
+
async function canonicalizeProjectRoot(projectRoot) {
|
|
1860
|
+
const resolved = import_path3.default.resolve(projectRoot);
|
|
1861
|
+
try {
|
|
1862
|
+
return await (0, import_promises.realpath)(resolved);
|
|
1863
|
+
} catch {
|
|
1864
|
+
return resolved;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
function toDate(value) {
|
|
1868
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
1869
|
+
return Number.isNaN(date.getTime()) ? /* @__PURE__ */ new Date() : date;
|
|
1870
|
+
}
|
|
1871
|
+
function cleanOptionalText(value, maxLength) {
|
|
1872
|
+
const cleaned = sanitizeReleaseMessage(value, maxLength);
|
|
1873
|
+
return cleaned || void 0;
|
|
1874
|
+
}
|
|
1875
|
+
function cleanHttpUrl(value) {
|
|
1876
|
+
if (typeof value !== "string") return void 0;
|
|
1877
|
+
try {
|
|
1878
|
+
const url = new URL(value);
|
|
1879
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return void 0;
|
|
1880
|
+
for (const key of Array.from(url.searchParams.keys())) {
|
|
1881
|
+
if (/token|secret|password|signature|credential|key/i.test(key)) {
|
|
1882
|
+
url.searchParams.set(key, "[\u5DF2\u8131\u654F]");
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
return url.toString();
|
|
1886
|
+
} catch {
|
|
1887
|
+
return void 0;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
function isRecord(value) {
|
|
1891
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1892
|
+
}
|
|
1893
|
+
|
|
947
1894
|
// src/adapters/pgyer_uploader.ts
|
|
948
1895
|
var PgyerUploader = class _PgyerUploader {
|
|
949
1896
|
name = "Pgyer";
|
|
@@ -1062,7 +2009,7 @@ var PgyerUploader = class _PgyerUploader {
|
|
|
1062
2009
|
|
|
1063
2010
|
// src/adapters/huawei_uploader.ts
|
|
1064
2011
|
var import_fs_extra4 = __toESM(require("fs-extra"), 1);
|
|
1065
|
-
var
|
|
2012
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1066
2013
|
var import_util = require("util");
|
|
1067
2014
|
var import_axios2 = __toESM(require("axios"), 1);
|
|
1068
2015
|
var import_form_data2 = __toESM(require("form-data"), 1);
|
|
@@ -1141,7 +2088,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1141
2088
|
const clientSecret = hwConfig.auth.clientSecret;
|
|
1142
2089
|
const appId = hwConfig.appId;
|
|
1143
2090
|
const releaseType = hwConfig.releaseType || "draft";
|
|
1144
|
-
const suffix =
|
|
2091
|
+
const suffix = import_path4.default.extname(filePath).substring(1);
|
|
1145
2092
|
const retryConfig = config.retry;
|
|
1146
2093
|
const tokenUrl = this.getTokenUrl(config);
|
|
1147
2094
|
const publishApiRoot = this.getApiRoot(config);
|
|
@@ -1162,27 +2109,39 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1162
2109
|
clientId,
|
|
1163
2110
|
appId
|
|
1164
2111
|
};
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
2112
|
+
const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || metadata.artifactPath || "" };
|
|
2113
|
+
logger.info(`[Huawei] \u5904\u7406\u4EA7\u7269\u6570\u91CF: ${Object.keys(artifacts).length}`);
|
|
2114
|
+
const uploadResults = [];
|
|
2115
|
+
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
2116
|
+
if (!p || !import_fs_extra4.default.existsSync(p)) return;
|
|
2117
|
+
const currentSuffix = import_path4.default.extname(p).substring(1);
|
|
2118
|
+
logger.info(`[Huawei] \u6B63\u5728\u4E0A\u4F20 ${abi} \u5305: ${import_path4.default.basename(p)}`);
|
|
2119
|
+
const uploadInfo = await retryStrategy.execute(
|
|
2120
|
+
() => this.getUploadUrl(ctx, currentSuffix),
|
|
2121
|
+
`Huawei get upload URL for ${abi}`
|
|
2122
|
+
);
|
|
2123
|
+
if (!uploadInfo.success || !uploadInfo.data) {
|
|
2124
|
+
throw uploadInfo.error || new Error(`Failed to get upload URL for ${abi}`);
|
|
2125
|
+
}
|
|
2126
|
+
const uploadFileRes = await retryStrategy.execute(
|
|
2127
|
+
() => this.executeUploadRequest(uploadInfo.data.uploadUrl, uploadInfo.data.authCode, p),
|
|
2128
|
+
`Huawei upload file for ${abi}`
|
|
2129
|
+
);
|
|
2130
|
+
if (!uploadFileRes.success || !uploadFileRes.data || uploadFileRes.data.length === 0) {
|
|
2131
|
+
throw uploadFileRes.error || new Error(`File upload failed for ${abi}`);
|
|
2132
|
+
}
|
|
2133
|
+
uploadResults.push({
|
|
2134
|
+
fileName: import_path4.default.basename(p),
|
|
2135
|
+
fileDestUrl: uploadFileRes.data[0].fileDestUrl
|
|
2136
|
+
});
|
|
2137
|
+
});
|
|
2138
|
+
await Promise.all(uploadPromises);
|
|
2139
|
+
if (uploadResults.length === 0) {
|
|
2140
|
+
throw new Error("No files were successfully uploaded");
|
|
1181
2141
|
}
|
|
1182
|
-
|
|
1183
|
-
logger.info("Linking file to app...");
|
|
2142
|
+
logger.info(`[Huawei] \u6B63\u5728\u5173\u8054 ${uploadResults.length} \u4E2A\u6587\u4EF6\u5230\u5E94\u7528...`);
|
|
1184
2143
|
const updateInfoResult = await retryStrategy.execute(
|
|
1185
|
-
() => this.updateAppFileInfo(ctx,
|
|
2144
|
+
() => this.updateAppFileInfo(ctx, uploadResults, filePath),
|
|
1186
2145
|
"Huawei update file info"
|
|
1187
2146
|
);
|
|
1188
2147
|
if (!updateInfoResult.success) {
|
|
@@ -1344,7 +2303,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1344
2303
|
}
|
|
1345
2304
|
/** 通用文件上传(FormData 方式,无需 API 认证头) */
|
|
1346
2305
|
async executeUploadRequest(uploadUrl, authCode, filePath) {
|
|
1347
|
-
const fileName =
|
|
2306
|
+
const fileName = import_path4.default.basename(filePath);
|
|
1348
2307
|
const fileSize = import_fs_extra4.default.statSync(filePath).size;
|
|
1349
2308
|
const form = new import_form_data2.default();
|
|
1350
2309
|
form.append("file", import_fs_extra4.default.createReadStream(filePath), {
|
|
@@ -1386,14 +2345,17 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1386
2345
|
throw new Error("Upload succeeded but fileDestUrl not found in response");
|
|
1387
2346
|
}
|
|
1388
2347
|
/** 更新应用文件信息 */
|
|
1389
|
-
async updateAppFileInfo(ctx,
|
|
1390
|
-
const
|
|
1391
|
-
const fileType =
|
|
2348
|
+
async updateAppFileInfo(ctx, files, anyFilePath) {
|
|
2349
|
+
const hasAab = files.some((f) => f.fileName.toLowerCase().endsWith(".aab"));
|
|
2350
|
+
const fileType = hasAab ? 7 /* AAB */ : 5 /* APP_PACKAGE */;
|
|
1392
2351
|
const res = await import_axios2.default.put(
|
|
1393
2352
|
`${ctx.apiRoot}/app-file-info`,
|
|
1394
2353
|
{
|
|
1395
2354
|
fileType,
|
|
1396
|
-
files:
|
|
2355
|
+
files: files.map((f) => ({
|
|
2356
|
+
fileName: f.fileName,
|
|
2357
|
+
fileDestUrl: f.fileDestUrl
|
|
2358
|
+
}))
|
|
1397
2359
|
},
|
|
1398
2360
|
{
|
|
1399
2361
|
params: { appId: ctx.appId },
|
|
@@ -1508,7 +2470,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1508
2470
|
logger.warn(`Asset file not found, skipping: ${filePath}`);
|
|
1509
2471
|
continue;
|
|
1510
2472
|
}
|
|
1511
|
-
const suffix =
|
|
2473
|
+
const suffix = import_path4.default.extname(filePath).substring(1).toLowerCase();
|
|
1512
2474
|
if (!HUAWEI_SUPPORTED_SUFFIXES.image.includes(suffix)) {
|
|
1513
2475
|
logger.warn(`Unsupported file type ${suffix}, skipping: ${filePath}`);
|
|
1514
2476
|
continue;
|
|
@@ -1563,9 +2525,10 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1563
2525
|
|
|
1564
2526
|
// src/adapters/xiaomi_uploader.ts
|
|
1565
2527
|
var import_fs_extra5 = __toESM(require("fs-extra"), 1);
|
|
2528
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
1566
2529
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
1567
2530
|
var import_form_data3 = __toESM(require("form-data"), 1);
|
|
1568
|
-
var
|
|
2531
|
+
var import_crypto4 = __toESM(require("crypto"), 1);
|
|
1569
2532
|
var XiaomiUploader = class _XiaomiUploader {
|
|
1570
2533
|
name = "Xiaomi";
|
|
1571
2534
|
static API_BASE = "https://api.developer.xiaomi.com/devupload";
|
|
@@ -1585,10 +2548,10 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
1585
2548
|
const chunks = [];
|
|
1586
2549
|
for (let offset = 0; offset < buffer.length; offset += maxBlock) {
|
|
1587
2550
|
const chunk = buffer.subarray(offset, offset + maxBlock);
|
|
1588
|
-
const encrypted =
|
|
2551
|
+
const encrypted = import_crypto4.default.publicEncrypt(
|
|
1589
2552
|
{
|
|
1590
2553
|
key: publicKey,
|
|
1591
|
-
padding:
|
|
2554
|
+
padding: import_crypto4.default.constants.RSA_PKCS1_PADDING
|
|
1592
2555
|
},
|
|
1593
2556
|
chunk
|
|
1594
2557
|
);
|
|
@@ -1602,15 +2565,15 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
1602
2565
|
*/
|
|
1603
2566
|
extractPublicKey(buffer) {
|
|
1604
2567
|
try {
|
|
1605
|
-
const x509 = new
|
|
2568
|
+
const x509 = new import_crypto4.default.X509Certificate(buffer);
|
|
1606
2569
|
return x509.publicKey;
|
|
1607
2570
|
} catch (e) {
|
|
1608
2571
|
try {
|
|
1609
2572
|
const content = buffer.toString();
|
|
1610
2573
|
if (content.includes("-----BEGIN")) {
|
|
1611
|
-
return
|
|
2574
|
+
return import_crypto4.default.createPublicKey(content);
|
|
1612
2575
|
}
|
|
1613
|
-
return
|
|
2576
|
+
return import_crypto4.default.createPublicKey(this.normalizePEM(content));
|
|
1614
2577
|
} catch (e2) {
|
|
1615
2578
|
throw new Error(`\u65E0\u6CD5\u63D0\u53D6\u516C\u94A5: ${e instanceof Error ? e.message : "\u672A\u77E5\u9519\u8BEF"}`);
|
|
1616
2579
|
}
|
|
@@ -1621,7 +2584,7 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
1621
2584
|
*/
|
|
1622
2585
|
async calculateFileMD5(filePath) {
|
|
1623
2586
|
return new Promise((resolve, reject) => {
|
|
1624
|
-
const hash =
|
|
2587
|
+
const hash = import_crypto4.default.createHash("md5");
|
|
1625
2588
|
const stream = import_fs_extra5.default.createReadStream(filePath);
|
|
1626
2589
|
stream.on("data", (data) => hash.update(data));
|
|
1627
2590
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -1632,7 +2595,7 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
1632
2595
|
* 计算字符串 MD5 (小写十六进制)
|
|
1633
2596
|
*/
|
|
1634
2597
|
calculateStringMD5(str) {
|
|
1635
|
-
return
|
|
2598
|
+
return import_crypto4.default.createHash("md5").update(str, "utf-8").digest("hex");
|
|
1636
2599
|
}
|
|
1637
2600
|
/**
|
|
1638
2601
|
* 构建 RequestData JSON
|
|
@@ -1813,80 +2776,39 @@ ${lines.join("\n")}
|
|
|
1813
2776
|
const xiaomiConfig = config.publish?.xiaomi;
|
|
1814
2777
|
const { userName, privateKey, publicKeyPath } = xiaomiConfig.auth;
|
|
1815
2778
|
const updateDesc = metadata.changelog || xiaomiConfig?.description || "App Update";
|
|
1816
|
-
const
|
|
1817
|
-
if (!import_fs_extra5.default.existsSync(filePath)) {
|
|
1818
|
-
return {
|
|
1819
|
-
platform: "xiaomi",
|
|
1820
|
-
success: false,
|
|
1821
|
-
message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
2779
|
+
const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
|
|
1824
2780
|
const resolvedPublicKey = await this.resolvePublicKey(publicKeyPath);
|
|
1825
2781
|
if (!resolvedPublicKey) {
|
|
1826
|
-
|
|
1827
|
-
platform: "xiaomi",
|
|
1828
|
-
success: false,
|
|
1829
|
-
message: "\u65E0\u6CD5\u83B7\u53D6 RSA \u516C\u94A5: publicKeyPath \u672A\u63D0\u4F9B\u6216\u6587\u4EF6\u4E0D\u5B58\u5728"
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
const packageName = xiaomiConfig.packageName || this.inferPackageName(filePath);
|
|
1833
|
-
if (!packageName) {
|
|
1834
|
-
return {
|
|
1835
|
-
platform: "xiaomi",
|
|
1836
|
-
success: false,
|
|
1837
|
-
message: "\u7F3A\u5C11 packageName \u914D\u7F6E, \u4E14\u65E0\u6CD5\u4ECE\u6587\u4EF6\u540D\u63A8\u65AD\u5E94\u7528\u5305\u540D"
|
|
1838
|
-
};
|
|
1839
|
-
}
|
|
1840
|
-
const appName = xiaomiConfig.appName || this.inferAppName(packageName);
|
|
1841
|
-
if (!appName) {
|
|
1842
|
-
return {
|
|
1843
|
-
platform: "xiaomi",
|
|
1844
|
-
success: false,
|
|
1845
|
-
message: "\u7F3A\u5C11 appName \u914D\u7F6E, \u4E14\u65E0\u6CD5\u4ECE\u5305\u540D\u63A8\u65AD\u5E94\u7528\u540D\u79F0"
|
|
1846
|
-
};
|
|
2782
|
+
throw new Error("\u65E0\u6CD5\u83B7\u53D6 RSA \u516C\u94A5");
|
|
1847
2783
|
}
|
|
1848
|
-
logger.info("\u5F00\u59CB\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97...");
|
|
1849
|
-
logger.info(`\u6587\u4EF6: ${filePath}`);
|
|
1850
|
-
logger.info(`\u5305\u540D: ${packageName}`);
|
|
1851
|
-
logger.info(`\u5E94\u7528\u540D: ${appName}`);
|
|
1852
2784
|
const publicKeyObj = this.extractPublicKey(resolvedPublicKey);
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
logger.info("\u6B63\u5728\u67E5\u8BE2\u5E94\u7528\u4FE1\u606F...");
|
|
1858
|
-
const status = await this.queryAppStatus(userName, packageName, privateKey, publicKeyObj);
|
|
1859
|
-
uploadType = status.type;
|
|
1860
|
-
existingAppInfo = status.appInfo;
|
|
1861
|
-
} else {
|
|
1862
|
-
uploadType = configUploadType;
|
|
1863
|
-
}
|
|
1864
|
-
const uploadTypeDesc = uploadType === "update" ? "\u5E94\u7528\u66F4\u65B0 (synchroType=1)" : "\u65B0\u589E\u5E94\u7528 (synchroType=0)";
|
|
1865
|
-
logger.info(`\u4E0A\u4F20\u7C7B\u578B: ${uploadTypeDesc}`);
|
|
1866
|
-
if (uploadType === "new" && xiaomiConfig.uploadType === "new") {
|
|
1867
|
-
const missing = [];
|
|
1868
|
-
if (xiaomiConfig.category == null) missing.push("category (\u5206\u7C7BID)");
|
|
1869
|
-
if (!xiaomiConfig.desc) missing.push("desc (\u5E94\u7528\u4ECB\u7ECD)");
|
|
1870
|
-
if (missing.length > 0) {
|
|
1871
|
-
return {
|
|
1872
|
-
platform: "xiaomi",
|
|
1873
|
-
success: false,
|
|
1874
|
-
message: `\u9996\u6B21\u63D0\u5BA1\u6A21\u5F0F\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: ${missing.join(", ")}\u3002\u8BF7\u5728\u540E\u53F0\u8865\u5168\u6216\u5728\u914D\u7F6E\u4E2D\u6DFB\u52A0\u3002`
|
|
1875
|
-
};
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
2785
|
+
const anyPath = Object.values(artifacts)[0];
|
|
2786
|
+
const packageName = xiaomiConfig.packageName || this.inferPackageName(anyPath);
|
|
2787
|
+
if (!packageName) throw new Error("\u7F3A\u5C11 packageName");
|
|
2788
|
+
const appName = xiaomiConfig.appName || this.inferAppName(packageName);
|
|
1878
2789
|
try {
|
|
1879
|
-
|
|
2790
|
+
logger.info(`\u5F00\u59CB\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97 (\u5305\u542B ${Object.keys(artifacts).length} \u4E2A\u5305)...`);
|
|
2791
|
+
let uploadType = xiaomiConfig.uploadType || "auto";
|
|
2792
|
+
let finalUploadType;
|
|
2793
|
+
let existingAppInfo = null;
|
|
2794
|
+
if (uploadType === "auto") {
|
|
2795
|
+
const status = await this.queryAppStatus(userName, packageName, privateKey, publicKeyObj);
|
|
2796
|
+
finalUploadType = status.type;
|
|
2797
|
+
existingAppInfo = status.appInfo;
|
|
2798
|
+
} else {
|
|
2799
|
+
finalUploadType = uploadType;
|
|
2800
|
+
}
|
|
2801
|
+
const retryStrategy = createUploadRetryStrategy(config.retry);
|
|
1880
2802
|
const result = await retryStrategy.execute(
|
|
1881
2803
|
() => this.performUpload({
|
|
1882
|
-
|
|
2804
|
+
artifacts,
|
|
1883
2805
|
userName,
|
|
1884
2806
|
privateKey,
|
|
1885
2807
|
publicKey: resolvedPublicKey,
|
|
2808
|
+
uploadType: finalUploadType,
|
|
1886
2809
|
packageName,
|
|
1887
|
-
appName,
|
|
2810
|
+
appName: appName || "",
|
|
1888
2811
|
updateDesc,
|
|
1889
|
-
uploadType,
|
|
1890
2812
|
category: xiaomiConfig.category || existingAppInfo?.category,
|
|
1891
2813
|
keyWords: xiaomiConfig.keyWords || existingAppInfo?.keyWords,
|
|
1892
2814
|
desc: xiaomiConfig.desc || existingAppInfo?.desc,
|
|
@@ -1896,50 +2818,19 @@ ${lines.join("\n")}
|
|
|
1896
2818
|
}),
|
|
1897
2819
|
"Xiaomi push upload"
|
|
1898
2820
|
);
|
|
1899
|
-
if (!result.success) {
|
|
1900
|
-
throw result.error || new Error("Upload failed");
|
|
1901
|
-
}
|
|
1902
|
-
logger.success("\u4E0A\u4F20\u5230\u5C0F\u7C73\u5E94\u7528\u5546\u5E97\u6210\u529F!");
|
|
1903
2821
|
return {
|
|
1904
2822
|
platform: "xiaomi",
|
|
1905
2823
|
success: true,
|
|
1906
|
-
message: "\u4E0A\u4F20\u6210\u529F
|
|
1907
|
-
data: result.data,
|
|
2824
|
+
message: "\u4E0A\u4F20\u6210\u529F",
|
|
1908
2825
|
consoleUrl: "https://dev.mi.com/distribute/app/details"
|
|
1909
2826
|
};
|
|
1910
2827
|
} catch (error) {
|
|
1911
|
-
logger.error(`\u4E0A\u4F20\
|
|
1912
|
-
|
|
1913
|
-
logger.info(`\u9519\u8BEF\u8BE6\u60C5: ${JSON.stringify(error.response.data)}`);
|
|
1914
|
-
}
|
|
1915
|
-
return {
|
|
1916
|
-
platform: "xiaomi",
|
|
1917
|
-
success: false,
|
|
1918
|
-
message: error.message,
|
|
1919
|
-
error
|
|
1920
|
-
};
|
|
2828
|
+
logger.error(`\u5C0F\u7C73\u4E0A\u4F20\u5931\u8D25: ${error.message}`);
|
|
2829
|
+
return { platform: "xiaomi", success: false, message: error.message, error };
|
|
1921
2830
|
}
|
|
1922
2831
|
}
|
|
1923
|
-
/**
|
|
1924
|
-
* 执行实际的上传操作
|
|
1925
|
-
*/
|
|
1926
2832
|
async performUpload(params) {
|
|
1927
|
-
const {
|
|
1928
|
-
filePath,
|
|
1929
|
-
userName,
|
|
1930
|
-
privateKey,
|
|
1931
|
-
publicKey,
|
|
1932
|
-
packageName,
|
|
1933
|
-
appName,
|
|
1934
|
-
updateDesc,
|
|
1935
|
-
uploadType,
|
|
1936
|
-
category,
|
|
1937
|
-
keyWords,
|
|
1938
|
-
desc,
|
|
1939
|
-
brief,
|
|
1940
|
-
privacyUrl,
|
|
1941
|
-
assets
|
|
1942
|
-
} = params;
|
|
2833
|
+
const { artifacts, userName, privateKey, publicKey, packageName, appName, updateDesc, uploadType, category, keyWords, desc, brief, privacyUrl, assets } = params;
|
|
1943
2834
|
const publicKeyObj = this.extractPublicKey(publicKey);
|
|
1944
2835
|
const requestData = this.buildRequestData({
|
|
1945
2836
|
userName,
|
|
@@ -1953,67 +2844,55 @@ ${lines.join("\n")}
|
|
|
1953
2844
|
brief,
|
|
1954
2845
|
privacyUrl
|
|
1955
2846
|
});
|
|
1956
|
-
logger.info("RequestData \u5DF2\u6784\u5EFA");
|
|
1957
|
-
logger.info(`RequestData: ${requestData}`);
|
|
1958
2847
|
const requestDataMD5 = this.calculateStringMD5(requestData);
|
|
1959
|
-
|
|
1960
|
-
|
|
2848
|
+
let mainPath;
|
|
2849
|
+
let secondPath = void 0;
|
|
2850
|
+
const artifactEntries = Object.entries(artifacts);
|
|
2851
|
+
const arm64 = artifactEntries.find(([abi]) => abi === "arm64-v8a");
|
|
2852
|
+
const arm32 = artifactEntries.find(([abi]) => abi === "armeabi-v7a");
|
|
2853
|
+
if (arm64 && arm32) {
|
|
2854
|
+
mainPath = arm64[1];
|
|
2855
|
+
secondPath = arm32[1];
|
|
2856
|
+
logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u67B6\u6784\u5206\u5305: \u4E3B\u5305(64\u4F4D)=${import_path5.default.basename(mainPath)}, \u6B21\u5305(32\u4F4D)=${import_path5.default.basename(secondPath)}`);
|
|
2857
|
+
} else {
|
|
2858
|
+
mainPath = artifactEntries[0][1];
|
|
2859
|
+
if (artifactEntries.length > 1) {
|
|
2860
|
+
secondPath = artifactEntries[1][1];
|
|
2861
|
+
logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u4E2A\u5305\uFF08\u975E\u6807\u51C6\u540D\uFF09\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u987A\u5E8F: \u4E3B\u5305=${import_path5.default.basename(mainPath)}, \u6B21\u5305=${import_path5.default.basename(secondPath)}`);
|
|
2862
|
+
} else {
|
|
2863
|
+
logger.info(`[Xiaomi] \u4F7F\u7528\u5355\u5305\u6A21\u5F0F: ${import_path5.default.basename(mainPath)}`);
|
|
2864
|
+
}
|
|
2865
|
+
}
|
|
2866
|
+
const mainMD5 = await this.calculateFileMD5(mainPath);
|
|
1961
2867
|
const extraSigItems = [];
|
|
1962
|
-
|
|
2868
|
+
if (secondPath) {
|
|
2869
|
+
const secondMD5 = await this.calculateFileMD5(secondPath);
|
|
2870
|
+
extraSigItems.push({ name: "secondApk", hash: secondMD5 });
|
|
2871
|
+
}
|
|
1963
2872
|
if (assets?.icon && import_fs_extra5.default.existsSync(assets.icon)) {
|
|
1964
|
-
|
|
1965
|
-
const iconMD5 = await this.calculateFileMD5(iconPath);
|
|
1966
|
-
extraSigItems.push({ name: "icon", hash: iconMD5 });
|
|
1967
|
-
}
|
|
1968
|
-
const screenshotPaths = [];
|
|
1969
|
-
if (assets?.screenshots) {
|
|
1970
|
-
for (let i = 0; i < assets.screenshots.length && i < 4; i++) {
|
|
1971
|
-
const ssPath = assets.screenshots[i];
|
|
1972
|
-
if (import_fs_extra5.default.existsSync(ssPath)) {
|
|
1973
|
-
screenshotPaths.push(ssPath);
|
|
1974
|
-
const ssMD5 = await this.calculateFileMD5(ssPath);
|
|
1975
|
-
extraSigItems.push({ name: `screenshot_${i + 1}`, hash: ssMD5 });
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
2873
|
+
extraSigItems.push({ name: "icon", hash: await this.calculateFileMD5(assets.icon) });
|
|
1978
2874
|
}
|
|
1979
|
-
const sig = this.generateUploadSIG(
|
|
1980
|
-
requestDataMD5,
|
|
1981
|
-
apkFileMD5,
|
|
1982
|
-
privateKey,
|
|
1983
|
-
publicKeyObj,
|
|
1984
|
-
extraSigItems.length > 0 ? extraSigItems : void 0
|
|
1985
|
-
);
|
|
1986
|
-
logger.info("SIG \u7B7E\u540D\u5DF2\u751F\u6210");
|
|
2875
|
+
const sig = this.generateUploadSIG(requestDataMD5, mainMD5, privateKey, publicKeyObj, extraSigItems);
|
|
1987
2876
|
const form = new import_form_data3.default();
|
|
1988
2877
|
form.append("RequestData", requestData);
|
|
1989
2878
|
form.append("SIG", sig);
|
|
1990
|
-
form.append("apk", import_fs_extra5.default.createReadStream(
|
|
1991
|
-
if (
|
|
1992
|
-
form.append("
|
|
1993
|
-
logger.info(`\u9644\u52A0 icon: ${iconPath}`);
|
|
2879
|
+
form.append("apk", import_fs_extra5.default.createReadStream(mainPath));
|
|
2880
|
+
if (secondPath) {
|
|
2881
|
+
form.append("secondApk", import_fs_extra5.default.createReadStream(secondPath));
|
|
1994
2882
|
}
|
|
1995
|
-
|
|
1996
|
-
form.append(
|
|
1997
|
-
logger.info(`\u9644\u52A0 screenshot_${i + 1}: ${screenshotPaths[i]}`);
|
|
2883
|
+
if (assets?.icon && import_fs_extra5.default.existsSync(assets.icon)) {
|
|
2884
|
+
form.append("icon", import_fs_extra5.default.createReadStream(assets.icon));
|
|
1998
2885
|
}
|
|
1999
|
-
logger.info(`POST ${_XiaomiUploader.PUSH_API}`);
|
|
2000
2886
|
const response = await import_axios3.default.post(_XiaomiUploader.PUSH_API, form, {
|
|
2001
|
-
headers:
|
|
2002
|
-
...form.getHeaders()
|
|
2003
|
-
},
|
|
2887
|
+
headers: form.getHeaders(),
|
|
2004
2888
|
maxContentLength: Infinity,
|
|
2005
2889
|
maxBodyLength: Infinity,
|
|
2006
|
-
timeout:
|
|
2007
|
-
// 60 秒请求超时
|
|
2890
|
+
timeout: 12e4
|
|
2008
2891
|
});
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
if (data?.result !== 0) {
|
|
2012
|
-
const errMsg = data?.message || data?.msg || JSON.stringify(data);
|
|
2013
|
-
throw new Error(`\u5C0F\u7C73 API \u8FD4\u56DE\u9519\u8BEF (result=${data?.result}): ${errMsg}`);
|
|
2892
|
+
if (response.data?.result !== 0) {
|
|
2893
|
+
throw new Error(`\u5C0F\u7C73 API \u9519\u8BEF: ${response.data?.message || JSON.stringify(response.data)}`);
|
|
2014
2894
|
}
|
|
2015
|
-
|
|
2016
|
-
return data;
|
|
2895
|
+
return response.data;
|
|
2017
2896
|
}
|
|
2018
2897
|
/**
|
|
2019
2898
|
* 解析公钥: 优先使用内容字段, 其次从文件路径读取
|
|
@@ -2055,7 +2934,7 @@ ${lines.join("\n")}
|
|
|
2055
2934
|
|
|
2056
2935
|
// src/adapters/apple_store_uploader.ts
|
|
2057
2936
|
var import_fs_extra6 = __toESM(require("fs-extra"), 1);
|
|
2058
|
-
var
|
|
2937
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
2059
2938
|
var import_child_process = require("child_process");
|
|
2060
2939
|
var import_util2 = require("util");
|
|
2061
2940
|
var import_axios4 = __toESM(require("axios"), 1);
|
|
@@ -2221,7 +3100,7 @@ var AppleStoreUploader = class {
|
|
|
2221
3100
|
appPassword,
|
|
2222
3101
|
"-showTransferRate"
|
|
2223
3102
|
].join(" ");
|
|
2224
|
-
logger.info(`[Transporter] Uploading: ${
|
|
3103
|
+
logger.info(`[Transporter] Uploading: ${import_path6.default.basename(ipaPath)}`);
|
|
2225
3104
|
try {
|
|
2226
3105
|
const { stdout, stderr } = await execAsync(command, {
|
|
2227
3106
|
timeout: 30 * 60 * 1e3,
|
|
@@ -2272,7 +3151,7 @@ Stderr: ${error.stderr || ""}`);
|
|
|
2272
3151
|
});
|
|
2273
3152
|
command = innerArgs.join(" ");
|
|
2274
3153
|
}
|
|
2275
|
-
logger.info(`[altool] Uploading: ${
|
|
3154
|
+
logger.info(`[altool] Uploading: ${import_path6.default.basename(ipaPath)}`);
|
|
2276
3155
|
logger.info(`[altool] Command prefix: ${tool.command}`);
|
|
2277
3156
|
try {
|
|
2278
3157
|
const { stdout, stderr } = await execAsync(command, {
|
|
@@ -2856,7 +3735,7 @@ var TestFlightUploader = class _TestFlightUploader {
|
|
|
2856
3735
|
|
|
2857
3736
|
// src/adapters/adhoc_uploader.ts
|
|
2858
3737
|
var import_fs_extra8 = __toESM(require("fs-extra"), 1);
|
|
2859
|
-
var
|
|
3738
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
2860
3739
|
var import_child_process2 = require("child_process");
|
|
2861
3740
|
var AdHocUploader = class {
|
|
2862
3741
|
name = "AdHoc";
|
|
@@ -2989,8 +3868,8 @@ var AdHocUploader = class {
|
|
|
2989
3868
|
* @param changelog 更新日志
|
|
2990
3869
|
*/
|
|
2991
3870
|
async createManifest(ipaPath, version, bundleId, changelog) {
|
|
2992
|
-
const ipaFileName =
|
|
2993
|
-
const appName = bundleId ||
|
|
3871
|
+
const ipaFileName = import_path7.default.basename(ipaPath);
|
|
3872
|
+
const appName = bundleId || import_path7.default.parse(ipaFileName).name;
|
|
2994
3873
|
const ipaUrl = `itms-services://?action=download-manifest&url=https://your-distribution-server.com/manifests/${ipaFileName}.plist`;
|
|
2995
3874
|
const manifestContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
2996
3875
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -3049,14 +3928,14 @@ var AdHocUploader = class {
|
|
|
3049
3928
|
*/
|
|
3050
3929
|
async setupDistributionDirectory(ipaPath, manifestPath) {
|
|
3051
3930
|
const projectRoot = process.cwd();
|
|
3052
|
-
const distributionDir =
|
|
3931
|
+
const distributionDir = import_path7.default.join(projectRoot, "build", "adhoc-distribution");
|
|
3053
3932
|
await import_fs_extra8.default.ensureDir(distributionDir);
|
|
3054
|
-
await import_fs_extra8.default.ensureDir(
|
|
3055
|
-
await import_fs_extra8.default.ensureDir(
|
|
3056
|
-
await import_fs_extra8.default.ensureDir(
|
|
3057
|
-
const ipaFileName =
|
|
3058
|
-
await import_fs_extra8.default.copy(ipaPath,
|
|
3059
|
-
await import_fs_extra8.default.copy(manifestPath,
|
|
3933
|
+
await import_fs_extra8.default.ensureDir(import_path7.default.join(distributionDir, "apps"));
|
|
3934
|
+
await import_fs_extra8.default.ensureDir(import_path7.default.join(distributionDir, "manifests"));
|
|
3935
|
+
await import_fs_extra8.default.ensureDir(import_path7.default.join(distributionDir, "icons"));
|
|
3936
|
+
const ipaFileName = import_path7.default.basename(ipaPath);
|
|
3937
|
+
await import_fs_extra8.default.copy(ipaPath, import_path7.default.join(distributionDir, "apps", ipaFileName));
|
|
3938
|
+
await import_fs_extra8.default.copy(manifestPath, import_path7.default.join(distributionDir, "manifests", `${ipaFileName}.plist`));
|
|
3060
3939
|
logger.info(`Distribution directory: ${distributionDir}`);
|
|
3061
3940
|
return distributionDir;
|
|
3062
3941
|
}
|
|
@@ -3066,7 +3945,7 @@ var AdHocUploader = class {
|
|
|
3066
3945
|
* @param bundleId Bundle ID
|
|
3067
3946
|
*/
|
|
3068
3947
|
generateInstallUrl(distributionPath, bundleId) {
|
|
3069
|
-
const manifestPath =
|
|
3948
|
+
const manifestPath = import_path7.default.join(distributionPath, "manifests", `*.plist`);
|
|
3070
3949
|
return `itms-services://?action=download-manifest&url=file://${manifestPath}`;
|
|
3071
3950
|
}
|
|
3072
3951
|
/**
|
|
@@ -3075,7 +3954,7 @@ var AdHocUploader = class {
|
|
|
3075
3954
|
* @param distributionPath 分发目录
|
|
3076
3955
|
*/
|
|
3077
3956
|
async generateQRCode(url, distributionPath) {
|
|
3078
|
-
const qrCodePath =
|
|
3957
|
+
const qrCodePath = import_path7.default.join(distributionPath, "install-qrcode.png");
|
|
3079
3958
|
try {
|
|
3080
3959
|
logger.info(`QR code would be saved to: ${qrCodePath}`);
|
|
3081
3960
|
logger.info(`Install URL: ${url}`);
|
|
@@ -3102,8 +3981,8 @@ var AdHocUploader = class {
|
|
|
3102
3981
|
logger.newLine();
|
|
3103
3982
|
logger.info("\u{1F4E6} Distribution Files:");
|
|
3104
3983
|
logger.info(` Directory: ${distributionPath}`);
|
|
3105
|
-
logger.info(` IPA: ${
|
|
3106
|
-
logger.info(` Manifest: ${
|
|
3984
|
+
logger.info(` IPA: ${import_path7.default.join(distributionPath, "apps", "*.ipa")}`);
|
|
3985
|
+
logger.info(` Manifest: ${import_path7.default.join(distributionPath, "manifests", "*.plist")}`);
|
|
3107
3986
|
logger.info(` QR Code: ${qrCodePath}`);
|
|
3108
3987
|
logger.newLine();
|
|
3109
3988
|
logger.info("\u{1F517} Installation Link:");
|
|
@@ -3162,7 +4041,7 @@ var AdHocUploader = class {
|
|
|
3162
4041
|
|
|
3163
4042
|
// src/adapters/harmony_uploader.ts
|
|
3164
4043
|
var import_fs_extra9 = __toESM(require("fs-extra"), 1);
|
|
3165
|
-
var
|
|
4044
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
3166
4045
|
var import_axios6 = __toESM(require("axios"), 1);
|
|
3167
4046
|
var import_form_data5 = __toESM(require("form-data"), 1);
|
|
3168
4047
|
var HarmonyUploader = class _HarmonyUploader {
|
|
@@ -3192,7 +4071,7 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3192
4071
|
const clientSecret = harmonyConfig.auth.clientSecret;
|
|
3193
4072
|
const appId = harmonyConfig.appId;
|
|
3194
4073
|
const releaseType = harmonyConfig.releaseType || "draft";
|
|
3195
|
-
const suffix =
|
|
4074
|
+
const suffix = import_path8.default.extname(filePath).substring(1);
|
|
3196
4075
|
const retryConfig = config.retry;
|
|
3197
4076
|
logger.info("Starting HarmonyOS AppGallery upload...");
|
|
3198
4077
|
try {
|
|
@@ -3309,7 +4188,7 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3309
4188
|
* @param filePath 文件路径
|
|
3310
4189
|
*/
|
|
3311
4190
|
async updateAppFileInfo(token, appId, authCode, filePath) {
|
|
3312
|
-
const fileName =
|
|
4191
|
+
const fileName = import_path8.default.basename(filePath);
|
|
3313
4192
|
const body = {
|
|
3314
4193
|
fileType: 7,
|
|
3315
4194
|
// 鸿蒙包的 fileType(需要根据华为最新文档确认)
|
|
@@ -3332,10 +4211,10 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3332
4211
|
|
|
3333
4212
|
// src/adapters/oppo_uploader.ts
|
|
3334
4213
|
var import_fs_extra10 = __toESM(require("fs-extra"), 1);
|
|
3335
|
-
var
|
|
4214
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
3336
4215
|
var import_axios7 = __toESM(require("axios"), 1);
|
|
3337
4216
|
var import_form_data6 = __toESM(require("form-data"), 1);
|
|
3338
|
-
var
|
|
4217
|
+
var import_crypto5 = __toESM(require("crypto"), 1);
|
|
3339
4218
|
var OppoUploader = class _OppoUploader {
|
|
3340
4219
|
name = "OPPO";
|
|
3341
4220
|
/** OPPO 开放 API 基础地址 */
|
|
@@ -3354,7 +4233,7 @@ var OppoUploader = class _OppoUploader {
|
|
|
3354
4233
|
*/
|
|
3355
4234
|
generateSign(params, clientSecret) {
|
|
3356
4235
|
const sorted = Object.keys(params).sort().filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
3357
|
-
return
|
|
4236
|
+
return import_crypto5.default.createHmac("sha256", clientSecret).update(sorted).digest("hex");
|
|
3358
4237
|
}
|
|
3359
4238
|
validateConfig(config) {
|
|
3360
4239
|
if (!config?.publish?.oppo?.enable) return false;
|
|
@@ -3373,7 +4252,7 @@ var OppoUploader = class _OppoUploader {
|
|
|
3373
4252
|
* 尝试从文件名推断包名
|
|
3374
4253
|
*/
|
|
3375
4254
|
inferPackageName(filePath) {
|
|
3376
|
-
const fileName =
|
|
4255
|
+
const fileName = import_path9.default.basename(filePath);
|
|
3377
4256
|
const match = fileName.match(/^([\w.]+?)(?:-(?:release|debug|unsigned))?\.apk$/i);
|
|
3378
4257
|
if (match && match[1].includes(".")) {
|
|
3379
4258
|
return match[1];
|
|
@@ -3391,110 +4270,47 @@ var OppoUploader = class _OppoUploader {
|
|
|
3391
4270
|
}
|
|
3392
4271
|
async upload(filePath, config, metadata) {
|
|
3393
4272
|
const oppoConfig = config.publish.oppo;
|
|
3394
|
-
const
|
|
3395
|
-
const clientSecret = oppoConfig.auth.clientSecret;
|
|
4273
|
+
const accessToken = await this.getAccessToken(oppoConfig.auth.clientId, oppoConfig.auth.clientSecret);
|
|
3396
4274
|
const packageName = oppoConfig.packageName || this.inferPackageName(filePath);
|
|
3397
|
-
if (!packageName) {
|
|
3398
|
-
return {
|
|
3399
|
-
platform: "oppo",
|
|
3400
|
-
success: false,
|
|
3401
|
-
message: "\u65E0\u6CD5\u83B7\u53D6\u5305\u540D (packageName)\uFF0C\u8BF7\u5728\u914D\u7F6E\u4E2D\u6307\u5B9A\u6216\u786E\u4FDD\u6587\u4EF6\u540D\u5305\u542B\u5305\u540D",
|
|
3402
|
-
consoleUrl: "https://open.oppomobile.com/new/ecology/app"
|
|
3403
|
-
};
|
|
3404
|
-
}
|
|
3405
|
-
const retryConfig = config.retry;
|
|
3406
4275
|
const consoleUrl = "https://open.oppomobile.com/new/ecology/app";
|
|
3407
|
-
if (!
|
|
3408
|
-
return {
|
|
3409
|
-
platform: "oppo",
|
|
3410
|
-
success: false,
|
|
3411
|
-
message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`,
|
|
3412
|
-
consoleUrl
|
|
3413
|
-
};
|
|
4276
|
+
if (!packageName) {
|
|
4277
|
+
return { platform: "oppo", success: false, message: "\u65E0\u6CD5\u83B7\u53D6\u5305\u540D", consoleUrl };
|
|
3414
4278
|
}
|
|
3415
|
-
|
|
3416
|
-
logger.info(`\u6587\u4EF6: ${filePath}`);
|
|
4279
|
+
const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
|
|
3417
4280
|
try {
|
|
3418
|
-
const retryStrategy = createUploadRetryStrategy(
|
|
3419
|
-
|
|
3420
|
-
const
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
(
|
|
3431
|
-
|
|
3432
|
-
);
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
throw uploadUrlResult.error || new Error("Failed to get upload URL");
|
|
3447
|
-
}
|
|
3448
|
-
const { uploadUrl, sign: uploadSign } = uploadUrlResult.data;
|
|
3449
|
-
logger.info("\u6B63\u5728\u4E0A\u4F20 APK \u6587\u4EF6...");
|
|
3450
|
-
const uploadFileResult = await retryStrategy.execute(
|
|
3451
|
-
() => this.uploadFile(uploadUrl, uploadSign, filePath),
|
|
3452
|
-
"OPPO file upload"
|
|
3453
|
-
);
|
|
3454
|
-
if (!uploadFileResult.success || !uploadFileResult.data) {
|
|
3455
|
-
throw uploadFileResult.error || new Error("File upload failed");
|
|
3456
|
-
}
|
|
3457
|
-
const rawUrl = uploadFileResult.data.url;
|
|
3458
|
-
const fileUrl = rawUrl.replace(/^http:\/\//i, "https://").replace("storedl1.nearme.com.cn", "storedl.nearme.com.cn");
|
|
3459
|
-
const md5 = uploadFileResult.data.md5;
|
|
3460
|
-
logger.info("\u6B63\u5728\u63D0\u4EA4\u7248\u672C\u4FE1\u606F...");
|
|
3461
|
-
const submitResult = await retryStrategy.execute(
|
|
3462
|
-
() => this.submitVersion({
|
|
3463
|
-
accessToken,
|
|
3464
|
-
clientSecret,
|
|
3465
|
-
packageName,
|
|
3466
|
-
appName: oppoConfig.appName || metadata.appName || onlineAppInfo.app_name || this.inferAppName(packageName),
|
|
3467
|
-
fileUrl,
|
|
3468
|
-
fileMd5: md5,
|
|
3469
|
-
versionName: metadata.version,
|
|
3470
|
-
versionCode: metadata.buildNumber,
|
|
3471
|
-
updateDesc: metadata.changelog || "App Update",
|
|
3472
|
-
onlineType: oppoConfig.onlineType,
|
|
3473
|
-
// 从配置读取
|
|
3474
|
-
onlineAppInfo
|
|
3475
|
-
// 传入线上信息
|
|
3476
|
-
}),
|
|
3477
|
-
"OPPO submit version"
|
|
3478
|
-
);
|
|
3479
|
-
if (!submitResult.success) {
|
|
3480
|
-
throw submitResult.error || new Error("Submit version failed");
|
|
3481
|
-
}
|
|
4281
|
+
const retryStrategy = createUploadRetryStrategy(config.retry);
|
|
4282
|
+
const onlineAppInfo = await this.getAppInfo(accessToken, oppoConfig.auth.clientSecret, packageName) || {};
|
|
4283
|
+
const uploadResults = [];
|
|
4284
|
+
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
4285
|
+
if (!p || !import_fs_extra10.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
|
|
4286
|
+
logger.info(`[OPPO] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305...`);
|
|
4287
|
+
const { uploadUrl, sign } = await this.getUploadUrl(accessToken, oppoConfig.auth.clientSecret);
|
|
4288
|
+
const uploadFileResult = await this.uploadFile(uploadUrl, sign, p);
|
|
4289
|
+
const fileUrl = uploadFileResult.url.replace(/^http:\/\//i, "https://").replace("storedl1.nearme.com.cn", "storedl.nearme.com.cn");
|
|
4290
|
+
let cpu_code = 0;
|
|
4291
|
+
if (abi === "arm64-v8a") cpu_code = 3;
|
|
4292
|
+
else if (abi === "armeabi-v7a") cpu_code = 4;
|
|
4293
|
+
uploadResults.push({ url: fileUrl, md5: uploadFileResult.md5, cpu_code });
|
|
4294
|
+
});
|
|
4295
|
+
await Promise.all(uploadPromises);
|
|
4296
|
+
logger.info(`[OPPO] \u6B63\u5728\u63D0\u4EA4\u6D41\u6C34\u53F7\u7EC4: ${JSON.stringify(uploadResults)}`);
|
|
4297
|
+
await this.submitVersion({
|
|
4298
|
+
accessToken,
|
|
4299
|
+
clientSecret: oppoConfig.auth.clientSecret,
|
|
4300
|
+
packageName,
|
|
4301
|
+
appName: oppoConfig.appName || onlineAppInfo.app_name || metadata.appName || this.inferAppName(packageName),
|
|
4302
|
+
apkUrls: uploadResults,
|
|
4303
|
+
versionName: metadata.version,
|
|
4304
|
+
versionCode: metadata.buildNumber,
|
|
4305
|
+
updateDesc: metadata.changelog || "App Update",
|
|
4306
|
+
onlineType: oppoConfig.onlineType,
|
|
4307
|
+
onlineAppInfo
|
|
4308
|
+
});
|
|
3482
4309
|
logger.success("\u4E0A\u4F20\u5230 OPPO \u5E94\u7528\u5546\u5E97\u6210\u529F!");
|
|
3483
|
-
return {
|
|
3484
|
-
platform: "oppo",
|
|
3485
|
-
success: true,
|
|
3486
|
-
message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u524D\u5F80\u540E\u53F0\u786E\u8BA4\u5E76\u63D0\u4EA4\u5BA1\u6838",
|
|
3487
|
-
consoleUrl
|
|
3488
|
-
};
|
|
4310
|
+
return { platform: "oppo", success: true, message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u786E\u8BA4", consoleUrl };
|
|
3489
4311
|
} catch (error) {
|
|
3490
4312
|
logger.error(`\u4E0A\u4F20\u5230 OPPO \u5931\u8D25: ${error.message}`);
|
|
3491
|
-
return {
|
|
3492
|
-
platform: "oppo",
|
|
3493
|
-
success: false,
|
|
3494
|
-
message: this.formatError(error),
|
|
3495
|
-
error,
|
|
3496
|
-
consoleUrl
|
|
3497
|
-
};
|
|
4313
|
+
return { platform: "oppo", success: false, message: error.message, error, consoleUrl };
|
|
3498
4314
|
}
|
|
3499
4315
|
}
|
|
3500
4316
|
/**
|
|
@@ -3615,16 +4431,7 @@ var OppoUploader = class _OppoUploader {
|
|
|
3615
4431
|
const signParams = {
|
|
3616
4432
|
access_token: params.accessToken,
|
|
3617
4433
|
timestamp,
|
|
3618
|
-
|
|
3619
|
-
// cpu_code: 0=通用包, 3=arm64-v8a, 4=armeabi-v7a, 5=x86_64
|
|
3620
|
-
apk_url: JSON.stringify([
|
|
3621
|
-
{
|
|
3622
|
-
url: params.fileUrl,
|
|
3623
|
-
md5: params.fileMd5,
|
|
3624
|
-
cpu_code: 0
|
|
3625
|
-
// 通用包
|
|
3626
|
-
}
|
|
3627
|
-
]),
|
|
4434
|
+
apk_url: JSON.stringify(params.apkUrls),
|
|
3628
4435
|
version_name: params.versionName.split("+")[0],
|
|
3629
4436
|
// 仅保留版本名部分
|
|
3630
4437
|
version_code: vCode,
|
|
@@ -3726,34 +4533,22 @@ var OppoUploader = class _OppoUploader {
|
|
|
3726
4533
|
|
|
3727
4534
|
// src/adapters/vivo_uploader.ts
|
|
3728
4535
|
var import_fs_extra11 = __toESM(require("fs-extra"), 1);
|
|
3729
|
-
var
|
|
4536
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
3730
4537
|
var import_axios8 = __toESM(require("axios"), 1);
|
|
3731
4538
|
var import_form_data7 = __toESM(require("form-data"), 1);
|
|
3732
|
-
var
|
|
4539
|
+
var import_crypto6 = __toESM(require("crypto"), 1);
|
|
3733
4540
|
var VivoUploader = class _VivoUploader {
|
|
3734
4541
|
name = "vivo";
|
|
3735
4542
|
/** vivo 开放 API 基础地址 */
|
|
3736
4543
|
static API_BASE = "https://developer-api.vivo.com.cn/router/rest";
|
|
3737
|
-
/**
|
|
3738
|
-
* vivo API 签名生成
|
|
3739
|
-
* 规则: 所有参数按 ASCII 码排序后拼接为 key=val&... 字符串,
|
|
3740
|
-
* 以 accessSecret 为密钥进行 HMAC-SHA256 加密,结果转为小写 16 进制字符串
|
|
3741
|
-
*/
|
|
3742
4544
|
generateSign(params, accessSecret) {
|
|
3743
4545
|
const sorted = Object.keys(params).sort().filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
3744
|
-
|
|
3745
|
-
logger.info(`[VIVO DEBUG] \u5F85\u7B7E\u540D\u4E32: ${sorted}
|
|
3746
|
-
-> \u7B7E\u540D\u7ED3\u679C: ${sign}`);
|
|
3747
|
-
return sign;
|
|
4546
|
+
return import_crypto6.default.createHmac("sha256", accessSecret).update(sorted).digest("hex");
|
|
3748
4547
|
}
|
|
3749
4548
|
validateConfig(config) {
|
|
3750
4549
|
if (!config?.publish?.vivo?.enable) return false;
|
|
3751
4550
|
const vivo = config.publish.vivo;
|
|
3752
|
-
|
|
3753
|
-
logger.error("vivo config missing: accessKey or accessSecret");
|
|
3754
|
-
return false;
|
|
3755
|
-
}
|
|
3756
|
-
return true;
|
|
4551
|
+
return !!(vivo.auth?.accessKey && vivo.auth?.accessSecret);
|
|
3757
4552
|
}
|
|
3758
4553
|
canHandle(filePath) {
|
|
3759
4554
|
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
@@ -3763,84 +4558,63 @@ var VivoUploader = class _VivoUploader {
|
|
|
3763
4558
|
const vivoConfig = config.publish.vivo;
|
|
3764
4559
|
const accessKey = vivoConfig.auth.accessKey;
|
|
3765
4560
|
const accessSecret = vivoConfig.auth.accessSecret;
|
|
3766
|
-
const retryConfig = config.retry;
|
|
3767
4561
|
const consoleUrl = "https://dev.vivo.com.cn/manage/app";
|
|
3768
|
-
|
|
3769
|
-
return {
|
|
3770
|
-
platform: "vivo",
|
|
3771
|
-
success: false,
|
|
3772
|
-
message: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`,
|
|
3773
|
-
consoleUrl
|
|
3774
|
-
};
|
|
3775
|
-
}
|
|
3776
|
-
const fileSize = import_fs_extra11.default.statSync(filePath).size;
|
|
3777
|
-
logger.info("\u5F00\u59CB\u4E0A\u4F20\u5230 vivo \u5E94\u7528\u5546\u5E97...");
|
|
3778
|
-
logger.info(`\u6587\u4EF6: ${filePath} (${(fileSize / 1024 / 1024).toFixed(2)} MB)`);
|
|
4562
|
+
const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
|
|
3779
4563
|
try {
|
|
3780
|
-
const
|
|
3781
|
-
const
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
4564
|
+
const uploadResults = {};
|
|
4565
|
+
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
4566
|
+
if (!p || !import_fs_extra11.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
|
|
4567
|
+
const md5 = await this.calculateFileMD5(p);
|
|
4568
|
+
logger.info(`[vivo] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${import_path10.default.basename(p)}`);
|
|
4569
|
+
const serialnumber = await this.uploadApk(accessKey, accessSecret, p, vivoConfig.packageName || "", md5);
|
|
4570
|
+
const pkgInfo = await parsePackageInfo(p, { androidSdkPath: config.globalSettings?.androidSdkPath });
|
|
4571
|
+
const nativeCodes = pkgInfo?.nativeCode || [];
|
|
4572
|
+
const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64") || abi === "arm64-v8a";
|
|
4573
|
+
const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || abi === "armeabi-v7a";
|
|
4574
|
+
uploadResults[abi] = { serialnumber, md5, is64Bit: has64Bit && !has32Bit };
|
|
4575
|
+
});
|
|
4576
|
+
await Promise.all(uploadPromises);
|
|
4577
|
+
const arm64Result = uploadResults["arm64-v8a"] || Object.values(uploadResults).find((r) => r.is64Bit);
|
|
4578
|
+
const arm32Result = uploadResults["armeabi-v7a"] || Object.values(uploadResults).find((r) => !r.is64Bit);
|
|
4579
|
+
const submitData = {
|
|
4580
|
+
packageName: vivoConfig.packageName,
|
|
4581
|
+
versionCode: parseInt(metadata.buildNumber) || 1,
|
|
4582
|
+
updateDesc: metadata.changelog || "App Update"
|
|
4583
|
+
};
|
|
4584
|
+
if (arm64Result) {
|
|
4585
|
+
submitData.serialnumber = arm64Result.serialnumber;
|
|
4586
|
+
submitData.fileMd5 = arm64Result.md5;
|
|
3790
4587
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
updateDesc: metadata.changelog || "App Update"
|
|
3800
|
-
}),
|
|
3801
|
-
"vivo submit version"
|
|
3802
|
-
);
|
|
3803
|
-
if (!submitResult.success) {
|
|
3804
|
-
throw submitResult.error || new Error("Submit version failed");
|
|
4588
|
+
if (arm32Result) {
|
|
4589
|
+
submitData.apk32 = arm32Result.serialnumber;
|
|
4590
|
+
submitData.md532 = arm32Result.md5;
|
|
4591
|
+
}
|
|
4592
|
+
if (!submitData.serialnumber && !submitData.apk32) {
|
|
4593
|
+
const single = Object.values(uploadResults)[0];
|
|
4594
|
+
submitData.serialnumber = single.serialnumber;
|
|
4595
|
+
submitData.fileMd5 = single.md5;
|
|
3805
4596
|
}
|
|
4597
|
+
logger.info(`[vivo] \u6B63\u5728\u63D0\u4EA4\u5BA1\u6838\u7533\u62A5: ${JSON.stringify(submitData)}`);
|
|
4598
|
+
await this.submitVersion(accessKey, accessSecret, submitData);
|
|
3806
4599
|
logger.success("\u4E0A\u4F20\u5230 vivo \u5E94\u7528\u5546\u5E97\u6210\u529F!");
|
|
3807
|
-
return {
|
|
3808
|
-
platform: "vivo",
|
|
3809
|
-
success: true,
|
|
3810
|
-
message: "\u4E0A\u4F20\u6210\u529F, \u8BF7\u524D\u5F80\u540E\u53F0\u786E\u8BA4\u5E76\u63D0\u4EA4\u5BA1\u6838",
|
|
3811
|
-
consoleUrl
|
|
3812
|
-
};
|
|
4600
|
+
return { platform: "vivo", success: true, message: "\u4E0A\u4F20\u5E76\u63D0\u4EA4\u5BA1\u6838\u6210\u529F", consoleUrl };
|
|
3813
4601
|
} catch (error) {
|
|
3814
4602
|
logger.error(`\u4E0A\u4F20\u5230 vivo \u5931\u8D25: ${error.message}`);
|
|
3815
|
-
return {
|
|
3816
|
-
platform: "vivo",
|
|
3817
|
-
success: false,
|
|
3818
|
-
message: this.formatError(error),
|
|
3819
|
-
error,
|
|
3820
|
-
consoleUrl
|
|
3821
|
-
};
|
|
4603
|
+
return { platform: "vivo", success: false, message: error.message, error, consoleUrl };
|
|
3822
4604
|
}
|
|
3823
4605
|
}
|
|
3824
|
-
/**
|
|
3825
|
-
* 计算文件 MD5
|
|
3826
|
-
*/
|
|
3827
4606
|
calculateFileMD5(filePath) {
|
|
3828
4607
|
return new Promise((resolve, reject) => {
|
|
3829
|
-
const hash =
|
|
4608
|
+
const hash = import_crypto6.default.createHash("md5");
|
|
3830
4609
|
const stream = import_fs_extra11.default.createReadStream(filePath);
|
|
3831
4610
|
stream.on("data", (data) => hash.update(data));
|
|
3832
4611
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
3833
4612
|
stream.on("error", reject);
|
|
3834
4613
|
});
|
|
3835
4614
|
}
|
|
3836
|
-
/**
|
|
3837
|
-
* 上传 APK 文件并获取流水号
|
|
3838
|
-
* 将公共参数与业务参数放入一个 multipart 请求中直接上传至 API_BASE
|
|
3839
|
-
*/
|
|
3840
4615
|
async uploadApk(accessKey, accessSecret, filePath, packageName, fileMd5) {
|
|
3841
4616
|
const timestamp = String(Date.now());
|
|
3842
|
-
const
|
|
3843
|
-
// 公共参数
|
|
4617
|
+
const params = {
|
|
3844
4618
|
access_key: accessKey,
|
|
3845
4619
|
timestamp,
|
|
3846
4620
|
format: "json",
|
|
@@ -3848,41 +4622,19 @@ var VivoUploader = class _VivoUploader {
|
|
|
3848
4622
|
sign_method: "hmac",
|
|
3849
4623
|
target_app_key: "developer",
|
|
3850
4624
|
method: "app.upload.apk.app",
|
|
3851
|
-
// 业务参数
|
|
3852
4625
|
packageName,
|
|
3853
4626
|
fileMd5
|
|
3854
4627
|
};
|
|
3855
|
-
const sign = this.generateSign(
|
|
4628
|
+
const sign = this.generateSign(params, accessSecret);
|
|
3856
4629
|
const form = new import_form_data7.default();
|
|
3857
|
-
|
|
3858
|
-
form.append(key, value);
|
|
3859
|
-
}
|
|
4630
|
+
Object.entries(params).forEach(([k, v]) => form.append(k, v));
|
|
3860
4631
|
form.append("sign", sign);
|
|
3861
|
-
form.append("file", import_fs_extra11.default.createReadStream(filePath), {
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
logger.info(
|
|
3866
|
-
`[VIVO DEBUG] \u5F00\u59CB\u4E0A\u4F20 APK (API\u8BF7\u6C42) \u53C2\u6570(\u4E0D\u542B\u6587\u4EF6): ${JSON.stringify({ ...paramsForSign, sign })}`
|
|
3867
|
-
);
|
|
3868
|
-
const res = await import_axios8.default.post(_VivoUploader.API_BASE, form, {
|
|
3869
|
-
headers: form.getHeaders(),
|
|
3870
|
-
maxContentLength: Infinity,
|
|
3871
|
-
maxBodyLength: Infinity,
|
|
3872
|
-
timeout: 3e5
|
|
3873
|
-
});
|
|
3874
|
-
logger.info(`[VIVO DEBUG] \u4E0A\u4F20\u54CD\u5E94: ${JSON.stringify(res.data)}`);
|
|
3875
|
-
if (res.data.code !== 0) {
|
|
3876
|
-
throw new Error(`\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25: ${res.data.msg || JSON.stringify(res.data)}`);
|
|
3877
|
-
}
|
|
3878
|
-
const serialnumber = res.data.data?.serialnumber || res.data.data;
|
|
3879
|
-
logger.info(`APK \u6587\u4EF6\u4E0A\u4F20\u5B8C\u6210\uFF0C\u6D41\u6C34\u53F7: ${serialnumber}`);
|
|
3880
|
-
return serialnumber;
|
|
4632
|
+
form.append("file", import_fs_extra11.default.createReadStream(filePath), { filename: import_path10.default.basename(filePath) });
|
|
4633
|
+
const res = await import_axios8.default.post(_VivoUploader.API_BASE, form, { headers: form.getHeaders(), timeout: 3e5 });
|
|
4634
|
+
if (res.data.code !== 0) throw new Error(`\u4E0A\u4F20\u5305\u5931\u8D25: ${res.data.msg}`);
|
|
4635
|
+
return res.data.data?.serialnumber || res.data.data;
|
|
3881
4636
|
}
|
|
3882
|
-
|
|
3883
|
-
* 提交版本更新信息
|
|
3884
|
-
*/
|
|
3885
|
-
async submitVersion(accessKey, accessSecret, params) {
|
|
4637
|
+
async submitVersion(accessKey, accessSecret, data) {
|
|
3886
4638
|
const timestamp = String(Date.now());
|
|
3887
4639
|
const reqParams = {
|
|
3888
4640
|
access_key: accessKey,
|
|
@@ -3892,53 +4644,34 @@ var VivoUploader = class _VivoUploader {
|
|
|
3892
4644
|
sign_method: "hmac",
|
|
3893
4645
|
target_app_key: "developer",
|
|
3894
4646
|
method: "app.sync.update.app",
|
|
3895
|
-
packageName:
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
apk: params.serialnumber,
|
|
3900
|
-
// 文档中的 apk 字段即为上传接口返回的 serialnumber
|
|
3901
|
-
fileMd5: params.fileMd5,
|
|
3902
|
-
// 文档必填
|
|
4647
|
+
packageName: data.packageName || "",
|
|
4648
|
+
versionCode: String(data.versionCode),
|
|
4649
|
+
apk: data.serialnumber || "",
|
|
4650
|
+
fileMd5: data.fileMd5 || "",
|
|
3903
4651
|
onlineType: "1",
|
|
3904
|
-
// 1:立即上架 2:定时上架 (必填)
|
|
3905
4652
|
compatibleDevice: "1",
|
|
3906
|
-
|
|
3907
|
-
updateDesc: params.updateDesc
|
|
4653
|
+
updateDesc: data.updateDesc
|
|
3908
4654
|
};
|
|
4655
|
+
if (data.apk32) {
|
|
4656
|
+
reqParams.apk32 = data.apk32;
|
|
4657
|
+
if (data.md532) reqParams.md532 = data.md532;
|
|
4658
|
+
}
|
|
3909
4659
|
reqParams.sign = this.generateSign(reqParams, accessSecret);
|
|
3910
4660
|
const formData = new URLSearchParams();
|
|
3911
|
-
|
|
3912
|
-
formData.append(key, value);
|
|
3913
|
-
}
|
|
4661
|
+
Object.entries(reqParams).forEach(([k, v]) => formData.append(k, v));
|
|
3914
4662
|
const res = await import_axios8.default.post(_VivoUploader.API_BASE, formData.toString(), {
|
|
3915
|
-
headers: {
|
|
3916
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
3917
|
-
},
|
|
4663
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
3918
4664
|
timeout: 3e4
|
|
3919
4665
|
});
|
|
3920
|
-
if (res.data.code !== 0) {
|
|
3921
|
-
throw new Error(`\u63D0\u4EA4\u7248\u672C\u5931\u8D25: ${res.data.msg || JSON.stringify(res.data)}`);
|
|
3922
|
-
}
|
|
3923
|
-
logger.info("\u7248\u672C\u4FE1\u606F\u5DF2\u63D0\u4EA4");
|
|
3924
|
-
}
|
|
3925
|
-
/**
|
|
3926
|
-
* 格式化错误信息
|
|
3927
|
-
*/
|
|
3928
|
-
formatError(error) {
|
|
3929
|
-
if (error.response?.data) {
|
|
3930
|
-
const data = error.response.data;
|
|
3931
|
-
return data.msg || data.message || error.message;
|
|
3932
|
-
}
|
|
3933
|
-
return error.message;
|
|
4666
|
+
if (res.data.code !== 0) throw new Error(`\u63D0\u4EA4\u5931\u8D25: ${res.data.msg}`);
|
|
3934
4667
|
}
|
|
3935
4668
|
};
|
|
3936
4669
|
|
|
3937
4670
|
// src/adapters/tencent_uploader.ts
|
|
3938
4671
|
var import_fs_extra12 = __toESM(require("fs-extra"), 1);
|
|
3939
|
-
var
|
|
4672
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
3940
4673
|
var import_axios9 = __toESM(require("axios"), 1);
|
|
3941
|
-
var
|
|
4674
|
+
var import_crypto7 = __toESM(require("crypto"), 1);
|
|
3942
4675
|
var TencentUploader = class _TencentUploader {
|
|
3943
4676
|
name = "Tencent";
|
|
3944
4677
|
/** 腾讯开放平台 API 基础地址 */
|
|
@@ -3951,7 +4684,7 @@ var TencentUploader = class _TencentUploader {
|
|
|
3951
4684
|
generateSign(params, accessSecret) {
|
|
3952
4685
|
const sortedKeys = Object.keys(params).sort();
|
|
3953
4686
|
const signStr = sortedKeys.filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
3954
|
-
return
|
|
4687
|
+
return import_crypto7.default.createHmac("sha256", accessSecret).update(signStr).digest("hex").toLowerCase();
|
|
3955
4688
|
}
|
|
3956
4689
|
validateConfig(config) {
|
|
3957
4690
|
if (!config?.publish?.tencent?.enable) return false;
|
|
@@ -3968,54 +4701,71 @@ var TencentUploader = class _TencentUploader {
|
|
|
3968
4701
|
async upload(filePath, config, metadata) {
|
|
3969
4702
|
const tencentConfig = config.publish.tencent;
|
|
3970
4703
|
const auth = tencentConfig.auth;
|
|
3971
|
-
logger.info(`Tencent \u8FD0\u884C\u65F6\u914D\u7F6E: ${JSON.stringify(tencentConfig)}`);
|
|
3972
4704
|
const consoleUrl = "https://app.open.qq.com";
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
}
|
|
4705
|
+
const pkgName = tencentConfig.packageName || "";
|
|
4706
|
+
const artifacts = typeof metadata.artifactPath === "object" ? metadata.artifactPath : { universal: filePath || (typeof metadata.artifactPath === "string" ? metadata.artifactPath : "") };
|
|
3976
4707
|
try {
|
|
3977
|
-
const
|
|
3978
|
-
const
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4708
|
+
const uploadResults = {};
|
|
4709
|
+
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
4710
|
+
if (!p || !import_fs_extra12.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
|
|
4711
|
+
const stats = import_fs_extra12.default.statSync(p);
|
|
4712
|
+
const fileName = import_path11.default.basename(p);
|
|
4713
|
+
logger.info(`[Tencent] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${fileName}`);
|
|
4714
|
+
const uploadInfo = await this.getFileUploadInfo(auth, stats.size, fileName, pkgName);
|
|
4715
|
+
await this.uploadToCos(uploadInfo.pre_sign_url, p, stats.size);
|
|
4716
|
+
const pkgInfo = await parsePackageInfo(p, {
|
|
4717
|
+
androidSdkPath: config.globalSettings?.androidSdkPath
|
|
4718
|
+
});
|
|
4719
|
+
const nativeCodes = pkgInfo?.nativeCode || [];
|
|
4720
|
+
const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64") || abi === "arm64-v8a";
|
|
4721
|
+
const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || nativeCodes.includes("x86") || abi === "armeabi-v7a";
|
|
4722
|
+
uploadResults[abi] = {
|
|
4723
|
+
serial_number: uploadInfo.serial_number,
|
|
4724
|
+
is64Bit: has64Bit && !has32Bit,
|
|
4725
|
+
md5: pkgInfo?.md5 || ""
|
|
4726
|
+
};
|
|
4727
|
+
});
|
|
4728
|
+
await Promise.all(uploadPromises);
|
|
4729
|
+
logger.info("[Tencent] \u6B63\u5728\u83B7\u53D6\u7EBF\u4E0A\u5E94\u7528\u8BE6\u60C5...");
|
|
4730
|
+
const anyPkgPath = Object.values(artifacts)[0];
|
|
4731
|
+
const samplePkgInfo = await parsePackageInfo(anyPkgPath, {
|
|
3987
4732
|
androidSdkPath: config.globalSettings?.androidSdkPath
|
|
3988
4733
|
});
|
|
3989
|
-
const nativeCodes = pkgInfo?.nativeCode || [];
|
|
3990
|
-
const has64Bit = nativeCodes.includes("arm64-v8a") || nativeCodes.includes("x86_64");
|
|
3991
|
-
const has32Bit = nativeCodes.includes("armeabi-v7a") || nativeCodes.includes("armeabi") || nativeCodes.includes("x86");
|
|
3992
|
-
const is64Bit = has64Bit && !has32Bit;
|
|
3993
|
-
const md5 = pkgInfo?.md5;
|
|
3994
4734
|
const onlineDetail = await this.queryAppDetail(
|
|
3995
4735
|
auth,
|
|
3996
|
-
|
|
3997
|
-
);
|
|
3998
|
-
logger.info(`\u83B7\u53D6\u7EBF\u4E0A\u8BE6\u60C5\u6210\u529F: ${onlineDetail.app_name || "\u672A\u547D\u540D"}`);
|
|
3999
|
-
logger.info("\u6B63\u5728\u63D0\u4EA4\u6D41\u6C34\u53F7\u5B8C\u6210\u5BA1\u6838\u7533\u62A5...");
|
|
4000
|
-
const submitResult = await this.submitModifyAppInfo(
|
|
4001
|
-
auth,
|
|
4002
|
-
{
|
|
4003
|
-
...onlineDetail,
|
|
4004
|
-
// 以前序详情为蓝本
|
|
4005
|
-
pkg_name: tencentConfig.packageName || pkgInfo?.packageName || onlineDetail.pkg_name || "",
|
|
4006
|
-
serial_number: uploadInfo.serial_number,
|
|
4007
|
-
update_desc: metadata.changelog || "\u7248\u672C\u66F4\u65B0",
|
|
4008
|
-
is64Bit,
|
|
4009
|
-
md5
|
|
4010
|
-
},
|
|
4011
|
-
{ timeout: tencentConfig.timeout || 6e4 }
|
|
4736
|
+
pkgName || samplePkgInfo?.packageName || ""
|
|
4012
4737
|
);
|
|
4013
|
-
|
|
4738
|
+
const submitData = {
|
|
4739
|
+
...onlineDetail,
|
|
4740
|
+
pkg_name: pkgName || samplePkgInfo?.packageName || onlineDetail.pkg_name || "",
|
|
4741
|
+
update_desc: metadata.changelog || "\u7248\u672C\u66F4\u65B0"
|
|
4742
|
+
};
|
|
4743
|
+
const arm64Result = uploadResults["arm64-v8a"] || Object.values(uploadResults).find((r) => r.is64Bit);
|
|
4744
|
+
const arm32Result = uploadResults["armeabi-v7a"] || Object.values(uploadResults).find((r) => !r.is64Bit);
|
|
4745
|
+
if (arm64Result) {
|
|
4746
|
+
submitData.apk64_serial_number = arm64Result.serial_number;
|
|
4747
|
+
submitData.apk64_md5 = arm64Result.md5;
|
|
4748
|
+
}
|
|
4749
|
+
if (arm32Result) {
|
|
4750
|
+
submitData.apk32_serial_number = arm32Result.serial_number;
|
|
4751
|
+
submitData.apk32_md5 = arm32Result.md5;
|
|
4752
|
+
}
|
|
4753
|
+
if (Object.keys(uploadResults).length === 1 && !submitData.apk64_serial_number && !submitData.apk32_serial_number) {
|
|
4754
|
+
const single = Object.values(uploadResults)[0];
|
|
4755
|
+
if (single.is64Bit) {
|
|
4756
|
+
submitData.apk64_serial_number = single.serial_number;
|
|
4757
|
+
submitData.apk64_md5 = single.md5;
|
|
4758
|
+
} else {
|
|
4759
|
+
submitData.apk32_serial_number = single.serial_number;
|
|
4760
|
+
submitData.apk32_md5 = single.md5;
|
|
4761
|
+
}
|
|
4762
|
+
}
|
|
4763
|
+
logger.info("[Tencent] \u6B63\u5728\u63D0\u4EA4\u5BA1\u6838\u7533\u62A5...");
|
|
4764
|
+
await this.submitModifyAppInfo(auth, submitData, { timeout: tencentConfig.timeout || 6e4 });
|
|
4014
4765
|
logger.success("\u817E\u8BAF\u5E94\u7528\u5B9D\u4E0A\u4F20\u5E76\u63D0\u4EA4\u6210\u529F!");
|
|
4015
4766
|
return { platform: "tencent", success: true, message: "\u4E0A\u4F20\u5E76\u63D0\u4EA4\u5BA1\u6838\u6210\u529F", consoleUrl };
|
|
4016
4767
|
} catch (error) {
|
|
4017
|
-
logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${
|
|
4018
|
-
logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${error.stack}`);
|
|
4768
|
+
logger.error(`\u817E\u8BAF\u4E0A\u4F20\u5931\u8D25: ${error.message}`);
|
|
4019
4769
|
return { platform: "tencent", success: false, message: error.message, error, consoleUrl };
|
|
4020
4770
|
}
|
|
4021
4771
|
}
|
|
@@ -4129,16 +4879,22 @@ var TencentUploader = class _TencentUploader {
|
|
|
4129
4879
|
signParams[key] = String(data[key]);
|
|
4130
4880
|
}
|
|
4131
4881
|
});
|
|
4132
|
-
if (data.
|
|
4133
|
-
signParams.apk64_file_serial_number = String(data.
|
|
4134
|
-
if (data.
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
signParams.
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4882
|
+
if (data.apk64_serial_number) {
|
|
4883
|
+
signParams.apk64_file_serial_number = String(data.apk64_serial_number);
|
|
4884
|
+
if (data.apk64_md5) signParams.apk64_file_md5 = data.apk64_md5;
|
|
4885
|
+
}
|
|
4886
|
+
if (data.apk32_serial_number) {
|
|
4887
|
+
signParams.apk32_file_serial_number = String(data.apk32_serial_number);
|
|
4888
|
+
if (data.apk32_md5) signParams.apk32_file_md5 = data.apk32_md5;
|
|
4889
|
+
}
|
|
4890
|
+
if (!data.apk64_serial_number && !data.apk32_serial_number && data.serial_number) {
|
|
4891
|
+
if (data.is64Bit) {
|
|
4892
|
+
signParams.apk64_file_serial_number = String(data.serial_number || "");
|
|
4893
|
+
if (data.md5) signParams.apk64_file_md5 = data.md5;
|
|
4894
|
+
} else {
|
|
4895
|
+
signParams.apk32_file_serial_number = String(data.serial_number || "");
|
|
4896
|
+
if (data.md5) signParams.apk32_file_md5 = data.md5;
|
|
4897
|
+
}
|
|
4142
4898
|
}
|
|
4143
4899
|
const sign = this.generateSign(signParams, accessSecret);
|
|
4144
4900
|
const formParams = {
|
|
@@ -4156,27 +4912,256 @@ var TencentUploader = class _TencentUploader {
|
|
|
4156
4912
|
return res.data;
|
|
4157
4913
|
}
|
|
4158
4914
|
};
|
|
4915
|
+
|
|
4916
|
+
// src/adapters/google_play_uploader.ts
|
|
4917
|
+
var import_fs_extra13 = __toESM(require("fs-extra"), 1);
|
|
4918
|
+
var import_path12 = __toESM(require("path"), 1);
|
|
4919
|
+
var import_axios10 = __toESM(require("axios"), 1);
|
|
4920
|
+
var import_google_auth_library = require("google-auth-library");
|
|
4921
|
+
var ANDROID_PUBLISHER_SCOPE = "https://www.googleapis.com/auth/androidpublisher";
|
|
4922
|
+
var PUBLISH_API_ROOT = "https://androidpublisher.googleapis.com/androidpublisher/v3";
|
|
4923
|
+
var UPLOAD_API_ROOT = "https://androidpublisher.googleapis.com/upload/androidpublisher/v3";
|
|
4924
|
+
var GOOGLE_PLAY_CONSOLE_URL = "https://play.google.com/console/developers";
|
|
4925
|
+
var GOOGLE_PLAY_INTERNAL_API_TRACK = "internal";
|
|
4926
|
+
var UPLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4927
|
+
var GooglePlayUploader = class {
|
|
4928
|
+
name = "Google Play";
|
|
4929
|
+
httpClient;
|
|
4930
|
+
getAccessToken;
|
|
4931
|
+
constructor(dependencies = {}) {
|
|
4932
|
+
this.httpClient = dependencies.httpClient || import_axios10.default;
|
|
4933
|
+
this.getAccessToken = dependencies.getAccessToken || this.createAccessToken.bind(this);
|
|
4934
|
+
}
|
|
4935
|
+
validateConfig(config) {
|
|
4936
|
+
const googleConfig = config?.publish?.google_play;
|
|
4937
|
+
if (!googleConfig?.enable) return false;
|
|
4938
|
+
if (!googleConfig.packageName || typeof googleConfig.packageName !== "string") {
|
|
4939
|
+
logger.error("Google Play \u914D\u7F6E\u7F3A\u5C11 packageName");
|
|
4940
|
+
return false;
|
|
4941
|
+
}
|
|
4942
|
+
const track = googleConfig.track || "internal";
|
|
4943
|
+
if (track !== "internal") {
|
|
4944
|
+
logger.error("Google Play \u9996\u7248\u4EC5\u652F\u6301 internal \u8F68\u9053");
|
|
4945
|
+
return false;
|
|
4946
|
+
}
|
|
4947
|
+
const auth = googleConfig.auth;
|
|
4948
|
+
const hasCredentialContent = typeof auth?.serviceAccountJsonContent === "string" && auth.serviceAccountJsonContent.trim().length > 0;
|
|
4949
|
+
const credentialPath = auth?.serviceAccountJsonPath;
|
|
4950
|
+
const hasCredentialPath = typeof credentialPath === "string" && credentialPath.trim().length > 0 && import_fs_extra13.default.existsSync(import_path12.default.resolve(credentialPath));
|
|
4951
|
+
if (!hasCredentialContent && !hasCredentialPath) {
|
|
4952
|
+
logger.error("Google Play \u914D\u7F6E\u7F3A\u5C11\u6709\u6548\u7684\u670D\u52A1\u8D26\u53F7 JSON \u6587\u4EF6\u6216\u5185\u5BB9");
|
|
4953
|
+
return false;
|
|
4954
|
+
}
|
|
4955
|
+
return true;
|
|
4956
|
+
}
|
|
4957
|
+
canHandle(filePath) {
|
|
4958
|
+
return import_path12.default.extname(filePath).toLowerCase() === ".aab";
|
|
4959
|
+
}
|
|
4960
|
+
async upload(filePath, config, _metadata) {
|
|
4961
|
+
if (!this.validateConfig(config)) {
|
|
4962
|
+
return this.failure("Google Play \u914D\u7F6E\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u5305\u540D\u3001internal \u8F68\u9053\u548C\u670D\u52A1\u8D26\u53F7\u6587\u4EF6");
|
|
4963
|
+
}
|
|
4964
|
+
if (!this.canHandle(filePath)) {
|
|
4965
|
+
return this.failure("Google Play \u4EC5\u63A5\u53D7 Android App Bundle\uFF08.aab\uFF09");
|
|
4966
|
+
}
|
|
4967
|
+
if (!import_fs_extra13.default.existsSync(filePath)) {
|
|
4968
|
+
return this.failure(`\u672A\u627E\u5230\u5F85\u4E0A\u4F20\u7684 AAB\uFF1A${filePath}`);
|
|
4969
|
+
}
|
|
4970
|
+
const googleConfig = config.publish.google_play;
|
|
4971
|
+
const packageName = googleConfig.packageName.trim();
|
|
4972
|
+
const encodedPackageName = encodeURIComponent(packageName);
|
|
4973
|
+
let editId;
|
|
4974
|
+
let accessToken;
|
|
4975
|
+
let committed = false;
|
|
4976
|
+
let stage = "create_edit";
|
|
4977
|
+
logger.info(`\u51C6\u5907\u5C06 ${import_path12.default.basename(filePath)} \u4E0A\u4F20\u5230 Google Play internal \u8349\u7A3F`);
|
|
4978
|
+
try {
|
|
4979
|
+
accessToken = await this.getAccessToken(googleConfig.auth);
|
|
4980
|
+
const requestConfig = {
|
|
4981
|
+
headers: { Authorization: `Bearer ${accessToken}` }
|
|
4982
|
+
};
|
|
4983
|
+
const editResponse = await this.httpClient.post(
|
|
4984
|
+
`${PUBLISH_API_ROOT}/applications/${encodedPackageName}/edits`,
|
|
4985
|
+
{},
|
|
4986
|
+
requestConfig
|
|
4987
|
+
);
|
|
4988
|
+
editId = editResponse.data.id;
|
|
4989
|
+
if (!editId) {
|
|
4990
|
+
throw new Error("Google Play \u672A\u8FD4\u56DE editId");
|
|
4991
|
+
}
|
|
4992
|
+
stage = "upload_bundle";
|
|
4993
|
+
const encodedEditId = encodeURIComponent(editId);
|
|
4994
|
+
const bundleResponse = await this.httpClient.post(
|
|
4995
|
+
`${UPLOAD_API_ROOT}/applications/${encodedPackageName}/edits/${encodedEditId}/bundles?uploadType=media`,
|
|
4996
|
+
import_fs_extra13.default.createReadStream(filePath),
|
|
4997
|
+
{
|
|
4998
|
+
timeout: UPLOAD_TIMEOUT_MS,
|
|
4999
|
+
maxBodyLength: Infinity,
|
|
5000
|
+
maxContentLength: Infinity,
|
|
5001
|
+
headers: {
|
|
5002
|
+
Authorization: `Bearer ${accessToken}`,
|
|
5003
|
+
"Content-Type": "application/octet-stream",
|
|
5004
|
+
"Content-Length": import_fs_extra13.default.statSync(filePath).size
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
);
|
|
5008
|
+
const versionCode = bundleResponse.data.versionCode;
|
|
5009
|
+
if (!Number.isInteger(versionCode)) {
|
|
5010
|
+
throw new Error("Google Play \u4E0A\u4F20\u6210\u529F\u4F46\u672A\u8FD4\u56DE\u6709\u6548\u7684 versionCode");
|
|
5011
|
+
}
|
|
5012
|
+
stage = "update_internal_track";
|
|
5013
|
+
await this.httpClient.put(
|
|
5014
|
+
`${PUBLISH_API_ROOT}/applications/${encodedPackageName}/edits/${encodedEditId}/tracks/${GOOGLE_PLAY_INTERNAL_API_TRACK}`,
|
|
5015
|
+
{
|
|
5016
|
+
track: GOOGLE_PLAY_INTERNAL_API_TRACK,
|
|
5017
|
+
releases: [
|
|
5018
|
+
{
|
|
5019
|
+
versionCodes: [String(versionCode)],
|
|
5020
|
+
status: "draft"
|
|
5021
|
+
}
|
|
5022
|
+
]
|
|
5023
|
+
},
|
|
5024
|
+
requestConfig
|
|
5025
|
+
);
|
|
5026
|
+
stage = "validate_edit";
|
|
5027
|
+
await this.httpClient.post(
|
|
5028
|
+
`${PUBLISH_API_ROOT}/applications/${encodedPackageName}/edits/${encodedEditId}:validate`,
|
|
5029
|
+
{},
|
|
5030
|
+
requestConfig
|
|
5031
|
+
);
|
|
5032
|
+
stage = "commit_edit";
|
|
5033
|
+
await this.httpClient.post(
|
|
5034
|
+
`${PUBLISH_API_ROOT}/applications/${encodedPackageName}/edits/${encodedEditId}:commit`,
|
|
5035
|
+
{},
|
|
5036
|
+
requestConfig
|
|
5037
|
+
);
|
|
5038
|
+
committed = true;
|
|
5039
|
+
logger.success(`Google Play internal \u8349\u7A3F\u5DF2\u521B\u5EFA\uFF08versionCode ${versionCode}\uFF09`);
|
|
5040
|
+
return {
|
|
5041
|
+
platform: "google_play",
|
|
5042
|
+
success: true,
|
|
5043
|
+
message: `AAB \u5DF2\u4E0A\u4F20\u5230 Google Play internal \u8349\u7A3F\uFF08versionCode ${versionCode}\uFF09`,
|
|
5044
|
+
consoleUrl: GOOGLE_PLAY_CONSOLE_URL,
|
|
5045
|
+
data: {
|
|
5046
|
+
editId,
|
|
5047
|
+
versionCode,
|
|
5048
|
+
track: "internal",
|
|
5049
|
+
apiTrack: GOOGLE_PLAY_INTERNAL_API_TRACK,
|
|
5050
|
+
status: "draft",
|
|
5051
|
+
committed: true
|
|
5052
|
+
}
|
|
5053
|
+
};
|
|
5054
|
+
} catch (error) {
|
|
5055
|
+
if (editId && accessToken && !committed) {
|
|
5056
|
+
await this.deleteEditBestEffort(encodedPackageName, editId, accessToken);
|
|
5057
|
+
}
|
|
5058
|
+
const message = this.describeError(error, stage);
|
|
5059
|
+
logger.error(`Google Play \u4E0A\u4F20\u5931\u8D25\uFF1A${message}`);
|
|
5060
|
+
return this.failure(message, error);
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
async createAccessToken(authConfig) {
|
|
5064
|
+
let credentials;
|
|
5065
|
+
if (authConfig.serviceAccountJsonContent?.trim()) {
|
|
5066
|
+
try {
|
|
5067
|
+
credentials = JSON.parse(authConfig.serviceAccountJsonContent);
|
|
5068
|
+
} catch {
|
|
5069
|
+
throw new Error("\u670D\u52A1\u8D26\u53F7 JSON \u5185\u5BB9\u683C\u5F0F\u65E0\u6548");
|
|
5070
|
+
}
|
|
5071
|
+
}
|
|
5072
|
+
const keyFile = !credentials && authConfig.serviceAccountJsonPath ? import_path12.default.resolve(authConfig.serviceAccountJsonPath) : void 0;
|
|
5073
|
+
const auth = new import_google_auth_library.GoogleAuth({
|
|
5074
|
+
scopes: [ANDROID_PUBLISHER_SCOPE],
|
|
5075
|
+
keyFile,
|
|
5076
|
+
credentials
|
|
5077
|
+
});
|
|
5078
|
+
const token = await auth.getAccessToken();
|
|
5079
|
+
if (!token) {
|
|
5080
|
+
throw new Error("Google Play \u8BA4\u8BC1\u672A\u8FD4\u56DE\u8BBF\u95EE\u4EE4\u724C");
|
|
5081
|
+
}
|
|
5082
|
+
return token;
|
|
5083
|
+
}
|
|
5084
|
+
async deleteEditBestEffort(encodedPackageName, editId, accessToken) {
|
|
5085
|
+
try {
|
|
5086
|
+
await this.httpClient.delete(
|
|
5087
|
+
`${PUBLISH_API_ROOT}/applications/${encodedPackageName}/edits/${encodeURIComponent(editId)}`,
|
|
5088
|
+
{ headers: { Authorization: `Bearer ${accessToken}` } }
|
|
5089
|
+
);
|
|
5090
|
+
} catch {
|
|
5091
|
+
logger.warn("Google Play \u672A\u63D0\u4EA4 edit \u7684\u81EA\u52A8\u6E05\u7406\u5931\u8D25\uFF0C\u8BF7\u5728 Play Console \u4E2D\u68C0\u67E5\u8349\u7A3F\u72B6\u6001");
|
|
5092
|
+
}
|
|
5093
|
+
}
|
|
5094
|
+
describeError(error, stage) {
|
|
5095
|
+
const status = error?.response?.status;
|
|
5096
|
+
const rawMessage = error?.response?.data?.error?.message || error?.response?.data?.message || error?.message || "\u672A\u77E5\u9519\u8BEF";
|
|
5097
|
+
const safeMessage = this.redactAndTruncate(String(rawMessage));
|
|
5098
|
+
if (/version\s*code|versionCode/i.test(safeMessage)) {
|
|
5099
|
+
return `Google Play \u62D2\u7EDD\u8BE5 versionCode\uFF0C\u8BF7\u63D0\u9AD8 Flutter \u6784\u5EFA\u53F7\u540E\u91CD\u65B0\u751F\u6210 AAB\uFF1A${safeMessage}`;
|
|
5100
|
+
}
|
|
5101
|
+
if (status === 401) {
|
|
5102
|
+
return "Google Play \u8BA4\u8BC1\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u670D\u52A1\u8D26\u53F7 JSON \u662F\u5426\u6709\u6548";
|
|
5103
|
+
}
|
|
5104
|
+
if (status === 403) {
|
|
5105
|
+
return "Google Play \u62D2\u7EDD\u8BBF\u95EE\uFF0C\u8BF7\u786E\u8BA4\u670D\u52A1\u8D26\u53F7\u5DF2\u52A0\u5165 Play Console \u5E76\u5177\u6709\u5185\u90E8\u6D4B\u8BD5\u7248\u672C\u4E0A\u4F20\u6743\u9650";
|
|
5106
|
+
}
|
|
5107
|
+
if (status === 404) {
|
|
5108
|
+
if (stage === "update_internal_track") {
|
|
5109
|
+
return "Google Play \u4E2D\u672A\u627E\u5230 internal \u5185\u90E8\u6D4B\u8BD5\u8F68\u9053\uFF0C\u8BF7\u786E\u8BA4\u5E94\u7528\u5DF2\u5728 Play Console \u521B\u5EFA\u5185\u90E8\u6D4B\u8BD5\u8F68\u9053";
|
|
5110
|
+
}
|
|
5111
|
+
if (stage !== "create_edit") {
|
|
5112
|
+
return `Google Play API \u5728 ${stage} \u9636\u6BB5\u672A\u627E\u5230\u76EE\u6807\u8D44\u6E90\uFF1A${safeMessage}`;
|
|
5113
|
+
}
|
|
5114
|
+
return "Google Play \u4E2D\u672A\u627E\u5230\u53EF\u4F9B API \u7F16\u8F91\u7684\u5E94\u7528\u3002\u8BF7\u786E\u8BA4 packageName \u5B8C\u5168\u4E00\u81F4\uFF1B\u82E5\u8FD9\u662F\u9996\u6B21\u4E0A\u4F20\uFF0C\u8BF7\u5148\u5728 Play Console \u624B\u5DE5\u4E0A\u4F20\u5E76\u4FDD\u5B58\u4E00\u4E2A APK/AAB\uFF0C\u518D\u91CD\u8BD5 API \u4E0A\u4F20";
|
|
5115
|
+
}
|
|
5116
|
+
if (error?.code === "ECONNABORTED" || /timeout/i.test(safeMessage)) {
|
|
5117
|
+
return "Google Play \u4E0A\u4F20\u8D85\u65F6\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\uFF1B\u672C\u5730 AAB \u672A\u88AB\u5220\u9664";
|
|
5118
|
+
}
|
|
5119
|
+
return `Google Play API \u8BF7\u6C42\u5931\u8D25${status ? `\uFF08HTTP ${status}\uFF09` : ""}\uFF1A${safeMessage}`;
|
|
5120
|
+
}
|
|
5121
|
+
redactAndTruncate(message) {
|
|
5122
|
+
return message.replace(/-----BEGIN PRIVATE KEY-----[\s\S]*?-----END PRIVATE KEY-----/gi, "[\u5DF2\u8131\u654F\u79C1\u94A5]").replace(/Bearer\s+[^\s"']+/gi, "Bearer [\u5DF2\u8131\u654F]").replace(/"private_key"\s*:\s*"[^"]*"/gi, '"private_key":"[\u5DF2\u8131\u654F]"').replace(/"access_token"\s*:\s*"[^"]*"/gi, '"access_token":"[\u5DF2\u8131\u654F]"').slice(0, 500);
|
|
5123
|
+
}
|
|
5124
|
+
failure(message, error) {
|
|
5125
|
+
return {
|
|
5126
|
+
platform: "google_play",
|
|
5127
|
+
success: false,
|
|
5128
|
+
message,
|
|
5129
|
+
// 原始 Axios 错误可能包含 Authorization 请求头,只返回脱敏后的 Error。
|
|
5130
|
+
error: error ? new Error(message) : void 0
|
|
5131
|
+
};
|
|
5132
|
+
}
|
|
5133
|
+
};
|
|
4159
5134
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4160
5135
|
0 && (module.exports = {
|
|
4161
5136
|
AdHocUploader,
|
|
4162
5137
|
AppleJWTGenerator,
|
|
4163
5138
|
AppleStoreUploader,
|
|
4164
5139
|
ConfigValidator,
|
|
5140
|
+
DEFAULT_RELEASE_RECEIPT_LIMIT,
|
|
4165
5141
|
DEFAULT_RETRY_CONFIG,
|
|
4166
5142
|
EnvValidator,
|
|
4167
5143
|
ErrorCategory,
|
|
5144
|
+
GooglePlayUploader,
|
|
4168
5145
|
HarmonyUploader,
|
|
4169
5146
|
HuaweiUploader,
|
|
4170
5147
|
OppoUploader,
|
|
4171
5148
|
PgyerUploader,
|
|
5149
|
+
RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
5150
|
+
ReleaseReceiptStore,
|
|
4172
5151
|
RetryStrategy,
|
|
4173
5152
|
TencentUploader,
|
|
4174
5153
|
TestFlightUploader,
|
|
4175
5154
|
UploadManager,
|
|
4176
5155
|
VivoUploader,
|
|
4177
5156
|
XiaomiUploader,
|
|
5157
|
+
createProjectKey,
|
|
5158
|
+
createReleaseReceipt,
|
|
4178
5159
|
createUploadManager,
|
|
4179
5160
|
createUploadRetryStrategy,
|
|
4180
|
-
|
|
5161
|
+
createUploadTargets,
|
|
5162
|
+
formatReleaseReceipt,
|
|
5163
|
+
parsePackageInfo,
|
|
5164
|
+
resolveReleaseProjectRoot,
|
|
5165
|
+
sanitizeReleaseMessage
|
|
4181
5166
|
});
|
|
4182
5167
|
//# sourceMappingURL=index.cjs.map
|