shipup 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.
Files changed (83) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +14 -0
  3. package/README.md +112 -0
  4. package/README.zh-CN.md +105 -0
  5. package/SECURITY.md +32 -0
  6. package/creds.example.yaml +62 -0
  7. package/dist/adapters/android/honor/api.js +360 -0
  8. package/dist/adapters/android/honor/api.js.map +1 -0
  9. package/dist/adapters/android/honor/index.js +122 -0
  10. package/dist/adapters/android/honor/index.js.map +1 -0
  11. package/dist/adapters/android/huawei/api.js +389 -0
  12. package/dist/adapters/android/huawei/api.js.map +1 -0
  13. package/dist/adapters/android/huawei/index.js +128 -0
  14. package/dist/adapters/android/huawei/index.js.map +1 -0
  15. package/dist/adapters/android/meizu/api.js +367 -0
  16. package/dist/adapters/android/meizu/api.js.map +1 -0
  17. package/dist/adapters/android/meizu/index.js +129 -0
  18. package/dist/adapters/android/meizu/index.js.map +1 -0
  19. package/dist/adapters/android/oppo/api.js +397 -0
  20. package/dist/adapters/android/oppo/api.js.map +1 -0
  21. package/dist/adapters/android/oppo/index.js +135 -0
  22. package/dist/adapters/android/oppo/index.js.map +1 -0
  23. package/dist/adapters/android/qq/api.js +184 -0
  24. package/dist/adapters/android/qq/api.js.map +1 -0
  25. package/dist/adapters/android/qq/index.js +80 -0
  26. package/dist/adapters/android/qq/index.js.map +1 -0
  27. package/dist/adapters/android/samsung/api.js +355 -0
  28. package/dist/adapters/android/samsung/api.js.map +1 -0
  29. package/dist/adapters/android/samsung/index.js +111 -0
  30. package/dist/adapters/android/samsung/index.js.map +1 -0
  31. package/dist/adapters/android/specs.js +106 -0
  32. package/dist/adapters/android/specs.js.map +1 -0
  33. package/dist/adapters/android/stub.js +27 -0
  34. package/dist/adapters/android/stub.js.map +1 -0
  35. package/dist/adapters/android/vivo/api.js +236 -0
  36. package/dist/adapters/android/vivo/api.js.map +1 -0
  37. package/dist/adapters/android/vivo/index.js +121 -0
  38. package/dist/adapters/android/vivo/index.js.map +1 -0
  39. package/dist/adapters/android/xiaomi/api.js +163 -0
  40. package/dist/adapters/android/xiaomi/api.js.map +1 -0
  41. package/dist/adapters/android/xiaomi/index.js +108 -0
  42. package/dist/adapters/android/xiaomi/index.js.map +1 -0
  43. package/dist/adapters/ios/appstore/asc.js +280 -0
  44. package/dist/adapters/ios/appstore/asc.js.map +1 -0
  45. package/dist/adapters/ios/appstore/index.js +323 -0
  46. package/dist/adapters/ios/appstore/index.js.map +1 -0
  47. package/dist/adapters/registry.js +34 -0
  48. package/dist/adapters/registry.js.map +1 -0
  49. package/dist/adapters/util.js +26 -0
  50. package/dist/adapters/util.js.map +1 -0
  51. package/dist/cli/index.js +479 -0
  52. package/dist/cli/index.js.map +1 -0
  53. package/dist/cli/output.js +65 -0
  54. package/dist/cli/output.js.map +1 -0
  55. package/dist/core/exit.js +20 -0
  56. package/dist/core/exit.js.map +1 -0
  57. package/dist/core/orchestrator.js +24 -0
  58. package/dist/core/orchestrator.js.map +1 -0
  59. package/dist/core/types.js +20 -0
  60. package/dist/core/types.js.map +1 -0
  61. package/dist/creds/load.js +68 -0
  62. package/dist/creds/load.js.map +1 -0
  63. package/dist/creds/schema.js +30 -0
  64. package/dist/creds/schema.js.map +1 -0
  65. package/dist/infra/crypto.js +12 -0
  66. package/dist/infra/crypto.js.map +1 -0
  67. package/dist/infra/http.js +87 -0
  68. package/dist/infra/http.js.map +1 -0
  69. package/dist/pkginfo/apk-icon.js +89 -0
  70. package/dist/pkginfo/apk-icon.js.map +1 -0
  71. package/dist/pkginfo/icon.js +83 -0
  72. package/dist/pkginfo/icon.js.map +1 -0
  73. package/dist/pkginfo/index.js +33 -0
  74. package/dist/pkginfo/index.js.map +1 -0
  75. package/docs/android-markets.md +190 -0
  76. package/docs/credentials.md +72 -0
  77. package/docs/providers.md +60 -0
  78. package/lib/common.mjs +265 -0
  79. package/lib/harmony.mjs +190 -0
  80. package/lib/huawei.mjs +153 -0
  81. package/lib/ios.mjs +477 -0
  82. package/package.json +70 -0
  83. package/shipup.mjs +160 -0
