@huoye/app-ship 0.1.2 → 0.2.1
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/README.md +21 -3
- package/dist/cli.js +733 -75
- package/dist/index.cjs +655 -67
- package/dist/index.d.cts +137 -14
- package/dist/index.d.ts +137 -14
- package/dist/index.js +629 -51
- package/package.json +11 -7
- 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,22 +34,32 @@ __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
|
|
|
@@ -600,16 +610,18 @@ var ConfigValidator = class {
|
|
|
600
610
|
logger.error(`Config file not found: ${filePath}`);
|
|
601
611
|
return null;
|
|
602
612
|
}
|
|
603
|
-
let content = import_fs_extra.default.readFileSync(filePath, "utf8");
|
|
604
|
-
const envRegex = /\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}/g;
|
|
605
|
-
const matches = [...content.matchAll(envRegex)];
|
|
606
|
-
const requiredEnvs = Array.from(new Set(matches.map((m) => m[1])));
|
|
607
|
-
if (!EnvValidator.validate(requiredEnvs)) {
|
|
608
|
-
return null;
|
|
609
|
-
}
|
|
610
|
-
content = content.replace(envRegex, (_, varName) => process.env[varName] || "");
|
|
611
613
|
try {
|
|
612
|
-
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);
|
|
613
625
|
if (!this.validateSchema(config)) {
|
|
614
626
|
return null;
|
|
615
627
|
}
|
|
@@ -619,15 +631,71 @@ var ConfigValidator = class {
|
|
|
619
631
|
return null;
|
|
620
632
|
}
|
|
621
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
|
+
}
|
|
622
682
|
/**
|
|
623
683
|
* 验证配置 Schema
|
|
624
684
|
* @param config 配置对象
|
|
625
685
|
*/
|
|
626
686
|
static validateSchema(config) {
|
|
627
687
|
if (config.publish?.app_store?.enable) {
|
|
688
|
+
const mode = config.publish.app_store.mode || "app_store";
|
|
628
689
|
const auth = config.publish.app_store.auth;
|
|
629
|
-
|
|
630
|
-
|
|
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
|
+
);
|
|
631
699
|
return false;
|
|
632
700
|
}
|
|
633
701
|
}
|
|
@@ -673,6 +741,20 @@ var ConfigValidator = class {
|
|
|
673
741
|
return false;
|
|
674
742
|
}
|
|
675
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
|
+
}
|
|
676
758
|
return true;
|
|
677
759
|
}
|
|
678
760
|
};
|
|
@@ -1014,6 +1096,7 @@ var UploadManager = class {
|
|
|
1014
1096
|
this.registerUploader("oppo", new OppoUploader());
|
|
1015
1097
|
this.registerUploader("vivo", new VivoUploader());
|
|
1016
1098
|
this.registerUploader("tencent", new TencentUploader());
|
|
1099
|
+
this.registerUploader("google_play", new GooglePlayUploader());
|
|
1017
1100
|
}
|
|
1018
1101
|
/**
|
|
1019
1102
|
* 注册上传器
|
|
@@ -1032,6 +1115,8 @@ var UploadManager = class {
|
|
|
1032
1115
|
if (config?.publish?.oppo?.enable) enabled.push("oppo");
|
|
1033
1116
|
if (config?.publish?.vivo?.enable) enabled.push("vivo");
|
|
1034
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");
|
|
1035
1120
|
if (config?.publish?.app_store?.enable) {
|
|
1036
1121
|
const mode = config.publish.app_store.mode || "app_store";
|
|
1037
1122
|
enabled.push(mode);
|
|
@@ -1175,7 +1260,7 @@ var UploadManager = class {
|
|
|
1175
1260
|
artifactPath: artifactSelection
|
|
1176
1261
|
};
|
|
1177
1262
|
await this.validateArtifactSelection(platform, artifactSelection, effectiveMetadata);
|
|
1178
|
-
const pkgInfo = await parsePackageInfo(filePath, {
|
|
1263
|
+
const pkgInfo = import_path2.default.extname(filePath).toLowerCase() === ".aab" ? null : await parsePackageInfo(filePath, {
|
|
1179
1264
|
androidSdkPath: effectiveMetadata.globalSettings?.androidSdkPath
|
|
1180
1265
|
});
|
|
1181
1266
|
if (pkgInfo) {
|
|
@@ -1532,6 +1617,280 @@ var AppleJWTGenerator = class _AppleJWTGenerator {
|
|
|
1532
1617
|
}
|
|
1533
1618
|
};
|
|
1534
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
|
+
|
|
1535
1894
|
// src/adapters/pgyer_uploader.ts
|
|
1536
1895
|
var PgyerUploader = class _PgyerUploader {
|
|
1537
1896
|
name = "Pgyer";
|
|
@@ -1650,7 +2009,7 @@ var PgyerUploader = class _PgyerUploader {
|
|
|
1650
2009
|
|
|
1651
2010
|
// src/adapters/huawei_uploader.ts
|
|
1652
2011
|
var import_fs_extra4 = __toESM(require("fs-extra"), 1);
|
|
1653
|
-
var
|
|
2012
|
+
var import_path4 = __toESM(require("path"), 1);
|
|
1654
2013
|
var import_util = require("util");
|
|
1655
2014
|
var import_axios2 = __toESM(require("axios"), 1);
|
|
1656
2015
|
var import_form_data2 = __toESM(require("form-data"), 1);
|
|
@@ -1729,7 +2088,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1729
2088
|
const clientSecret = hwConfig.auth.clientSecret;
|
|
1730
2089
|
const appId = hwConfig.appId;
|
|
1731
2090
|
const releaseType = hwConfig.releaseType || "draft";
|
|
1732
|
-
const suffix =
|
|
2091
|
+
const suffix = import_path4.default.extname(filePath).substring(1);
|
|
1733
2092
|
const retryConfig = config.retry;
|
|
1734
2093
|
const tokenUrl = this.getTokenUrl(config);
|
|
1735
2094
|
const publishApiRoot = this.getApiRoot(config);
|
|
@@ -1755,8 +2114,8 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1755
2114
|
const uploadResults = [];
|
|
1756
2115
|
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
1757
2116
|
if (!p || !import_fs_extra4.default.existsSync(p)) return;
|
|
1758
|
-
const currentSuffix =
|
|
1759
|
-
logger.info(`[Huawei] \u6B63\u5728\u4E0A\u4F20 ${abi} \u5305: ${
|
|
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)}`);
|
|
1760
2119
|
const uploadInfo = await retryStrategy.execute(
|
|
1761
2120
|
() => this.getUploadUrl(ctx, currentSuffix),
|
|
1762
2121
|
`Huawei get upload URL for ${abi}`
|
|
@@ -1772,7 +2131,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1772
2131
|
throw uploadFileRes.error || new Error(`File upload failed for ${abi}`);
|
|
1773
2132
|
}
|
|
1774
2133
|
uploadResults.push({
|
|
1775
|
-
fileName:
|
|
2134
|
+
fileName: import_path4.default.basename(p),
|
|
1776
2135
|
fileDestUrl: uploadFileRes.data[0].fileDestUrl
|
|
1777
2136
|
});
|
|
1778
2137
|
});
|
|
@@ -1944,7 +2303,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
1944
2303
|
}
|
|
1945
2304
|
/** 通用文件上传(FormData 方式,无需 API 认证头) */
|
|
1946
2305
|
async executeUploadRequest(uploadUrl, authCode, filePath) {
|
|
1947
|
-
const fileName =
|
|
2306
|
+
const fileName = import_path4.default.basename(filePath);
|
|
1948
2307
|
const fileSize = import_fs_extra4.default.statSync(filePath).size;
|
|
1949
2308
|
const form = new import_form_data2.default();
|
|
1950
2309
|
form.append("file", import_fs_extra4.default.createReadStream(filePath), {
|
|
@@ -2111,7 +2470,7 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
2111
2470
|
logger.warn(`Asset file not found, skipping: ${filePath}`);
|
|
2112
2471
|
continue;
|
|
2113
2472
|
}
|
|
2114
|
-
const suffix =
|
|
2473
|
+
const suffix = import_path4.default.extname(filePath).substring(1).toLowerCase();
|
|
2115
2474
|
if (!HUAWEI_SUPPORTED_SUFFIXES.image.includes(suffix)) {
|
|
2116
2475
|
logger.warn(`Unsupported file type ${suffix}, skipping: ${filePath}`);
|
|
2117
2476
|
continue;
|
|
@@ -2166,10 +2525,10 @@ var HuaweiUploader = class _HuaweiUploader {
|
|
|
2166
2525
|
|
|
2167
2526
|
// src/adapters/xiaomi_uploader.ts
|
|
2168
2527
|
var import_fs_extra5 = __toESM(require("fs-extra"), 1);
|
|
2169
|
-
var
|
|
2528
|
+
var import_path5 = __toESM(require("path"), 1);
|
|
2170
2529
|
var import_axios3 = __toESM(require("axios"), 1);
|
|
2171
2530
|
var import_form_data3 = __toESM(require("form-data"), 1);
|
|
2172
|
-
var
|
|
2531
|
+
var import_crypto4 = __toESM(require("crypto"), 1);
|
|
2173
2532
|
var XiaomiUploader = class _XiaomiUploader {
|
|
2174
2533
|
name = "Xiaomi";
|
|
2175
2534
|
static API_BASE = "https://api.developer.xiaomi.com/devupload";
|
|
@@ -2189,10 +2548,10 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
2189
2548
|
const chunks = [];
|
|
2190
2549
|
for (let offset = 0; offset < buffer.length; offset += maxBlock) {
|
|
2191
2550
|
const chunk = buffer.subarray(offset, offset + maxBlock);
|
|
2192
|
-
const encrypted =
|
|
2551
|
+
const encrypted = import_crypto4.default.publicEncrypt(
|
|
2193
2552
|
{
|
|
2194
2553
|
key: publicKey,
|
|
2195
|
-
padding:
|
|
2554
|
+
padding: import_crypto4.default.constants.RSA_PKCS1_PADDING
|
|
2196
2555
|
},
|
|
2197
2556
|
chunk
|
|
2198
2557
|
);
|
|
@@ -2206,15 +2565,15 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
2206
2565
|
*/
|
|
2207
2566
|
extractPublicKey(buffer) {
|
|
2208
2567
|
try {
|
|
2209
|
-
const x509 = new
|
|
2568
|
+
const x509 = new import_crypto4.default.X509Certificate(buffer);
|
|
2210
2569
|
return x509.publicKey;
|
|
2211
2570
|
} catch (e) {
|
|
2212
2571
|
try {
|
|
2213
2572
|
const content = buffer.toString();
|
|
2214
2573
|
if (content.includes("-----BEGIN")) {
|
|
2215
|
-
return
|
|
2574
|
+
return import_crypto4.default.createPublicKey(content);
|
|
2216
2575
|
}
|
|
2217
|
-
return
|
|
2576
|
+
return import_crypto4.default.createPublicKey(this.normalizePEM(content));
|
|
2218
2577
|
} catch (e2) {
|
|
2219
2578
|
throw new Error(`\u65E0\u6CD5\u63D0\u53D6\u516C\u94A5: ${e instanceof Error ? e.message : "\u672A\u77E5\u9519\u8BEF"}`);
|
|
2220
2579
|
}
|
|
@@ -2225,7 +2584,7 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
2225
2584
|
*/
|
|
2226
2585
|
async calculateFileMD5(filePath) {
|
|
2227
2586
|
return new Promise((resolve, reject) => {
|
|
2228
|
-
const hash =
|
|
2587
|
+
const hash = import_crypto4.default.createHash("md5");
|
|
2229
2588
|
const stream = import_fs_extra5.default.createReadStream(filePath);
|
|
2230
2589
|
stream.on("data", (data) => hash.update(data));
|
|
2231
2590
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -2236,7 +2595,7 @@ var XiaomiUploader = class _XiaomiUploader {
|
|
|
2236
2595
|
* 计算字符串 MD5 (小写十六进制)
|
|
2237
2596
|
*/
|
|
2238
2597
|
calculateStringMD5(str) {
|
|
2239
|
-
return
|
|
2598
|
+
return import_crypto4.default.createHash("md5").update(str, "utf-8").digest("hex");
|
|
2240
2599
|
}
|
|
2241
2600
|
/**
|
|
2242
2601
|
* 构建 RequestData JSON
|
|
@@ -2494,14 +2853,14 @@ ${lines.join("\n")}
|
|
|
2494
2853
|
if (arm64 && arm32) {
|
|
2495
2854
|
mainPath = arm64[1];
|
|
2496
2855
|
secondPath = arm32[1];
|
|
2497
|
-
logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u67B6\u6784\u5206\u5305: \u4E3B\u5305(64\u4F4D)=${
|
|
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)}`);
|
|
2498
2857
|
} else {
|
|
2499
2858
|
mainPath = artifactEntries[0][1];
|
|
2500
2859
|
if (artifactEntries.length > 1) {
|
|
2501
2860
|
secondPath = artifactEntries[1][1];
|
|
2502
|
-
logger.info(`[Xiaomi] \u8BC6\u522B\u5230\u591A\u4E2A\u5305\uFF08\u975E\u6807\u51C6\u540D\uFF09\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u987A\u5E8F: \u4E3B\u5305=${
|
|
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)}`);
|
|
2503
2862
|
} else {
|
|
2504
|
-
logger.info(`[Xiaomi] \u4F7F\u7528\u5355\u5305\u6A21\u5F0F: ${
|
|
2863
|
+
logger.info(`[Xiaomi] \u4F7F\u7528\u5355\u5305\u6A21\u5F0F: ${import_path5.default.basename(mainPath)}`);
|
|
2505
2864
|
}
|
|
2506
2865
|
}
|
|
2507
2866
|
const mainMD5 = await this.calculateFileMD5(mainPath);
|
|
@@ -2575,7 +2934,7 @@ ${lines.join("\n")}
|
|
|
2575
2934
|
|
|
2576
2935
|
// src/adapters/apple_store_uploader.ts
|
|
2577
2936
|
var import_fs_extra6 = __toESM(require("fs-extra"), 1);
|
|
2578
|
-
var
|
|
2937
|
+
var import_path6 = __toESM(require("path"), 1);
|
|
2579
2938
|
var import_child_process = require("child_process");
|
|
2580
2939
|
var import_util2 = require("util");
|
|
2581
2940
|
var import_axios4 = __toESM(require("axios"), 1);
|
|
@@ -2741,7 +3100,7 @@ var AppleStoreUploader = class {
|
|
|
2741
3100
|
appPassword,
|
|
2742
3101
|
"-showTransferRate"
|
|
2743
3102
|
].join(" ");
|
|
2744
|
-
logger.info(`[Transporter] Uploading: ${
|
|
3103
|
+
logger.info(`[Transporter] Uploading: ${import_path6.default.basename(ipaPath)}`);
|
|
2745
3104
|
try {
|
|
2746
3105
|
const { stdout, stderr } = await execAsync(command, {
|
|
2747
3106
|
timeout: 30 * 60 * 1e3,
|
|
@@ -2792,7 +3151,7 @@ Stderr: ${error.stderr || ""}`);
|
|
|
2792
3151
|
});
|
|
2793
3152
|
command = innerArgs.join(" ");
|
|
2794
3153
|
}
|
|
2795
|
-
logger.info(`[altool] Uploading: ${
|
|
3154
|
+
logger.info(`[altool] Uploading: ${import_path6.default.basename(ipaPath)}`);
|
|
2796
3155
|
logger.info(`[altool] Command prefix: ${tool.command}`);
|
|
2797
3156
|
try {
|
|
2798
3157
|
const { stdout, stderr } = await execAsync(command, {
|
|
@@ -3376,7 +3735,7 @@ var TestFlightUploader = class _TestFlightUploader {
|
|
|
3376
3735
|
|
|
3377
3736
|
// src/adapters/adhoc_uploader.ts
|
|
3378
3737
|
var import_fs_extra8 = __toESM(require("fs-extra"), 1);
|
|
3379
|
-
var
|
|
3738
|
+
var import_path7 = __toESM(require("path"), 1);
|
|
3380
3739
|
var import_child_process2 = require("child_process");
|
|
3381
3740
|
var AdHocUploader = class {
|
|
3382
3741
|
name = "AdHoc";
|
|
@@ -3509,8 +3868,8 @@ var AdHocUploader = class {
|
|
|
3509
3868
|
* @param changelog 更新日志
|
|
3510
3869
|
*/
|
|
3511
3870
|
async createManifest(ipaPath, version, bundleId, changelog) {
|
|
3512
|
-
const ipaFileName =
|
|
3513
|
-
const appName = bundleId ||
|
|
3871
|
+
const ipaFileName = import_path7.default.basename(ipaPath);
|
|
3872
|
+
const appName = bundleId || import_path7.default.parse(ipaFileName).name;
|
|
3514
3873
|
const ipaUrl = `itms-services://?action=download-manifest&url=https://your-distribution-server.com/manifests/${ipaFileName}.plist`;
|
|
3515
3874
|
const manifestContent = `<?xml version="1.0" encoding="UTF-8"?>
|
|
3516
3875
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
@@ -3569,14 +3928,14 @@ var AdHocUploader = class {
|
|
|
3569
3928
|
*/
|
|
3570
3929
|
async setupDistributionDirectory(ipaPath, manifestPath) {
|
|
3571
3930
|
const projectRoot = process.cwd();
|
|
3572
|
-
const distributionDir =
|
|
3931
|
+
const distributionDir = import_path7.default.join(projectRoot, "build", "adhoc-distribution");
|
|
3573
3932
|
await import_fs_extra8.default.ensureDir(distributionDir);
|
|
3574
|
-
await import_fs_extra8.default.ensureDir(
|
|
3575
|
-
await import_fs_extra8.default.ensureDir(
|
|
3576
|
-
await import_fs_extra8.default.ensureDir(
|
|
3577
|
-
const ipaFileName =
|
|
3578
|
-
await import_fs_extra8.default.copy(ipaPath,
|
|
3579
|
-
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`));
|
|
3580
3939
|
logger.info(`Distribution directory: ${distributionDir}`);
|
|
3581
3940
|
return distributionDir;
|
|
3582
3941
|
}
|
|
@@ -3586,7 +3945,7 @@ var AdHocUploader = class {
|
|
|
3586
3945
|
* @param bundleId Bundle ID
|
|
3587
3946
|
*/
|
|
3588
3947
|
generateInstallUrl(distributionPath, bundleId) {
|
|
3589
|
-
const manifestPath =
|
|
3948
|
+
const manifestPath = import_path7.default.join(distributionPath, "manifests", `*.plist`);
|
|
3590
3949
|
return `itms-services://?action=download-manifest&url=file://${manifestPath}`;
|
|
3591
3950
|
}
|
|
3592
3951
|
/**
|
|
@@ -3595,7 +3954,7 @@ var AdHocUploader = class {
|
|
|
3595
3954
|
* @param distributionPath 分发目录
|
|
3596
3955
|
*/
|
|
3597
3956
|
async generateQRCode(url, distributionPath) {
|
|
3598
|
-
const qrCodePath =
|
|
3957
|
+
const qrCodePath = import_path7.default.join(distributionPath, "install-qrcode.png");
|
|
3599
3958
|
try {
|
|
3600
3959
|
logger.info(`QR code would be saved to: ${qrCodePath}`);
|
|
3601
3960
|
logger.info(`Install URL: ${url}`);
|
|
@@ -3622,8 +3981,8 @@ var AdHocUploader = class {
|
|
|
3622
3981
|
logger.newLine();
|
|
3623
3982
|
logger.info("\u{1F4E6} Distribution Files:");
|
|
3624
3983
|
logger.info(` Directory: ${distributionPath}`);
|
|
3625
|
-
logger.info(` IPA: ${
|
|
3626
|
-
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")}`);
|
|
3627
3986
|
logger.info(` QR Code: ${qrCodePath}`);
|
|
3628
3987
|
logger.newLine();
|
|
3629
3988
|
logger.info("\u{1F517} Installation Link:");
|
|
@@ -3682,7 +4041,7 @@ var AdHocUploader = class {
|
|
|
3682
4041
|
|
|
3683
4042
|
// src/adapters/harmony_uploader.ts
|
|
3684
4043
|
var import_fs_extra9 = __toESM(require("fs-extra"), 1);
|
|
3685
|
-
var
|
|
4044
|
+
var import_path8 = __toESM(require("path"), 1);
|
|
3686
4045
|
var import_axios6 = __toESM(require("axios"), 1);
|
|
3687
4046
|
var import_form_data5 = __toESM(require("form-data"), 1);
|
|
3688
4047
|
var HarmonyUploader = class _HarmonyUploader {
|
|
@@ -3712,7 +4071,7 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3712
4071
|
const clientSecret = harmonyConfig.auth.clientSecret;
|
|
3713
4072
|
const appId = harmonyConfig.appId;
|
|
3714
4073
|
const releaseType = harmonyConfig.releaseType || "draft";
|
|
3715
|
-
const suffix =
|
|
4074
|
+
const suffix = import_path8.default.extname(filePath).substring(1);
|
|
3716
4075
|
const retryConfig = config.retry;
|
|
3717
4076
|
logger.info("Starting HarmonyOS AppGallery upload...");
|
|
3718
4077
|
try {
|
|
@@ -3829,7 +4188,7 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3829
4188
|
* @param filePath 文件路径
|
|
3830
4189
|
*/
|
|
3831
4190
|
async updateAppFileInfo(token, appId, authCode, filePath) {
|
|
3832
|
-
const fileName =
|
|
4191
|
+
const fileName = import_path8.default.basename(filePath);
|
|
3833
4192
|
const body = {
|
|
3834
4193
|
fileType: 7,
|
|
3835
4194
|
// 鸿蒙包的 fileType(需要根据华为最新文档确认)
|
|
@@ -3852,10 +4211,10 @@ var HarmonyUploader = class _HarmonyUploader {
|
|
|
3852
4211
|
|
|
3853
4212
|
// src/adapters/oppo_uploader.ts
|
|
3854
4213
|
var import_fs_extra10 = __toESM(require("fs-extra"), 1);
|
|
3855
|
-
var
|
|
4214
|
+
var import_path9 = __toESM(require("path"), 1);
|
|
3856
4215
|
var import_axios7 = __toESM(require("axios"), 1);
|
|
3857
4216
|
var import_form_data6 = __toESM(require("form-data"), 1);
|
|
3858
|
-
var
|
|
4217
|
+
var import_crypto5 = __toESM(require("crypto"), 1);
|
|
3859
4218
|
var OppoUploader = class _OppoUploader {
|
|
3860
4219
|
name = "OPPO";
|
|
3861
4220
|
/** OPPO 开放 API 基础地址 */
|
|
@@ -3874,7 +4233,7 @@ var OppoUploader = class _OppoUploader {
|
|
|
3874
4233
|
*/
|
|
3875
4234
|
generateSign(params, clientSecret) {
|
|
3876
4235
|
const sorted = Object.keys(params).sort().filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
3877
|
-
return
|
|
4236
|
+
return import_crypto5.default.createHmac("sha256", clientSecret).update(sorted).digest("hex");
|
|
3878
4237
|
}
|
|
3879
4238
|
validateConfig(config) {
|
|
3880
4239
|
if (!config?.publish?.oppo?.enable) return false;
|
|
@@ -3893,7 +4252,7 @@ var OppoUploader = class _OppoUploader {
|
|
|
3893
4252
|
* 尝试从文件名推断包名
|
|
3894
4253
|
*/
|
|
3895
4254
|
inferPackageName(filePath) {
|
|
3896
|
-
const fileName =
|
|
4255
|
+
const fileName = import_path9.default.basename(filePath);
|
|
3897
4256
|
const match = fileName.match(/^([\w.]+?)(?:-(?:release|debug|unsigned))?\.apk$/i);
|
|
3898
4257
|
if (match && match[1].includes(".")) {
|
|
3899
4258
|
return match[1];
|
|
@@ -4174,17 +4533,17 @@ var OppoUploader = class _OppoUploader {
|
|
|
4174
4533
|
|
|
4175
4534
|
// src/adapters/vivo_uploader.ts
|
|
4176
4535
|
var import_fs_extra11 = __toESM(require("fs-extra"), 1);
|
|
4177
|
-
var
|
|
4536
|
+
var import_path10 = __toESM(require("path"), 1);
|
|
4178
4537
|
var import_axios8 = __toESM(require("axios"), 1);
|
|
4179
4538
|
var import_form_data7 = __toESM(require("form-data"), 1);
|
|
4180
|
-
var
|
|
4539
|
+
var import_crypto6 = __toESM(require("crypto"), 1);
|
|
4181
4540
|
var VivoUploader = class _VivoUploader {
|
|
4182
4541
|
name = "vivo";
|
|
4183
4542
|
/** vivo 开放 API 基础地址 */
|
|
4184
4543
|
static API_BASE = "https://developer-api.vivo.com.cn/router/rest";
|
|
4185
4544
|
generateSign(params, accessSecret) {
|
|
4186
4545
|
const sorted = Object.keys(params).sort().filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
4187
|
-
return
|
|
4546
|
+
return import_crypto6.default.createHmac("sha256", accessSecret).update(sorted).digest("hex");
|
|
4188
4547
|
}
|
|
4189
4548
|
validateConfig(config) {
|
|
4190
4549
|
if (!config?.publish?.vivo?.enable) return false;
|
|
@@ -4206,7 +4565,7 @@ var VivoUploader = class _VivoUploader {
|
|
|
4206
4565
|
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
4207
4566
|
if (!p || !import_fs_extra11.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
|
|
4208
4567
|
const md5 = await this.calculateFileMD5(p);
|
|
4209
|
-
logger.info(`[vivo] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${
|
|
4568
|
+
logger.info(`[vivo] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${import_path10.default.basename(p)}`);
|
|
4210
4569
|
const serialnumber = await this.uploadApk(accessKey, accessSecret, p, vivoConfig.packageName || "", md5);
|
|
4211
4570
|
const pkgInfo = await parsePackageInfo(p, { androidSdkPath: config.globalSettings?.androidSdkPath });
|
|
4212
4571
|
const nativeCodes = pkgInfo?.nativeCode || [];
|
|
@@ -4246,7 +4605,7 @@ var VivoUploader = class _VivoUploader {
|
|
|
4246
4605
|
}
|
|
4247
4606
|
calculateFileMD5(filePath) {
|
|
4248
4607
|
return new Promise((resolve, reject) => {
|
|
4249
|
-
const hash =
|
|
4608
|
+
const hash = import_crypto6.default.createHash("md5");
|
|
4250
4609
|
const stream = import_fs_extra11.default.createReadStream(filePath);
|
|
4251
4610
|
stream.on("data", (data) => hash.update(data));
|
|
4252
4611
|
stream.on("end", () => resolve(hash.digest("hex")));
|
|
@@ -4270,7 +4629,7 @@ var VivoUploader = class _VivoUploader {
|
|
|
4270
4629
|
const form = new import_form_data7.default();
|
|
4271
4630
|
Object.entries(params).forEach(([k, v]) => form.append(k, v));
|
|
4272
4631
|
form.append("sign", sign);
|
|
4273
|
-
form.append("file", import_fs_extra11.default.createReadStream(filePath), { filename:
|
|
4632
|
+
form.append("file", import_fs_extra11.default.createReadStream(filePath), { filename: import_path10.default.basename(filePath) });
|
|
4274
4633
|
const res = await import_axios8.default.post(_VivoUploader.API_BASE, form, { headers: form.getHeaders(), timeout: 3e5 });
|
|
4275
4634
|
if (res.data.code !== 0) throw new Error(`\u4E0A\u4F20\u5305\u5931\u8D25: ${res.data.msg}`);
|
|
4276
4635
|
return res.data.data?.serialnumber || res.data.data;
|
|
@@ -4310,9 +4669,9 @@ var VivoUploader = class _VivoUploader {
|
|
|
4310
4669
|
|
|
4311
4670
|
// src/adapters/tencent_uploader.ts
|
|
4312
4671
|
var import_fs_extra12 = __toESM(require("fs-extra"), 1);
|
|
4313
|
-
var
|
|
4672
|
+
var import_path11 = __toESM(require("path"), 1);
|
|
4314
4673
|
var import_axios9 = __toESM(require("axios"), 1);
|
|
4315
|
-
var
|
|
4674
|
+
var import_crypto7 = __toESM(require("crypto"), 1);
|
|
4316
4675
|
var TencentUploader = class _TencentUploader {
|
|
4317
4676
|
name = "Tencent";
|
|
4318
4677
|
/** 腾讯开放平台 API 基础地址 */
|
|
@@ -4325,7 +4684,7 @@ var TencentUploader = class _TencentUploader {
|
|
|
4325
4684
|
generateSign(params, accessSecret) {
|
|
4326
4685
|
const sortedKeys = Object.keys(params).sort();
|
|
4327
4686
|
const signStr = sortedKeys.filter((key) => params[key] !== void 0 && params[key] !== null).map((key) => `${key}=${params[key]}`).join("&");
|
|
4328
|
-
return
|
|
4687
|
+
return import_crypto7.default.createHmac("sha256", accessSecret).update(signStr).digest("hex").toLowerCase();
|
|
4329
4688
|
}
|
|
4330
4689
|
validateConfig(config) {
|
|
4331
4690
|
if (!config?.publish?.tencent?.enable) return false;
|
|
@@ -4350,7 +4709,7 @@ var TencentUploader = class _TencentUploader {
|
|
|
4350
4709
|
const uploadPromises = Object.entries(artifacts).map(async ([abi, p]) => {
|
|
4351
4710
|
if (!p || !import_fs_extra12.default.existsSync(p)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${p}`);
|
|
4352
4711
|
const stats = import_fs_extra12.default.statSync(p);
|
|
4353
|
-
const fileName =
|
|
4712
|
+
const fileName = import_path11.default.basename(p);
|
|
4354
4713
|
logger.info(`[Tencent] \u6B63\u5728\u5904\u7406 ${abi} \u67B6\u6784\u5305: ${fileName}`);
|
|
4355
4714
|
const uploadInfo = await this.getFileUploadInfo(auth, stats.size, fileName, pkgName);
|
|
4356
4715
|
await this.uploadToCos(uploadInfo.pre_sign_url, p, stats.size);
|
|
@@ -4553,27 +4912,256 @@ var TencentUploader = class _TencentUploader {
|
|
|
4553
4912
|
return res.data;
|
|
4554
4913
|
}
|
|
4555
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
|
+
};
|
|
4556
5134
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4557
5135
|
0 && (module.exports = {
|
|
4558
5136
|
AdHocUploader,
|
|
4559
5137
|
AppleJWTGenerator,
|
|
4560
5138
|
AppleStoreUploader,
|
|
4561
5139
|
ConfigValidator,
|
|
5140
|
+
DEFAULT_RELEASE_RECEIPT_LIMIT,
|
|
4562
5141
|
DEFAULT_RETRY_CONFIG,
|
|
4563
5142
|
EnvValidator,
|
|
4564
5143
|
ErrorCategory,
|
|
5144
|
+
GooglePlayUploader,
|
|
4565
5145
|
HarmonyUploader,
|
|
4566
5146
|
HuaweiUploader,
|
|
4567
5147
|
OppoUploader,
|
|
4568
5148
|
PgyerUploader,
|
|
5149
|
+
RELEASE_RECEIPT_SCHEMA_VERSION,
|
|
5150
|
+
ReleaseReceiptStore,
|
|
4569
5151
|
RetryStrategy,
|
|
4570
5152
|
TencentUploader,
|
|
4571
5153
|
TestFlightUploader,
|
|
4572
5154
|
UploadManager,
|
|
4573
5155
|
VivoUploader,
|
|
4574
5156
|
XiaomiUploader,
|
|
5157
|
+
createProjectKey,
|
|
5158
|
+
createReleaseReceipt,
|
|
4575
5159
|
createUploadManager,
|
|
4576
5160
|
createUploadRetryStrategy,
|
|
4577
|
-
|
|
5161
|
+
createUploadTargets,
|
|
5162
|
+
formatReleaseReceipt,
|
|
5163
|
+
parsePackageInfo,
|
|
5164
|
+
resolveReleaseProjectRoot,
|
|
5165
|
+
sanitizeReleaseMessage
|
|
4578
5166
|
});
|
|
4579
5167
|
//# sourceMappingURL=index.cjs.map
|