@@ -0,0 +1,163 @@
1
+ // 小米按 packageName 识别应用,无 app_id。鉴权方式:把 {password, sig:[...]} 这段 JSON
2
+ // 用开发者 RSA 公钥(PKCS#1 v1.5)整体加密、hex 编码后放入 multipart 字段 SIG;
3
+ // 服务端解密后校验 password 及各文件/字段的 md5(sig 数组里的 hash)。
4
+ // 流程:dev/query 取线上 appName 等信息 → dev/push(multipart:RequestData + SIG + apk + icon)即提交。
5
+ import { readFile } from 'node:fs/promises';
6
+ import { basename } from 'node:path';
7
+ import { createPublicKey, publicEncrypt, constants } from 'node:crypto';
8
+ import { md5Hex } from '../../../infra/crypto.js';
9
+ import { UploadError } from '../../../core/types.js';
10
+ const BASE = 'https://api.developer.xiaomi.com/devupload/dev';
11
+ // 其 base64url(JWK 的 e 字段)即 "AQAB"。
12
+ const RSA_EXPONENT_B64URL = Buffer.from([0x01, 0x00, 0x01]).toString('base64url');
13
+ // PKCS#1 v1.5 type2 加密填充开销固定 11 字节;故单块明文上限 = 模数字节数 - 11。
14
+ const PKCS1_PADDING_OVERHEAD = 11;
15
+ /** 把十六进制模数字符串规整为偶数长度的小写 hex。 */
16
+ function normalizeHex(hex) {
17
+ let h = hex.trim().toLowerCase().replace(/^0x/, '');
18
+ if (h.length % 2 !== 0)
19
+ h = `0${h}`;
20
+ return h;
21
+ }
22
+ /** 模数 hex → 大端无符号字节(去掉前导 0 字节,作为 JWK n / 计算密钥字节长度)。 */
23
+ function modulusBuffer(rsaModulusHex) {
24
+ const buf = Buffer.from(normalizeHex(rsaModulusHex), 'hex');
25
+ let i = 0;
26
+ while (i < buf.length - 1 && buf[i] === 0)
27
+ i++;
28
+ return i > 0 ? buf.subarray(i) : buf;
29
+ }
30
+ /** 用模数 hex + 指数 65537 构造 RSA 公钥(经 JWK 导入)。 */
31
+ function buildPublicKey(rsaModulusHex) {
32
+ const modBuf = modulusBuffer(rsaModulusHex);
33
+ if (modBuf.length === 0)
34
+ throw new UploadError('rsa', 'rsa_modulus 为空或非法');
35
+ const key = createPublicKey({
36
+ key: { kty: 'RSA', n: modBuf.toString('base64url'), e: RSA_EXPONENT_B64URL },
37
+ format: 'jwk',
38
+ });
39
+ return { key, keyBytes: modBuf.length };
40
+ }
41
+ /**
42
+ * 各密文块拼接后 hex 编码返回。
43
+ * 注意:PKCS#1 v1.5 type2 填充是随机的(Node 与 PointyCastle 皆然),密文每次不同但
44
+ * 语义等价——服务端解密得到同一明文,这是预期行为,不是 bug。
45
+ */
46
+ export function rsaSignature(body, rsaModulusHex) {
47
+ const { key, keyBytes } = buildPublicKey(rsaModulusHex);
48
+ const inputBlockSize = keyBytes - PKCS1_PADDING_OVERHEAD;
49
+ if (inputBlockSize <= 0)
50
+ throw new UploadError('rsa', 'rsa_modulus 过短,无法 PKCS#1 加密');
51
+ const input = Buffer.from(body, 'utf8');
52
+ const blocks = [];
53
+ for (let off = 0; off < input.length; off += inputBlockSize) {
54
+ const chunk = input.subarray(off, Math.min(off + inputBlockSize, input.length));
55
+ const enc = publicEncrypt({ key, padding: constants.RSA_PKCS1_PADDING }, chunk);
56
+ blocks.push(enc);
57
+ }
58
+ return Buffer.concat(blocks).toString('hex');
59
+ }
60
+ /** 对 UTF-8 字符串计算小写 MD5 hex。 */
61
+ function md5OfString(s) {
62
+ return md5Hex(Buffer.from(s, 'utf8'));
63
+ }
64
+ /** 构造并 RSA 加密 SIG 字段:{password, sig:[{name,hash}...]}。 */
65
+ function buildSig(password, sig, rsaModulusHex) {
66
+ const sigBody = JSON.stringify({ password, sig });
67
+ return rsaSignature(sigBody, rsaModulusHex);
68
+ }
69
+ /** POST multipart,校验 http 状态与业务 result===0,返回解析后的 JSON。 */
70
+ async function postMultipart(url, form, timeoutMs) {
71
+ const res = await fetch(url, {
72
+ method: 'POST',
73
+ body: form,
74
+ signal: AbortSignal.timeout(timeoutMs),
75
+ });
76
+ const text = await res.text();
77
+ if (!res.ok) {
78
+ throw new UploadError(res.status, `http ${res.status}: ${text.slice(0, 500)}`);
79
+ }
80
+ let data;
81
+ try {
82
+ data = JSON.parse(text);
83
+ }
84
+ catch {
85
+ throw new UploadError('parse', `非 JSON 响应: ${text.slice(0, 500)}`);
86
+ }
87
+ if (Number(data?.result) !== 0) {
88
+ throw new UploadError(data?.result ?? -1, String(data?.message ?? 'unknown'));
89
+ }
90
+ return data;
91
+ }
92
+ /** dev/query:查询应用线上信息(appName、线上版本等)。 */
93
+ export async function getAppInfo(args) {
94
+ // 注意:requestData 必须「序列化一次、字段与哈希共用同一字符串」,
95
+ // 否则 SIG 内的 md5 与实际 RequestData 字段不一致,服务端会校验失败。
96
+ const requestData = JSON.stringify({
97
+ userName: args.userName,
98
+ packageName: args.packageName,
99
+ });
100
+ const sig = buildSig(args.password, [{ name: 'RequestData', hash: md5OfString(requestData) }], args.rsaModulus);
101
+ const form = new FormData();
102
+ form.append('RequestData', requestData);
103
+ form.append('SIG', sig);
104
+ const data = await postMultipart(`${BASE}/query`, form, args.timeoutMs);
105
+ return data;
106
+ }
107
+ /**
108
+ * dev/push:上传 APK + 图标 + 版本信息并提交(小米 push 即提交,无独立提审步骤)。
109
+ */
110
+ export async function uploadXiaomi(args) {
111
+ const appInfo = await getAppInfo({
112
+ userName: args.userName,
113
+ password: args.password,
114
+ rsaModulus: args.rsaModulus,
115
+ packageName: args.packageName,
116
+ timeoutMs: args.queryTimeoutMs,
117
+ });
118
+ // appName 必传:优先用调用方覆盖值,否则沿用线上值(保持现有行为)。
119
+ const onlineAppName = appInfo.packageInfo.appName;
120
+ // appInfo:brief/desc 按需生效——仅当有值才加入 RequestData,否则保持原字段集不变。
121
+ const appInfoBody = {
122
+ appName: args.appName ?? onlineAppName,
123
+ packageName: args.packageName,
124
+ updateDesc: args.updateDesc,
125
+ versionName: args.versionName,
126
+ privacyUrl: args.privacyUrl,
127
+ };
128
+ if (args.brief !== undefined)
129
+ appInfoBody.brief = args.brief;
130
+ if (args.desc !== undefined)
131
+ appInfoBody.desc = args.desc;
132
+ const body = {
133
+ userName: args.userName,
134
+ synchroType: 1,
135
+ appInfo: appInfoBody,
136
+ };
137
+ const requestData = JSON.stringify(body);
138
+ const apkBytes = await readFile(args.apkPath);
139
+ const apkMd5 = md5Hex(apkBytes);
140
+ const iconMd5 = md5Hex(args.iconBytes);
141
+ // SIG 的 md5 列表与 multipart 文件部分须严格一致:RequestData → apk → icon → screenshot_1..N。
142
+ const sigEntries = [
143
+ { name: 'RequestData', hash: md5OfString(requestData) },
144
+ { name: 'apk', hash: apkMd5 },
145
+ { name: 'icon', hash: iconMd5 },
146
+ ];
147
+ const screenshots = args.screenshots ?? [];
148
+ for (let i = 0; i < screenshots.length; i++) {
149
+ sigEntries.push({ name: `screenshot_${i + 1}`, hash: md5Hex(screenshots[i]) });
150
+ }
151
+ const sig = buildSig(args.password, sigEntries, args.rsaModulus);
152
+ const form = new FormData();
153
+ form.append('RequestData', requestData);
154
+ form.append('SIG', sig);
155
+ form.append('apk', new Blob([apkBytes], { type: 'application/octet-stream' }), basename(args.apkPath));
156
+ form.append('icon', new Blob([args.iconBytes], { type: 'image/png' }), args.iconFileName);
157
+ for (let i = 0; i < screenshots.length; i++) {
158
+ const field = `screenshot_${i + 1}`;
159
+ form.append(field, new Blob([screenshots[i]], { type: 'image/png' }), `${field}.png`);
160
+ }
161
+ await postMultipart(`${BASE}/push`, form, args.uploadTimeoutMs);
162
+ }
163
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../src/adapters/android/xiaomi/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { toFailed } from '../../util.js';
2
+ import { prepareScreenshot } from '../../../pkginfo/icon.js';
3
+ import { resolveIconBytes } from '../../../pkginfo/apk-icon.js';
4
+ import { getChannelSpec, warnIfTooLong } from '../specs.js';
5
+ import { getAppInfo, uploadXiaomi } from './api.js';
6
+ /**
7
+ * 小米 getAppInfo 返回的是【线上已审核通过】的版本,没有独立的「提交版本审核态」接口。
8
+ * - 目标版本(versionCode 优先,其次 versionName)已是线上版本 → approved;
9
+ * - 指定了目标版本但与线上不一致 → 仍在审核/排队 → pending_review;
10
+ * - 未指定目标版本 → 仅说明存在线上版本 → approved。
11
+ */
12
+ function mapXiaomiStatus(ctx, onlineVersionName, onlineVersionCode) {
13
+ if (ctx.versionCode && onlineVersionCode === ctx.versionCode)
14
+ return 'approved';
15
+ if (ctx.versionName && onlineVersionName === ctx.versionName)
16
+ return 'approved';
17
+ if (ctx.versionCode || ctx.versionName)
18
+ return 'pending_review';
19
+ return 'approved';
20
+ }
21
+ /** 小米应用市场:dev/push 即提交(上传 + 提审一步完成),故 upload 始终为 upload+submit。 */
22
+ export class XiaomiAdapter {
23
+ channel = 'xiaomi';
24
+ async upload(ctx) {
25
+ const start = Date.now();
26
+ try {
27
+ const spec = getChannelSpec('xiaomi');
28
+ // 小米 push 必传图标:优先 --icon,否则用 aapt 从 APK 自动提取(按 spec 缩放 512²、压到 ≤1MB)。
29
+ const iconBytes = await resolveIconBytes({
30
+ iconPath: ctx.iconPath,
31
+ apkPath: ctx.pkg,
32
+ size: spec.iconSize,
33
+ maxBytes: spec.iconMaxBytes,
34
+ });
35
+ // 文案按需更新:写前对一句话简介(brief)做长度告警(小米 ≤17 汉字,仅告警不阻断)。
36
+ warnIfTooLong('xiaomi', 'summary', ctx.summary, spec.summaryMax);
37
+ // 截图按需更新:仅当 ctx.screenshots 非空时,按 spec 处理为 png 并随表单上传;空=沿用线上。
38
+ let screenshots;
39
+ if (ctx.screenshots && ctx.screenshots.length > 0) {
40
+ screenshots = [];
41
+ for (const p of ctx.screenshots) {
42
+ screenshots.push(await prepareScreenshot(p, spec.screenshotFormat, spec.screenshotMaxBytes));
43
+ }
44
+ }
45
+ await uploadXiaomi({
46
+ userName: ctx.creds.user_name,
47
+ password: ctx.creds.password,
48
+ rsaModulus: ctx.creds.rsa_modulus,
49
+ packageName: ctx.packageName,
50
+ versionName: ctx.versionName,
51
+ updateDesc: ctx.releaseNote ?? '',
52
+ privacyUrl: ctx.privacyUrl ?? '',
53
+ appName: ctx.appName,
54
+ brief: ctx.summary,
55
+ desc: ctx.description,
56
+ apkPath: ctx.pkg,
57
+ iconBytes,
58
+ iconFileName: 'icon.png',
59
+ screenshots,
60
+ queryTimeoutMs: ctx.timeoutMs,
61
+ uploadTimeoutMs: Math.max(ctx.timeoutMs, 900000),
62
+ });
63
+ return {
64
+ channel: this.channel,
65
+ packageName: ctx.packageName,
66
+ versionName: ctx.versionName,
67
+ versionCode: ctx.versionCode,
68
+ action: 'upload+submit',
69
+ status: 'submitted',
70
+ errorCode: null,
71
+ message: '',
72
+ durationMs: Date.now() - start,
73
+ };
74
+ }
75
+ catch (e) {
76
+ return toFailed(this.channel, e, start, 'upload+submit', ctx);
77
+ }
78
+ }
79
+ async queryStatus(ctx) {
80
+ const start = Date.now();
81
+ try {
82
+ const appInfo = await getAppInfo({
83
+ userName: ctx.creds.user_name,
84
+ password: ctx.creds.password,
85
+ rsaModulus: ctx.creds.rsa_modulus,
86
+ packageName: ctx.packageName ?? '',
87
+ timeoutMs: ctx.timeoutMs,
88
+ });
89
+ const onlineVersionName = appInfo.packageInfo.versionName;
90
+ const onlineVersionCode = String(appInfo.packageInfo.versionCode);
91
+ return {
92
+ channel: this.channel,
93
+ packageName: ctx.packageName,
94
+ versionName: ctx.versionName,
95
+ versionCode: ctx.versionCode,
96
+ action: 'status',
97
+ status: mapXiaomiStatus(ctx, onlineVersionName, onlineVersionCode),
98
+ marketVersion: onlineVersionName,
99
+ errorCode: null,
100
+ durationMs: Date.now() - start,
101
+ };
102
+ }
103
+ catch (e) {
104
+ return toFailed(this.channel, e, start, 'status');
105
+ }
106
+ }
107
+ }
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/adapters/android/xiaomi/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,280 @@
1
+ // App Store Connect API v1 客户端。
2
+ // 鉴权:JWT ES256(node:crypto,EC P-256)。封装 ascFetch 自动带 Authorization: Bearer。
3
+ import { createPrivateKey, sign } from 'node:crypto';
4
+ import { setTimeout as sleep } from 'node:timers/promises';
5
+ import { UploadError } from '../../../core/types.js';
6
+ import { withRetry } from '../../../infra/http.js';
7
+ const ASC_BASE = 'https://api.appstoreconnect.apple.com';
8
+ function base64url(input) {
9
+ return Buffer.from(input).toString('base64url');
10
+ }
11
+ /**
12
+ * 生成 ASC JWT(ES256)。
13
+ * header {alg:'ES256', kid:key_id, typ:'JWT'}
14
+ * payload {iss:issuer_id, iat:now, exp:now+1200, aud:'appstoreconnect-v1'}
15
+ * 签名用 dsaEncoding:'ieee-p1363' 直接得到 JOSE(R||S) 格式(无需 DER→JOSE 手工转换)。
16
+ */
17
+ export function makeAscToken(creds) {
18
+ const now = Math.floor(Date.now() / 1000);
19
+ const header = { alg: 'ES256', kid: creds.keyId, typ: 'JWT' };
20
+ const payload = { iss: creds.issuerId, iat: now, exp: now + 1200, aud: 'appstoreconnect-v1' };
21
+ const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(payload))}`;
22
+ const key = createPrivateKey(creds.privateKey);
23
+ const signature = sign('sha256', Buffer.from(signingInput), { key, dsaEncoding: 'ieee-p1363' });
24
+ return `${signingInput}.${base64url(signature)}`;
25
+ }
26
+ const enc = encodeURIComponent;
27
+ export class AscClient {
28
+ creds;
29
+ requestTimeoutMs;
30
+ token;
31
+ tokenExp = 0;
32
+ constructor(creds, requestTimeoutMs = 60_000) {
33
+ this.creds = creds;
34
+ this.requestTimeoutMs = requestTimeoutMs;
35
+ }
36
+ authToken() {
37
+ const now = Math.floor(Date.now() / 1000);
38
+ if (!this.token || now >= this.tokenExp - 60) {
39
+ this.token = makeAscToken(this.creds);
40
+ this.tokenExp = now + 1200;
41
+ }
42
+ return this.token;
43
+ }
44
+ /**
45
+ * 通用请求:method/path(+body),自动带鉴权;非 2xx 抛 UploadError(携带 ASC 错误码,合并全部 errors)。
46
+ * 仅对 **GET** 在 5xx/429/网络/超时 时重试(幂等);POST/PATCH 不自动重试,避免重复创建/重复提交。
47
+ */
48
+ async ascFetch(method, path, body) {
49
+ const url = path.startsWith('http') ? path : `${ASC_BASE}${path}`;
50
+ return withRetry(async () => {
51
+ const res = await fetch(url, {
52
+ method,
53
+ headers: {
54
+ Authorization: `Bearer ${this.authToken()}`,
55
+ ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
56
+ },
57
+ body: body !== undefined ? JSON.stringify(body) : undefined,
58
+ signal: AbortSignal.timeout(this.requestTimeoutMs),
59
+ });
60
+ const text = await res.text();
61
+ if (!res.ok) {
62
+ let code = res.status;
63
+ let msg = text.slice(0, 800);
64
+ try {
65
+ const errs = JSON.parse(text)?.errors;
66
+ if (Array.isArray(errs) && errs.length > 0) {
67
+ code = errs[0].code ?? res.status;
68
+ // 提审失败常一次返回多条(缺截图/缺合规/缺 whatsNew),全部拼出便于诊断。
69
+ msg =
70
+ errs
71
+ .map((e) => [e.title, e.detail].filter(Boolean).join(' — '))
72
+ .filter(Boolean)
73
+ .join(' | ') || msg;
74
+ }
75
+ }
76
+ catch {
77
+ /* 保留原始文本 */
78
+ }
79
+ throw new UploadError(code, `ASC ${method} ${path} -> ${res.status}: ${msg}`);
80
+ }
81
+ if (!text)
82
+ return null; // 204 No Content(如 PATCH relationships)
83
+ try {
84
+ return JSON.parse(text);
85
+ }
86
+ catch {
87
+ throw new UploadError('parse', `ASC 非 JSON 响应: ${text.slice(0, 300)}`);
88
+ }
89
+ }, { label: `ASC ${method} ${path}`, retries: method === 'GET' ? 3 : 0 });
90
+ }
91
+ // ── builds ──
92
+ /**
93
+ * 轮询 build 直到 processingState=VALID,返回 build id。
94
+ * GET /v1/builds?filter[app]=<appId>&filter[version]=<buildVersion>
95
+ *
96
+ * 两点健壮性(避免「上游上传失败 → 静默轮询到超时」的坑):
97
+ * - build 完全查不到(空结果)通常意味着上游 altool 上传失败/未完成:只容忍 notFoundGraceMs,
98
+ * 之后 **快速失败** 并给出可诊断信息,而不是一路静默轮询到 deadline(默认 30min)。
99
+ * 一旦见过该 build(哪怕仍在 PROCESSING),就转为正常等待到 deadline。
100
+ * - 每次轮询打一行 stderr 进度(VALID 命中直接返回、不打日志,健康路径保持安静)。
101
+ */
102
+ async waitBuildValid(appId, buildVersion, preReleaseVersion, deadlineMs, intervalMs = 15_000, notFoundGraceMs = 300_000) {
103
+ // 用 CFBundleVersion + 营销版本(版本列车)双过滤,避免跨版本列车复用同一 build 号时选错;
104
+ // sort=-uploadedDate 取最新上传,避免 data[0] 顺序不确定。
105
+ const train = preReleaseVersion
106
+ ? `&filter[preReleaseVersion.version]=${enc(preReleaseVersion)}`
107
+ : '';
108
+ const query = `/v1/builds?filter[app]=${enc(appId)}&filter[version]=${enc(buildVersion)}${train}&sort=-uploadedDate&limit=1`;
109
+ const label = `${buildVersion}(${preReleaseVersion ?? '-'})`;
110
+ const notFoundDeadline = Math.min(Date.now() + notFoundGraceMs, deadlineMs);
111
+ let seen = false;
112
+ for (let attempt = 1;; attempt++) {
113
+ const data = await this.ascFetch('GET', query);
114
+ const build = data?.data?.[0];
115
+ const state = build?.attributes?.processingState;
116
+ if (state === 'VALID')
117
+ return build.id;
118
+ if (state === 'FAILED' || state === 'INVALID') {
119
+ throw new UploadError(state, `build ${label} 处理失败: ${state}`);
120
+ }
121
+ if (build)
122
+ seen = true;
123
+ const now = Date.now();
124
+ console.error(`[asc] 等待 build ${label} 就绪… 第 ${attempt} 次,state=${state ?? '未出现(ASC 上暂无此 build)'}`);
125
+ // 从未出现且已过宽限期:几乎必然是上游没传上来,快速失败(而非空转到 deadline)。
126
+ if (!seen && now > notFoundDeadline) {
127
+ throw new UploadError('build_not_found', `ASC 上找不到 build ${label}(${Math.round(notFoundGraceMs / 1000)}s 内始终未出现)。` +
128
+ '通常是上游 altool 上传失败/未完成——请检查 asc-app 部署日志确认 build 已成功上传到 App Store Connect,再重试提审。');
129
+ }
130
+ if (now > deadlineMs) {
131
+ throw new UploadError('timeout', `等待 build ${label} 进入 VALID 超时(最后 state=${state ?? '未出现'})`);
132
+ }
133
+ await sleep(intervalMs);
134
+ }
135
+ }
136
+ // ── appStoreVersions ──
137
+ /** GET /v1/apps/<appId>/appStoreVersions?filter[versionString]&filter[platform]=IOS,取第一条或 null。 */
138
+ async getAppStoreVersion(appId, versionString) {
139
+ const data = await this.ascFetch('GET', `/v1/apps/${enc(appId)}/appStoreVersions?filter[versionString]=${enc(versionString)}&filter[platform]=IOS&limit=1`);
140
+ return data?.data?.[0] ?? null;
141
+ }
142
+ /**
143
+ * 用 bundleId 反查 app 的数字 Apple ID。
144
+ * altool 传包靠 ipa 里的 bundleId 定位 app(不需数字 id),但 REST API 要数字 id 引用资源。
145
+ */
146
+ async getAppIdByBundleId(bundleId) {
147
+ const data = await this.ascFetch('GET', `/v1/apps?filter[bundleId]=${enc(bundleId)}&limit=1`);
148
+ const id = data?.data?.[0]?.id;
149
+ if (!id) {
150
+ throw new UploadError('not_found', `ASC 未找到 bundleId=${bundleId} 对应的 app(确认 bundle id 正确、且该 API key 有此 app 权限)`);
151
+ }
152
+ return String(id);
153
+ }
154
+ /** app_id 优先;否则用 bundleId 反查。两者都没有则报错。 */
155
+ async resolveAppId(appId, bundleId) {
156
+ if (appId)
157
+ return appId;
158
+ if (bundleId)
159
+ return this.getAppIdByBundleId(bundleId);
160
+ throw new UploadError('input', '需要 --app-id 或 --bundle-id 之一');
161
+ }
162
+ /** 取已有 appStoreVersion,否则创建(POST /v1/appStoreVersions)。返回 version id。 */
163
+ async ensureAppStoreVersion(appId, versionString, releaseType) {
164
+ const existing = await this.getAppStoreVersion(appId, versionString);
165
+ if (existing) {
166
+ // 已存在版本:若 releaseType 与现值不同则纠正(仅在可编辑态有效,否则 ASC 会拒)。
167
+ if (releaseType && existing.attributes?.releaseType !== releaseType) {
168
+ await this.ascFetch('PATCH', `/v1/appStoreVersions/${enc(existing.id)}`, {
169
+ data: { type: 'appStoreVersions', id: existing.id, attributes: { releaseType } },
170
+ });
171
+ }
172
+ return existing.id;
173
+ }
174
+ const attributes = { platform: 'IOS', versionString };
175
+ if (releaseType)
176
+ attributes.releaseType = releaseType;
177
+ const res = await this.ascFetch('POST', '/v1/appStoreVersions', {
178
+ data: {
179
+ type: 'appStoreVersions',
180
+ attributes,
181
+ relationships: { app: { data: { type: 'apps', id: appId } } },
182
+ },
183
+ });
184
+ return res.data.id;
185
+ }
186
+ // ── localizations ──
187
+ /** 写多地区文案:已存在的 locale PATCH,否则 POST(仅写非空字段)。 */
188
+ async writeLocalizations(versionId, byLocale) {
189
+ const locales = Object.keys(byLocale);
190
+ if (locales.length === 0)
191
+ return;
192
+ const list = await this.ascFetch('GET', `/v1/appStoreVersions/${enc(versionId)}/appStoreVersionLocalizations?limit=50`);
193
+ const existing = list?.data ?? [];
194
+ for (const locale of locales) {
195
+ const attrs = {};
196
+ for (const [k, v] of Object.entries(byLocale[locale])) {
197
+ if (v != null && v !== '')
198
+ attrs[k] = v;
199
+ }
200
+ if (Object.keys(attrs).length === 0)
201
+ continue;
202
+ const found = existing.find((l) => l.attributes?.locale === locale);
203
+ if (found) {
204
+ await this.ascFetch('PATCH', `/v1/appStoreVersionLocalizations/${enc(found.id)}`, {
205
+ data: { type: 'appStoreVersionLocalizations', id: found.id, attributes: attrs },
206
+ });
207
+ }
208
+ else {
209
+ await this.ascFetch('POST', '/v1/appStoreVersionLocalizations', {
210
+ data: {
211
+ type: 'appStoreVersionLocalizations',
212
+ attributes: { locale, ...attrs },
213
+ relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
214
+ },
215
+ });
216
+ }
217
+ }
218
+ }
219
+ /** 关联 build:PATCH /v1/appStoreVersions/<id>/relationships/build。 */
220
+ async attachBuild(versionId, buildId) {
221
+ await this.ascFetch('PATCH', `/v1/appStoreVersions/${enc(versionId)}/relationships/build`, {
222
+ data: { type: 'builds', id: buildId },
223
+ });
224
+ }
225
+ // ── reviewSubmissions(三步)──
226
+ async submitForReview(appId, versionId) {
227
+ const sub = await this.ascFetch('POST', '/v1/reviewSubmissions', {
228
+ data: {
229
+ type: 'reviewSubmissions',
230
+ attributes: { platform: 'IOS' },
231
+ relationships: { app: { data: { type: 'apps', id: appId } } },
232
+ },
233
+ });
234
+ const subId = sub.data.id;
235
+ await this.ascFetch('POST', '/v1/reviewSubmissionItems', {
236
+ data: {
237
+ type: 'reviewSubmissionItems',
238
+ relationships: {
239
+ reviewSubmission: { data: { type: 'reviewSubmissions', id: subId } },
240
+ appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } },
241
+ },
242
+ },
243
+ });
244
+ await this.ascFetch('PATCH', `/v1/reviewSubmissions/${enc(subId)}`, {
245
+ data: { type: 'reviewSubmissions', id: subId, attributes: { submitted: true } },
246
+ });
247
+ }
248
+ // ── phased release / release request ──
249
+ /** GET /v1/appStoreVersions/<id>/appStoreVersionPhasedRelease(to-one),无则 null。 */
250
+ async getPhasedRelease(versionId) {
251
+ const data = await this.ascFetch('GET', `/v1/appStoreVersions/${enc(versionId)}/appStoreVersionPhasedRelease`);
252
+ return data?.data ?? null;
253
+ }
254
+ /** 开启 7 天灰度:POST /v1/appStoreVersionPhasedReleases。 */
255
+ async startPhasedRelease(versionId) {
256
+ const res = await this.ascFetch('POST', '/v1/appStoreVersionPhasedReleases', {
257
+ data: {
258
+ type: 'appStoreVersionPhasedReleases',
259
+ relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
260
+ },
261
+ });
262
+ return res.data;
263
+ }
264
+ /** PATCH /v1/appStoreVersionPhasedReleases/<id> 设 phasedReleaseState(合法值 ACTIVE/PAUSED/COMPLETE)。 */
265
+ async setPhasedReleaseState(phasedId, state) {
266
+ await this.ascFetch('PATCH', `/v1/appStoreVersionPhasedReleases/${enc(phasedId)}`, {
267
+ data: { type: 'appStoreVersionPhasedReleases', id: phasedId, attributes: { phasedReleaseState: state } },
268
+ });
269
+ }
270
+ /** 手动触发发布:POST /v1/appStoreVersionReleaseRequests。 */
271
+ async requestRelease(versionId) {
272
+ await this.ascFetch('POST', '/v1/appStoreVersionReleaseRequests', {
273
+ data: {
274
+ type: 'appStoreVersionReleaseRequests',
275
+ relationships: { appStoreVersion: { data: { type: 'appStoreVersions', id: versionId } } },
276
+ },
277
+ });
278
+ }
279
+ }
280
+ //# sourceMappingURL=asc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asc.js","sourceRoot":"","sources":["../../../../src/adapters/ios/appstore/asc.ts"],"names":[],"mappings":"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"}