@chengyu08/ipa-cli 0.2.1 → 0.2.3

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 (68) hide show
  1. package/README.md +477 -199
  2. package/README.screenshots.md +20 -0
  3. package/assets/screenshot-frames/iphone-dark-03.webp +0 -0
  4. package/dist/src/cli.js +23 -2
  5. package/dist/src/cli.js.map +1 -1
  6. package/dist/src/commands/actions.d.ts +2 -0
  7. package/dist/src/commands/actions.js +127 -0
  8. package/dist/src/commands/actions.js.map +1 -0
  9. package/dist/src/commands/convert.d.ts +2 -0
  10. package/dist/src/commands/convert.js +105 -0
  11. package/dist/src/commands/convert.js.map +1 -0
  12. package/dist/src/commands/knowledge.d.ts +2 -0
  13. package/dist/src/commands/knowledge.js +235 -0
  14. package/dist/src/commands/knowledge.js.map +1 -0
  15. package/dist/src/commands/screenshots.js +83 -5
  16. package/dist/src/commands/screenshots.js.map +1 -1
  17. package/dist/src/commands/upload.js +90 -34
  18. package/dist/src/commands/upload.js.map +1 -1
  19. package/dist/src/core/actions/ios-build-template.d.ts +28 -0
  20. package/dist/src/core/actions/ios-build-template.js +491 -0
  21. package/dist/src/core/actions/ios-build-template.js.map +1 -0
  22. package/dist/src/core/convert.d.ts +14 -0
  23. package/dist/src/core/convert.js +440 -0
  24. package/dist/src/core/convert.js.map +1 -0
  25. package/dist/src/core/ip.js +2 -2
  26. package/dist/src/core/knowledge/parser.d.ts +7 -0
  27. package/dist/src/core/knowledge/parser.js +233 -0
  28. package/dist/src/core/knowledge/parser.js.map +1 -0
  29. package/dist/src/core/knowledge/similarity.d.ts +10 -0
  30. package/dist/src/core/knowledge/similarity.js +48 -0
  31. package/dist/src/core/knowledge/similarity.js.map +1 -0
  32. package/dist/src/core/knowledge/stats.d.ts +23 -0
  33. package/dist/src/core/knowledge/stats.js +33 -0
  34. package/dist/src/core/knowledge/stats.js.map +1 -0
  35. package/dist/src/core/knowledge/store.d.ts +11 -0
  36. package/dist/src/core/knowledge/store.js +54 -0
  37. package/dist/src/core/knowledge/store.js.map +1 -0
  38. package/dist/src/core/screenshots/background.js +33 -24
  39. package/dist/src/core/screenshots/background.js.map +1 -1
  40. package/dist/src/core/screenshots/config-template.d.ts +130 -2
  41. package/dist/src/core/screenshots/config-template.js +357 -28
  42. package/dist/src/core/screenshots/config-template.js.map +1 -1
  43. package/dist/src/core/screenshots/convert-6.5.d.ts +8 -0
  44. package/dist/src/core/screenshots/convert-6.5.js +254 -0
  45. package/dist/src/core/screenshots/convert-6.5.js.map +1 -0
  46. package/dist/src/core/screenshots/render-issue.d.ts +15 -0
  47. package/dist/src/core/screenshots/render-issue.js +58 -0
  48. package/dist/src/core/screenshots/render-issue.js.map +1 -0
  49. package/dist/src/core/screenshots/render.d.ts +6 -0
  50. package/dist/src/core/screenshots/render.js +388 -55
  51. package/dist/src/core/screenshots/render.js.map +1 -1
  52. package/dist/src/core/screenshots/report.d.ts +3 -1
  53. package/dist/src/core/screenshots/report.js +39 -0
  54. package/dist/src/core/screenshots/report.js.map +1 -1
  55. package/dist/src/core/upload.d.ts +4 -1
  56. package/dist/src/core/upload.js +174 -17
  57. package/dist/src/core/upload.js.map +1 -1
  58. package/dist/src/models/convert.d.ts +93 -0
  59. package/dist/src/models/convert.js +36 -0
  60. package/dist/src/models/convert.js.map +1 -0
  61. package/dist/src/models/knowledge.d.ts +32 -0
  62. package/dist/src/models/knowledge.js +26 -0
  63. package/dist/src/models/knowledge.js.map +1 -0
  64. package/dist/src/models/screenshots.d.ts +105 -5
  65. package/dist/src/models/screenshots.js +59 -0
  66. package/dist/src/models/screenshots.js.map +1 -1
  67. package/docs/screenshots-command.md +167 -10
  68. package/package.json +3 -1
package/README.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - 基于本地基线库执行 Top N 相似检索
19
19
  - 执行 `analyze + scan + Top1 compare` 的端到端冒烟检查
20
20
  - 使用 Apple Transporter 将 IPA 真实上传到 App Store Connect
21
+ - 初始化 GitHub Actions iOS IPA 构建模板、签名材料目录和 Secrets 说明
21
22
  - 生成、校验和批量渲染 App Store 上架图
22
23
  - 批量扫描当前目录或多个子项目中的静态图片风险
23
24
  - 输出机器可消费的静态图片整改清单 JSON
@@ -75,12 +76,11 @@ ipa-cli compare ./AppA.ipa ./AppB.ipa --format panel
75
76
  # 3) 做一次完整冒烟检查
76
77
  ipa-cli smoke ./YourApp.ipa --library ./data/library --format text
77
78
 
78
- # 4) 预检并准备上传到 App Store Connect
79
- ipa-cli upload ./YourApp.ipa \
80
- --username dev@example.com \
81
- --password-env APPSTORE_PASSWORD \
82
- --asset-description ./AppStoreInfo.plist \
83
- --dry-run
79
+ # 4) 直接接收 Xcode 导出目录,默认 verify 后再 upload
80
+ ipa-cli upload ./dist/export
81
+
82
+ # 5) 初始化 GitHub Actions iOS 构建模板
83
+ ipa-cli actions init-ios-build ./YourProject
84
84
  ```
85
85
 
86
86
  如果你还没有全局安装,也可以把上面的 `ipa-cli` 替换成 `npx @chengyu08/ipa-cli`。
@@ -190,218 +190,341 @@ ipa-cli assets review . --scope children --format panel
190
190
 
191
191
  ### 9. 上传 IPA 到 App Store Connect
192
192
 
193
- 这一节给出最常用的上传操作流程。后文 `upload` 命令说明里会再展开每个参数的含义、边界和更多示例。
193
+ 这一节只讲推荐做法:把 Xcode 导出目录直接交给 `ipa-cli`,让工具自动完成本地预检、Apple verify 和正式上传。
194
+
195
+ 推荐的目录结构如下:
196
+
197
+ ```text
198
+ dist/export/
199
+ YourApp.ipa
200
+ AppStoreInfo.plist
201
+ ```
202
+
203
+ 如果你的导出目录里只有下面这些文件:
204
+
205
+ ```text
206
+ dist/export/
207
+ YourApp.ipa
208
+ ExportOptions.plist
209
+ DistributionSummary.plist
210
+ ```
211
+
212
+ 那还不够。它们的作用不同:
213
+
214
+ - `ExportOptions.plist`
215
+ 这是 Xcode 导出时使用的配置文件,描述导出方式和签名选项
216
+ - `DistributionSummary.plist`
217
+ 这是导出结果摘要,主要记录签名、provisioning、entitlements 等信息
218
+ - `AppStoreInfo.plist`
219
+ 这是给 Apple Transporter 上传 IPA 时使用的描述文件
194
220
 
195
- 推荐按下面顺序操作:
221
+ `ExportOptions.plist` 和 `DistributionSummary.plist` 不能替代 `AppStoreInfo.plist`。
196
222
 
197
- 1. 先准备好 `.ipa`、账号凭据,以及 Linux / Windows 必需的 `AppStoreInfo.plist`
198
- 2. 先跑一次 `--dry-run`,确认命令拼装、路径和认证方式都正确
199
- 3. 再跑一次 `--verify-only`,让 Apple Transporter 先做真实预校验
200
- 4. 最后执行正式上传
223
+ 推荐流程:
224
+
225
+ 1. 先配置一次环境变量
226
+ 2. 之后直接执行 `ipa-cli upload ./dist/export`
227
+ 3. 默认流程会自动执行 `verify -> upload`
201
228
 
202
229
  开始前请确认:
203
230
 
204
231
  - App 已经在 App Store Connect 中创建,Bundle ID 与待上传构建一致
205
232
  - 账号角色具备上传构建版本权限
206
- - IPA Xcode 导出的正式构建产物,而不是随手打包的临时文件
207
- - Linux / Windows 上传时,必须同时提供 Xcode 导出的 `AppStoreInfo.plist`
233
+ - 输入优先使用 Xcode 导出目录,不建议手工分开传 `.ipa` 和 `AppStoreInfo.plist`
234
+ - Linux / Windows 上传时,导出目录中必须包含 `AppStoreInfo.plist`
208
235
  - 如果使用 API key,Account Holder 需要先在 App Store Connect 开通 API 访问并生成 key
209
236
 
210
- 认证方式建议如下:
237
+ 推荐的环境变量:
238
+
239
+ ```bash
240
+ # Apple ID 方式
241
+ export ASC_APPLE_ID="dev@example.com"
242
+ export ASC_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
243
+
244
+ # API key 方式
245
+ export ASC_API_KEY="ABCD123456"
246
+ export ASC_API_ISSUER="57246542-96fe-1a63-e053-0824d011072a"
247
+ export ASC_PRIVATE_KEY_FILE="./AuthKey_ABCD123456.p8"
248
+
249
+ # 可选
250
+ export ASC_PROVIDER="ExampleProvider"
251
+ export ASC_TRANSPORTER_PATH="/usr/local/itms/bin/iTMSTransporter"
252
+ ```
253
+
254
+ ### 9.1 先确认你有没有 `AppStoreInfo.plist`
255
+
256
+ 如果你是通过 Xcode Organizer 直接点导出,目录里不一定会自动出现 `AppStoreInfo.plist`。Apple 官方说明里明确提到,这个文件会在你使用 `xcodebuild` 导出,并且开启 `generateAppStoreInformation=YES` 时生成。
257
+
258
+ 对于 Flutter 项目,最稳妥的做法不是猜 Flutter 导出目录里会不会顺带出现它,而是显式走这条链路:
259
+
260
+ 1. `flutter build ipa` 先生成 `.xcarchive`
261
+ 2. `xcodebuild -exportArchive` 再按 `ExportOptions.plist` 导出
262
+ 3. 在导出目录里检查 `AppStoreInfo.plist`
263
+
264
+ 一个最直接的做法是:
265
+
266
+ 1. 准备一个 `ExportOptions.plist`
267
+ 2. 确保其中包含 `generateAppStoreInformation`
268
+ 3. 用 `xcodebuild -exportArchive` 导出
269
+
270
+ 仓库里已经放了一份可直接复制的模板:
271
+
272
+ ```text
273
+ scripts/ExportOptions.app-store.example.plist
274
+ ```
275
+
276
+ 你可以先复制它:
211
277
 
212
- - 本地手工上传:优先用 `--username + --password-env`
213
- - CI 或自动化:优先用 `--api-key + --api-issuer + --private-key-file`
214
- - 已有外部签发 JWT:直接用 `--jwt`
278
+ ```bash
279
+ cp ./scripts/ExportOptions.app-store.example.plist ./ios/ExportOptions.plist
280
+ ```
215
281
 
216
- ### 9.1 先做 dry-run
282
+ 示例 `ExportOptions.plist`:
217
283
 
218
- 先让 CLI 做本地预检,确认它能识别 IPA、认证方式、Transporter 路径和 `AppStoreInfo.plist`,但不会真正调用 Apple 上传。
284
+ ```xml
285
+ <?xml version="1.0" encoding="UTF-8"?>
286
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
287
+ <plist version="1.0">
288
+ <dict>
289
+ <key>method</key>
290
+ <string>app-store</string>
291
+ <key>signingStyle</key>
292
+ <string>automatic</string>
293
+ <key>generateAppStoreInformation</key>
294
+ <true/>
295
+ </dict>
296
+ </plist>
297
+ ```
219
298
 
220
- Apple ID 示例:
299
+ 示例导出命令:
221
300
 
222
301
  ```bash
223
- ipa-cli upload ./YourApp.ipa \
224
- --username dev@example.com \
225
- --password-env APPSTORE_PASSWORD \
226
- --asset-description ./AppStoreInfo.plist \
227
- --dry-run
302
+ xcodebuild -exportArchive \
303
+ -archivePath ./build/YourApp.xcarchive \
304
+ -exportPath ./dist/export \
305
+ -exportOptionsPlist ./ExportOptions.plist
228
306
  ```
229
307
 
230
- API key 示例:
308
+ 导出完成后,检查:
231
309
 
232
310
  ```bash
233
- ipa-cli upload ./YourApp.ipa \
234
- --api-key ABCD123456 \
235
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
236
- --private-key-file ./AuthKey_ABCD123456.p8 \
237
- --asset-description ./AppStoreInfo.plist \
238
- --dry-run
311
+ ls -la ./dist/export
239
312
  ```
240
313
 
241
- 你会看到一段“上传预检”摘要,包括:
314
+ 理想情况下你会看到:
242
315
 
243
- - 应用名、Bundle ID、Version、Build
244
- - 当前使用的 Transporter 模式:`upload` 或 `verify`
245
- - 当前认证方式:`apple-id`、`api-key` 或 `jwt`
246
- - `Transporter` 可执行文件路径
247
- - `Asset Description` 和 `Event File` 路径
316
+ ```text
317
+ YourApp.ipa
318
+ AppStoreInfo.plist
319
+ ExportOptions.plist
320
+ DistributionSummary.plist
321
+ ```
248
322
 
249
- ### 9.2 再做 Apple 官方预校验
323
+ 如果没有 `AppStoreInfo.plist`,当前仓库的 `upload` 命令会直接告诉你:`ExportOptions.plist` 和 `DistributionSummary.plist` 不能替代它,并提示你重新导出。
250
324
 
251
- 如果你希望在正式上传前就拿到 Apple 侧的校验结果,使用 `--verify-only`。这会调用 Apple Transporter 的 `verify` 模式,不执行真正上传。
325
+ #### Flutter 项目推荐步骤
252
326
 
253
- 只校验 metadata:
327
+ 假设你在 Flutter 项目根目录下操作,推荐顺序如下。
328
+
329
+ 第一步,复制模板:
254
330
 
255
331
  ```bash
256
- ipa-cli upload ./YourApp.ipa \
257
- --verify-only \
258
- --metadata-only \
259
- --event-file ./transporter-events.json \
260
- --api-key ABCD123456 \
261
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
262
- --private-key-file ./AuthKey_ABCD123456.p8 \
263
- --asset-description ./AppStoreInfo.plist
332
+ cp ./scripts/ExportOptions.app-store.example.plist ./ios/ExportOptions.plist
264
333
  ```
265
334
 
266
- 校验 metadata 和 assets
335
+ 第二步,先生成 archive
267
336
 
268
337
  ```bash
269
- ipa-cli upload ./YourApp.ipa \
270
- --verify-only \
271
- --event-file ./transporter-events.json \
272
- --api-key ABCD123456 \
273
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
274
- --private-key-file ./AuthKey_ABCD123456.p8 \
275
- --asset-description ./AppStoreInfo.plist
338
+ flutter build ipa --release
276
339
  ```
277
340
 
278
- 说明:
341
+ Flutter 官方文档说明,这一步会生成:
279
342
 
280
- - `--verify-only` 会调用 Apple 官方 `verify` 模式
281
- - `--metadata-only` 只在 verify 模式下有效
282
- - `--event-file` 会把 Apple 结构化输出落到文件,便于 CI 归档和后处理
283
- - verify 成功不代表构建已经上传,只代表它通过了这一步预校验
343
+ - `build/ios/archive/Runner.xcarchive`
344
+ - `build/ios/ipa/*.ipa`
284
345
 
285
- ### 9.3 再执行正式上传
346
+ 第三步,用 `xcodebuild` 重新导出,显式生成 `AppStoreInfo.plist`:
286
347
 
287
- Apple ID 上传:
348
+ ```bash
349
+ xcodebuild -exportArchive \
350
+ -archivePath build/ios/archive/Runner.xcarchive \
351
+ -exportPath dist/export \
352
+ -exportOptionsPlist ios/ExportOptions.plist
353
+ ```
354
+
355
+ 第四步,检查导出目录:
356
+
357
+ ```bash
358
+ ls -la dist/export
359
+ ```
360
+
361
+ 第五步,确认看到了:
362
+
363
+ ```text
364
+ Runner.ipa
365
+ AppStoreInfo.plist
366
+ ExportOptions.plist
367
+ DistributionSummary.plist
368
+ ```
369
+
370
+ 第六步,再交给 `ipa-cli`:
288
371
 
289
372
  ```bash
290
- ipa-cli upload ./YourApp.ipa \
291
- --username dev@example.com \
292
- --password-env APPSTORE_PASSWORD \
293
- --asset-description ./AppStoreInfo.plist
373
+ ipa-cli upload ./dist/export
294
374
  ```
295
375
 
296
- 如果你的账号关联多个 provider,可以补 `--provider`:
376
+ 如果你想在 Flutter 项目里把这套流程先只跑检查,不正式上传:
297
377
 
298
378
  ```bash
299
- ipa-cli upload ./YourApp.ipa \
300
- --username dev@example.com \
301
- --password-env APPSTORE_PASSWORD \
302
- --provider ExampleProvider \
303
- --asset-description ./AppStoreInfo.plist
379
+ ipa-cli upload ./dist/export --check-only
304
380
  ```
305
381
 
306
- API key 上传:
382
+ 如果你已经有自己的 `ExportOptions.plist`,也可以不使用仓库模板。关键只有一个:必须包含 `generateAppStoreInformation = YES`。
383
+
384
+ ### 9.2 最简单的正式上传
385
+
386
+ 环境变量已经配置好后,最简单的命令就是:
307
387
 
308
388
  ```bash
309
- ipa-cli upload ./YourApp.ipa \
310
- --api-key ABCD123456 \
311
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
312
- --private-key-file ./AuthKey_ABCD123456.p8 \
313
- --asset-description ./AppStoreInfo.plist
389
+ ipa-cli upload ./dist/export
314
390
  ```
315
391
 
316
- 如果你已经有 JWT,也可以直接上传:
392
+ 默认执行流程:
393
+
394
+ 1. 本地预检
395
+ 2. Apple `verify`
396
+ 3. Apple `upload`
397
+
398
+ 这也是现在最推荐的默认路径。用户不需要自己决定先 verify 还是先 upload。
399
+
400
+ ### 9.3 先做 dry-run
401
+
402
+ 第一次上手、刚换机器、或者刚调整凭据时,先跑:
317
403
 
318
404
  ```bash
319
- ipa-cli upload ./YourApp.ipa \
320
- --jwt "$ASC_JWT" \
321
- --asset-description ./AppStoreInfo.plist
405
+ ipa-cli upload ./dist/export --dry-run
322
406
  ```
323
407
 
324
- 上传成功后,CLI 只表示“上传请求已提交给 Apple”。构建通常还要经过 App Store Connect processing,稍后才会在后台可见。
408
+ 它不会调用 Apple,只会本地检查:
409
+
410
+ - 目录里是否有唯一的 `.ipa`
411
+ - 是否发现了 `AppStoreInfo.plist`
412
+ - 当前会使用哪种认证方式
413
+ - 当前准备执行哪些步骤
414
+ - 当前要调用哪个 `iTMSTransporter`
325
415
 
326
- ### 9.4 常见场景示例
416
+ ### 9.4 只做检查,不正式上传
327
417
 
328
- macOS 本地上传,省略 `AppStoreInfo.plist`:
418
+ 如果你想让 Apple 做真实预校验,但这次不想正式上传:
329
419
 
330
420
  ```bash
331
- ipa-cli upload ./YourApp.ipa \
332
- --username dev@example.com \
333
- --password-env APPSTORE_PASSWORD
421
+ ipa-cli upload ./dist/export --check-only
334
422
  ```
335
423
 
336
- 说明:macOS 允许不传 `AppStoreInfo.plist`,但如果你本来就有 Xcode 导出的文件,仍然建议一起传入,行为更稳定。
424
+ 如果你只想检查 metadata:
337
425
 
338
- Linux 上传:
426
+ ```bash
427
+ ipa-cli upload ./dist/export --check-only --metadata-only
428
+ ```
429
+
430
+ 如果你希望把 Apple 结构化输出写入文件,便于 CI 归档:
339
431
 
340
432
  ```bash
341
- ipa-cli upload ./YourApp.ipa \
342
- --api-key ABCD123456 \
343
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
344
- --private-key-file ./AuthKey_ABCD123456.p8 \
345
- --asset-description ./AppStoreInfo.plist
433
+ ipa-cli upload ./dist/export \
434
+ --check-only \
435
+ --event-file ./artifacts/transporter-events.json
346
436
  ```
347
437
 
348
- Windows 上传并显式指定 Transporter 路径:
438
+ ### 9.5 跳过 verify,直接上传
439
+
440
+ 默认不建议这么做,但如果你非常确定不需要 verify,可以显式跳过:
349
441
 
350
442
  ```bash
351
- ipa-cli upload .\\YourApp.ipa ^
352
- --transporter "C:\\Program Files (x86)\\itms\\iTMSTransporter.cmd" ^
353
- --api-key ABCD123456 ^
354
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a ^
355
- --private-key-file .\\AuthKey_ABCD123456.p8 ^
356
- --asset-description .\\AppStoreInfo.plist
443
+ ipa-cli upload ./dist/export --skip-verify
357
444
  ```
358
445
 
359
- API key 但不显式传私钥文件:
446
+ ### 9.6 常见场景示例
447
+
448
+ macOS 本地最简单用法:
360
449
 
361
450
  ```bash
362
- ipa-cli upload ./YourApp.ipa \
363
- --api-key ABCD123456 \
364
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
365
- --asset-description ./AppStoreInfo.plist
451
+ export ASC_APPLE_ID="dev@example.com"
452
+ export ASC_APP_PASSWORD="xxxx-xxxx-xxxx-xxxx"
453
+
454
+ ipa-cli upload ./dist/export
366
455
  ```
367
456
 
368
- 说明:这种方式依赖 Transporter 自己按默认规则查找 `AuthKey_<keyId>.p8`。如果你希望命令行为更可控,建议始终显式传 `--private-key-file`。
457
+ Linux CI API key
458
+
459
+ ```bash
460
+ export ASC_API_KEY="ABCD123456"
461
+ export ASC_API_ISSUER="57246542-96fe-1a63-e053-0824d011072a"
462
+ export ASC_PRIVATE_KEY_FILE="./AuthKey_ABCD123456.p8"
463
+
464
+ ipa-cli upload ./dist/export --check-only
465
+ ipa-cli upload ./dist/export
466
+ ```
369
467
 
370
- CI 示例:
468
+ Windows 显式指定 Transporter:
371
469
 
372
470
  ```bash
373
- export APPSTORE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
471
+ set ASC_API_KEY=ABCD123456
472
+ set ASC_API_ISSUER=57246542-96fe-1a63-e053-0824d011072a
473
+ set ASC_PRIVATE_KEY_FILE=.\AuthKey_ABCD123456.p8
374
474
 
375
- ipa-cli upload ./build/YourApp.ipa \
376
- --verify-only \
377
- --event-file ./artifacts/transporter-events.json \
378
- --username dev@example.com \
379
- --password-env APPSTORE_PASSWORD \
380
- --asset-description ./build/AppStoreInfo.plist
475
+ ipa-cli upload .\dist\export ^
476
+ --transporter "C:\Program Files (x86)\itms\iTMSTransporter.cmd"
477
+ ```
381
478
 
382
- ipa-cli upload ./build/YourApp.ipa \
383
- --username dev@example.com \
384
- --password-env APPSTORE_PASSWORD \
385
- --asset-description ./build/AppStoreInfo.plist
479
+ 仍然使用单个 IPA 文件:
480
+
481
+ ```bash
482
+ ipa-cli upload ./dist/export/YourApp.ipa \
483
+ --asset-description ./dist/export/AppStoreInfo.plist
386
484
  ```
387
485
 
388
- ### 9.5 安全建议
486
+ 说明:单个 `.ipa` 仍然支持,但为了简化用户心智,优先推荐直接传导出目录。
487
+
488
+ 如果你的账号关联多个 provider,可以继续补 `--provider`:
489
+
490
+ ```bash
491
+ ipa-cli upload ./dist/export --provider ExampleProvider
492
+ ```
389
493
 
390
- - 优先使用 `--password-env`,不要把密码直接写死在 shell 历史里
494
+ ### 9.7 高级参数什么时候用
495
+
496
+ - `--asset-description <path>`
497
+ 当自动发现失败,或者你故意想覆盖默认 plist 时再传
498
+ - `--event-file <path>`
499
+ 适合 CI、日志归档和结构化后处理
500
+ - `--transporter <path>`
501
+ 适合 PATH 不稳定,或者机器上装了多个 Transporter 的场景
502
+ - `--jwt <token>`
503
+ 适合外部系统已经代你签发 JWT 的场景
504
+ - `--verify-only`
505
+ 旧行为兼容别名。新的主路径里优先使用 `--check-only`
506
+
507
+ ### 9.8 安全建议
508
+
509
+ - 优先使用环境变量,不要把密码直接写死在命令历史里
391
510
  - 不要把 `.p8` 私钥提交到仓库
392
511
  - `--event-file` 中可能包含 Apple 结构化返回信息,建议和构建产物一起归档,但不要对外公开
393
512
  - 如果你在 CI 中使用 JWT,尽量设置短有效期,避免长期复用
394
513
 
395
- ### 9.6 常见报错与处理
514
+ ### 9.9 常见报错与处理
396
515
 
397
516
  - `未找到 Apple Transporter`
398
517
  先安装 Apple Transporter,或者通过 `--transporter` 显式指定 `iTMSTransporter` 路径
518
+ - `导出目录中未找到 .ipa 文件`
519
+ 说明当前传入的目录不是 Xcode 标准导出目录,或者目录层级传错了
520
+ - `导出目录中找到多个 .ipa 文件`
521
+ 说明目录里放了多个导出产物。改成只保留一个,或者直接传入目标 `.ipa` 文件
399
522
  - `Linux / Windows 上传 App 时必须提供 --asset-description`
400
- 说明当前是非 macOS 环境,必须补上 Xcode 导出的 `AppStoreInfo.plist`
401
- - `--metadata-only 仅能和 --verify-only 一起使用`
402
- 说明你把 metadata-only 用在了正式上传模式,去掉它或者同时加上 `--verify-only`
523
+ 说明当前目录里没有可用的 `AppStoreInfo.plist`,或者你传的是单个 `.ipa` 但没补 plist
524
+ - `--metadata-only 不能和 --skip-verify 同时使用`
525
+ 说明你把 metadata-only 用在了跳过 verify 的流程里
403
526
  - `必须提供一种上传认证方式`
404
- 说明当前没有提供 Apple ID、API key 或 JWT 中的任意一种
527
+ 说明当前既没有命令行参数,也没有环境变量可用于上传认证
405
528
  - `上传认证方式只能选择一种`
406
529
  说明同时传了多套认证方式,例如 `--username` 和 `--api-key` 同时出现
407
530
  - `IPA 缺少 CFBundleIdentifier / CFBundleShortVersionString / CFBundleVersion`
@@ -472,6 +595,9 @@ cp ./scripts/ipatool.env.example ./scripts/ipatool.env
472
595
  - `scan`:把目标样本与本地基线库做 Top N 检索
473
596
  - `smoke`:执行 `analyze + scan + Top1 compare` 的端到端冒烟检查
474
597
  - `upload`:使用 Apple Transporter 将 IPA 上传到 App Store Connect
598
+ - `actions init-ios-build`:初始化 GitHub Actions iOS 构建模板、签名目录和 Secrets 说明
599
+ - `convert pdf-to-png`:将单个 PDF 文件转换为 PNG 图片
600
+ - `convert png-to-pdf`:将单张 PNG 或目录内多张 PNG 合并为 PDF
475
601
  - `screenshots init-config`:初始化上架图工作区,生成配置、`raw/` 目录和说明文档
476
602
  - `screenshots from-ipa`:从 IPA 中提取候选图片并自动生成上架图
477
603
  - `screenshots from-simulator`:从 iOS 模拟器采集一张原始截图
@@ -643,103 +769,113 @@ ipa-cli smoke ./fingerprint.json --library ./data/library --format html
643
769
 
644
770
  ### `upload`
645
771
 
646
- 使用 Apple Transporter IPA 上传到 App Store Connect,或先调用 Apple 官方 `verify` 模式做真实预校验。
772
+ 接收单个 `.ipa` 文件或 Xcode 导出目录,自动完成本地预检、Apple verify 和正式上传。
647
773
 
648
774
  ```bash
649
- ipa-cli upload <ipa> [options]
775
+ ipa-cli upload <input> [options]
650
776
  ```
651
777
 
652
- 最常见的两种用法如下:
778
+ 参数:
653
779
 
654
- 先做真实预校验:
780
+ - `<input>`:单个 `.ipa` 文件,或包含 `.ipa` / `AppStoreInfo.plist` 的 Xcode 导出目录
781
+ - `ExportOptions.plist` / `DistributionSummary.plist` 不能替代 `AppStoreInfo.plist`
782
+ - `--check-only`:执行本地预检并调用 Apple verify,不执行正式上传
783
+ - `--skip-verify`:跳过 Apple verify,直接上传
784
+ - `--metadata-only`:只在 verify 流程中有效,只校验 metadata
785
+ - `--transporter <path>`:显式指定 `iTMSTransporter` 路径
786
+ - `--asset-description <path>`:显式指定 `AppStoreInfo.plist`;当自动发现失败时使用
787
+ - `--event-file <path>`:输出 Apple 结构化事件文件;默认两步流程下会自动拆成 `*.verify.*` 和 `*.upload.*`
788
+ - `--username <appleId>` / `--password-env <env>`:Apple ID 方式
789
+ - `--api-key <keyId>` / `--api-issuer <issuerId>` / `--private-key-file <path>`:API key 方式
790
+ - `--jwt <token>`:直接传入外部生成的 JWT
791
+ - `--provider <shortName>`:Apple ID 多 provider 账号使用
792
+ - `--dry-run`:只做本地预检并展示计划执行的步骤,不真正调用 Apple
793
+
794
+ 最推荐的用法:
655
795
 
656
796
  ```bash
657
- ipa-cli upload ./YourApp.ipa \
658
- --verify-only \
659
- --event-file ./transporter-events.json \
660
- --api-key ABCD123456 \
661
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
662
- --private-key-file ./AuthKey_ABCD123456.p8 \
663
- --asset-description ./AppStoreInfo.plist
797
+ ipa-cli upload ./dist/export
664
798
  ```
665
799
 
666
- 再做正式上传:
800
+ 只做检查:
667
801
 
668
802
  ```bash
669
- ipa-cli upload ./YourApp.ipa \
670
- --api-key ABCD123456 \
671
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a \
672
- --private-key-file ./AuthKey_ABCD123456.p8 \
673
- --asset-description ./AppStoreInfo.plist
803
+ ipa-cli upload ./dist/export --check-only
674
804
  ```
675
805
 
676
- 参数:
806
+ 跳过 verify:
677
807
 
678
- - `<ipa>`:待上传的 `.ipa` 文件
679
- - `--verify-only`:调用 Apple Transporter `verify` 模式做真实预校验,不执行上传
680
- - `--metadata-only`:只在 verify 模式下有效,只校验 metadata,不校验 assets
681
- - `--transporter <path>`:显式指定 `iTMSTransporter` 路径
682
- - `--asset-description <path>`:Xcode 导出的 `AppStoreInfo.plist`;Linux / Windows 必填
683
- - `--event-file <path>`:保存 Apple 结构化事件输出,适合 CI 归档或后处理
684
- - `--username <appleId>`:Apple ID 登录方式
685
- - `--password <password>`:App 专用密码;更推荐 `--password-env`
686
- - `--password-env <env>`:从环境变量读取 App 专用密码
687
- - `--api-key <keyId>`:App Store Connect API key ID
688
- - `--api-issuer <issuerId>`:Team API key 对应的 issuer ID
689
- - `--private-key-file <path>`:`AuthKey_<keyId>.p8` 私钥文件;提供后 CLI 会本地生成 JWT
690
- - `--jwt <token>`:已生成好的 App Store Connect JWT
691
- - `--provider <shortName>`:仅 Apple ID 模式使用;多 provider 账号常需要
692
- - `--verbose`:把 Transporter 日志级别提升到 `eXtreme`
693
- - `--dry-run`:只做本地预检和命令预演,不执行 Apple 上传或 verify
808
+ ```bash
809
+ ipa-cli upload ./dist/export --skip-verify
810
+ ```
694
811
 
695
- 推荐操作顺序:
812
+ 第一次上手先跑 dry-run:
696
813
 
697
- 1. 先跑 `--dry-run`,确认路径、认证方式和 `AppStoreInfo.plist` 没问题
698
- 2. 再跑 `--verify-only`,拿到 Apple 官方预校验结果
699
- 3. 最后执行正式上传
814
+ ```bash
815
+ ipa-cli upload ./dist/export --dry-run
816
+ ```
700
817
 
701
- 更多示例:
818
+ 如果你已经提前配置了 `ASC_APPLE_ID` / `ASC_APP_PASSWORD` 或 `ASC_API_KEY` / `ASC_API_ISSUER` / `ASC_PRIVATE_KEY_FILE`,通常不再需要重复传认证参数。
702
819
 
703
- Apple ID + 环境变量密码:
820
+ ### `actions`
821
+
822
+ 初始化 GitHub Actions iOS 构建模板,并生成签名材料目录、Secrets 模板和指导文档。
704
823
 
705
824
  ```bash
706
- ipa-cli upload ./YourApp.ipa \
707
- --username dev@example.com \
708
- --password-env APPSTORE_PASSWORD \
709
- --asset-description ./AppStoreInfo.plist
825
+ ipa-cli actions init-ios-build [path] [options]
710
826
  ```
711
827
 
712
- Apple ID + 多 provider:
828
+ 参数:
829
+
830
+ - `[path]`:目标项目根目录,默认当前目录
831
+ - `--bundle-id <id>`:覆盖自动检测到的 Bundle ID
832
+ - `--xcode-workspace <path>`:workspace 路径,默认 `ios/Runner.xcworkspace`
833
+ - `--xcode-project <path>`:`project.pbxproj` 路径,默认 `ios/Runner.xcodeproj/project.pbxproj`
834
+ - `--scheme <name>`:Xcode scheme 名称,默认 `Runner`
835
+ - `--workflow-file <name>`:workflow 文件名,默认 `ios-build.yml`
836
+ - `--force`:覆盖已存在的初始化文件
837
+
838
+ 示例:
713
839
 
714
840
  ```bash
715
- ipa-cli upload ./YourApp.ipa \
716
- --username dev@example.com \
717
- --password-env APPSTORE_PASSWORD \
718
- --provider ExampleProvider \
719
- --asset-description ./AppStoreInfo.plist
841
+ ipa-cli actions init-ios-build ./YourProject
842
+ ipa-cli actions init-ios-build ./YourProject --bundle-id com.example.app --scheme MyApp
720
843
  ```
721
844
 
722
- direct JWT:
845
+ 生成后会得到:
846
+
847
+ - `.github/workflows/ios-build.yml`
848
+ - `ci/github-actions/ios-build/README.md`
849
+ - `ci/github-actions/ios-build/secrets.example.env`
850
+ - `ci/github-actions/ios-build/signing/`
851
+
852
+ 其中 README 会明确写出:
853
+
854
+ - `.cer`、`.p12`、`.mobileprovision` 分别放哪里
855
+ - 哪些 GitHub Secrets 需要创建
856
+ - 每个 Secret 的值从哪里来
857
+ - 如何用 `print-secrets.sh` 自动生成 base64 值
858
+
859
+ ### `convert`
860
+
861
+ 执行 PDF 与 PNG 之间的格式转换。
723
862
 
724
863
  ```bash
725
- ipa-cli upload ./YourApp.ipa \
726
- --jwt "$ASC_JWT" \
727
- --asset-description ./AppStoreInfo.plist
864
+ ipa-cli convert pdf-to-png <input> [--output <path>] [--scale <n>] [--overwrite] [--format text|json]
865
+ ipa-cli convert png-to-pdf <input> [--output <file>] [--overwrite] [--format text|json]
728
866
  ```
729
867
 
730
- Windows 显式指定 Transporter:
868
+ - `pdf-to-png`:把单个 PDF 转成 PNG。单页 PDF 默认输出同名 `.png`;多页 PDF 默认输出到同级 `<name>-png/` 目录。
869
+ - `png-to-pdf`:把单张 PNG 或目录内多张 PNG 合并为 PDF。目录模式会递归收集 `.png` 文件,并按路径排序后逐页写入。
870
+
871
+ 示例:
731
872
 
732
873
  ```bash
733
- ipa-cli upload .\\YourApp.ipa ^
734
- --transporter "C:\\Program Files (x86)\\itms\\iTMSTransporter.cmd" ^
735
- --api-key ABCD123456 ^
736
- --api-issuer 57246542-96fe-1a63-e053-0824d011072a ^
737
- --private-key-file .\\AuthKey_ABCD123456.p8 ^
738
- --asset-description .\\AppStoreInfo.plist
874
+ ipa-cli convert pdf-to-png ./docs/design.pdf --output ./docs/design-pages --scale 2
875
+ ipa-cli convert png-to-pdf ./screens --output ./screens.pdf
876
+ ipa-cli convert png-to-pdf ./poster.png --format json
739
877
  ```
740
878
 
741
- 如果你只是想先判断当前包“能不能过 Transporter 这一关”,优先用 `--verify-only`。如果 verify 成功,再执行正式上传,失败定位会更清楚。
742
-
743
879
  ### `screenshots`
744
880
 
745
881
  生成、渲染并校验 App Store 上架图。README 已内置完整用法说明,不需要跳转外部文档也能查看命令、配置字段和目录约定。
@@ -747,7 +883,7 @@ ipa-cli upload .\\YourApp.ipa ^
747
883
  当前支持的子命令:
748
884
 
749
885
  ```bash
750
- ipa-cli screenshots init-config [path] [--force]
886
+ ipa-cli screenshots init-config [path] [--force] [--template <id>] [--background-template <id>] [--layout-template <id>] [--device-frame-template <id>] [--list-templates]
751
887
  ipa-cli screenshots from-ipa <ipa> [--workspace <dir>] [--output <dir>] [--locale <locale>] [--target <target>] [--max-frames <n>] [--dry-run] [--overwrite] [--format text|json|panel]
752
888
  ipa-cli screenshots from-simulator [app] --output <file> [--device <name>] [--udid <udid>] [--bundle-id <id>] [--wait <ms>] [--overwrite] [--format text|json|panel]
753
889
  ipa-cli screenshots check [dir] [--platform ios] [--locale <locale>] [--strict] [--fingerprint <file>] [--format text|json|panel]
@@ -795,6 +931,9 @@ ipa-cli screenshots init-config [path] [--force]
795
931
  ipa-cli screenshots init-config
796
932
  ipa-cli screenshots init-config ./configs/appstore.screenshot.config.json --force
797
933
  ipa-cli screenshots init-config ./screenshots-workspace
934
+ ipa-cli screenshots init-config ./screenshots-workspace --template marketing
935
+ ipa-cli screenshots init-config ./screenshots-workspace --background-template image-hero --layout-template device-with-title --device-frame-template iphone-dark-03
936
+ ipa-cli screenshots init-config --list-templates
798
937
  ```
799
938
 
800
939
  默认会同时生成:
@@ -805,6 +944,34 @@ ipa-cli screenshots init-config ./screenshots-workspace
805
944
 
806
945
  如果参数传的是目录,例如 `./screenshots-workspace`,这些文件会生成在该目录下;如果参数传的是 `.json` 文件路径,则会生成在该配置文件所在目录下。默认模板会先生成 `targets: ["iphone-6.5"]`。这个槽位的可接受尺寸是 `1242 × 2688px`、`2688 × 1242px`、`1284 × 2778px`、`2778 × 1284px`,当前默认竖屏输出优先使用 `1284 × 2778px`。
807
946
 
947
+ `init-config` 现在把预设分成两层:
948
+
949
+ - 推荐预设:`starter`、`feature`、`marketing`
950
+ - 高级预设:`immersive`、`fullscreen`
951
+
952
+ - `--background-template`:背景模板,内置 `solid-light`、`gradient-blue`、`gradient-sunset`、`image-hero`、`raw-blur`
953
+ - `--layout-template`:布局模板,内置 `device-with-title-subtitle`、`device-with-title`、`raw-fullscreen`
954
+ - `--device-frame-template`:设备框模板,内置 `iphone-dark-01`、`iphone-dark-02`、`iphone-dark-03`、`none`
955
+
956
+ 为了减少记忆负担,`--template` 还支持一组场景别名:
957
+
958
+ - `app-store-starter`
959
+ - `app-store-feature`
960
+ - `app-store-marketing`
961
+ - `app-store-immersive`
962
+ - `app-store-fullscreen`
963
+
964
+ 推荐用法:
965
+
966
+ 1. 先只选 `--template`
967
+ 2. 只有预设不够时,再单独覆盖 `--background-template`、`--layout-template` 或 `--device-frame-template`
968
+
969
+ 如果你只想查看模板列表,不写任何文件,可以执行:
970
+
971
+ ```bash
972
+ ipa-cli screenshots init-config --list-templates
973
+ ```
974
+
808
975
  #### `screenshots from-simulator`
809
976
 
810
977
  从 iOS 模拟器采集一张原始截图,可选安装并启动 `.app` 后再截图。
@@ -909,6 +1076,26 @@ ipa-cli screenshots render ./screenshot.config.json --output ./screenshots --ove
909
1076
  }
910
1077
  ```
911
1078
 
1079
+ 如果你这次不需要标题、手机外框或副标题,只想把已经设计好的整张图直接输出为商店尺寸,可以单独使用 `raw-fullscreen`:
1080
+
1081
+ ```json
1082
+ {
1083
+ "version": 1,
1084
+ "platform": "ios",
1085
+ "targets": ["iphone-6.5"],
1086
+ "locales": ["zh-Hans"],
1087
+ "frames": [
1088
+ {
1089
+ "name": "01_launch",
1090
+ "raw": "raw/{locale}/01_launch.png",
1091
+ "layout": "raw-fullscreen"
1092
+ }
1093
+ ]
1094
+ }
1095
+ ```
1096
+
1097
+ 把原图放到 `raw/zh-Hans/01_launch.png` 后,直接执行 `ipa-cli screenshots render ./screenshot.config.json --output ./screenshots --format panel` 即可。`raw-fullscreen` 默认按 `cover` 铺满整个输出尺寸;只有想保留完整原图并露出背景层时,才需要额外设置 `rawFit: "contain"` 和 `background`。
1098
+
912
1099
  `init-config` 生成的默认配置会额外包含 `defaults`:
913
1100
 
914
1101
  ```json
@@ -954,9 +1141,16 @@ ipa-cli screenshots render ./screenshot.config.json --output ./screenshots --ove
954
1141
  | `defaults.background` | 否 | 全局背景配置;兼容纯色字符串,也支持 `preset`、`solid`、`linear-gradient`、`image`、`raw-blur`、`stack` |
955
1142
  | `defaults.titleColor` | 否 | 标题颜色,默认 `#111827` |
956
1143
  | `defaults.subtitleColor` | 否 | 副标题颜色,默认 `#4B5563` |
1144
+ | `defaults.titleStrokeColor` | 否 | 标题描边颜色;不填则不描边 |
1145
+ | `defaults.subtitleStrokeColor` | 否 | 副标题描边颜色;不填则不描边 |
957
1146
  | `defaults.titleFontSize` | 否 | 标题字号,单位 px;不填时按画布宽度自动计算 |
958
1147
  | `defaults.subtitleFontSize` | 否 | 副标题字号,单位 px;不填时按画布宽度自动计算 |
959
- | `defaults.fontFamily` | 否 | SVG 文本字体族,默认 `Inter, Arial, sans-serif` |
1148
+ | `defaults.fontFamily` | 否 | 标题/副标题共用字体族回退,默认 `Inter, Arial, sans-serif` |
1149
+ | `defaults.fontFile` | 否 | 标题/副标题共用字体文件回退;相对配置文件目录解析 |
1150
+ | `defaults.titleFontFamily` | 否 | 标题默认字体族;不填时回退到 `defaults.fontFamily` |
1151
+ | `defaults.titleFontFile` | 否 | 标题默认字体文件;不填时回退到 `defaults.fontFile` |
1152
+ | `defaults.subtitleFontFamily` | 否 | 副标题默认字体族;不填时回退到 `defaults.fontFamily` |
1153
+ | `defaults.subtitleFontFile` | 否 | 副标题默认字体文件;不填时回退到 `defaults.fontFile` |
960
1154
  | `defaults.orientation` | 否 | `portrait` 或 `landscape`,默认 `portrait` |
961
1155
  | `defaults.deviceWidthRatio` | 否 | 设备截图宽度相对画布宽度的比例,范围 `0.35` 到 `0.95`,默认 `0.76` |
962
1156
  | `defaults.deviceFrame` | 否 | 全局默认手机框配置;支持内置 `preset` 或自定义 `src` + `screen` |
@@ -966,28 +1160,78 @@ ipa-cli screenshots render ./screenshot.config.json --output ./screenshots --ove
966
1160
  | `frames[].raw` | 是 | 原始截图路径,相对配置文件所在目录解析;支持 `{locale}` 和 `{target}` 占位符 |
967
1161
  | `frames[].title` | 否 | 标题文案 |
968
1162
  | `frames[].subtitle` | 否 | 副标题文案 |
1163
+ | `frames[].subtitles` | 否 | 多副标题数组;存在时优先于 `subtitle` |
969
1164
  | `frames[].layout` | 否 | 布局类型,支持 `raw-fullscreen`、`device-with-title`、`device-with-title-subtitle` |
970
1165
  | `frames[].rawFit` | 否 | `raw-fullscreen` 下原图缩放方式;默认 `cover`,设为 `contain` 时可露出背景层 |
971
1166
  | `frames[].backgroundPreset` | 否 | 单张图引用背景预设名;优先级低于显式 `background`,高于全局默认背景 |
972
1167
  | `frames[].background` | 否 | 覆盖全局背景配置 |
973
1168
  | `frames[].titleColor` | 否 | 覆盖全局标题颜色 |
974
1169
  | `frames[].subtitleColor` | 否 | 覆盖全局副标题颜色 |
1170
+ | `frames[].titleStrokeColor` | 否 | 覆盖全局标题描边颜色 |
1171
+ | `frames[].subtitleStrokeColor` | 否 | 覆盖全局副标题描边颜色 |
975
1172
  | `frames[].titleFontSize` | 否 | 覆盖单张图标题字号,单位 px |
976
1173
  | `frames[].subtitleFontSize` | 否 | 覆盖单张图副标题字号,单位 px |
1174
+ | `frames[].titleFontFamily` | 否 | 覆盖单张图标题字体族 |
1175
+ | `frames[].titleFontFile` | 否 | 覆盖单张图标题字体文件 |
1176
+ | `frames[].subtitleFontFamily` | 否 | 覆盖单张图副标题默认字体族 |
1177
+ | `frames[].subtitleFontFile` | 否 | 覆盖单张图副标题默认字体文件 |
1178
+ | `frames[].subtitles[].text` | 否 | 单条副标题文案 |
1179
+ | `frames[].subtitles[].color` | 否 | 单条副标题颜色 |
1180
+ | `frames[].subtitles[].strokeColor` | 否 | 单条副标题描边颜色 |
1181
+ | `frames[].subtitles[].fontSize` | 否 | 单条副标题字号,单位 px |
1182
+ | `frames[].subtitles[].fontFamily` | 否 | 单条副标题字体族 |
1183
+ | `frames[].subtitles[].fontFile` | 否 | 单条副标题字体文件 |
977
1184
  | `frames[].deviceFrame` | 否 | 单张图覆盖手机框配置;可只写 `offsetY`、`scale` 等局部字段 |
978
1185
  | `frames[].target` | 否 | 只对某个目标槽位生效 |
979
1186
  | `frames[].locale` | 否 | 只对某个语言生效 |
980
1187
 
981
- 字号字段速查:
1188
+ 文字字体与字号速查:
982
1189
 
983
1190
  - `defaults.titleFontSize`:全局默认标题字号
984
1191
  - `defaults.subtitleFontSize`:全局默认副标题字号
985
1192
  - `frames[].titleFontSize`:覆盖单张图标题字号
986
1193
  - `frames[].subtitleFontSize`:覆盖单张图副标题字号
1194
+ - `defaults.titleFontFamily` / `defaults.titleFontFile`:标题默认字体
1195
+ - `defaults.subtitleFontFamily` / `defaults.subtitleFontFile`:副标题默认字体
1196
+ - `defaults.titleStrokeColor` / `defaults.subtitleStrokeColor`:标题 / 副标题默认描边颜色
1197
+ - `frames[].titleFontFamily` / `frames[].titleFontFile`:单张图覆盖标题字体
1198
+ - `frames[].subtitleFontFamily` / `frames[].subtitleFontFile`:单张图覆盖副标题默认字体
1199
+ - `frames[].titleStrokeColor` / `frames[].subtitleStrokeColor`:单张图覆盖标题 / 副标题描边颜色
1200
+ - `frames[].subtitles[].fontFamily` / `frames[].subtitles[].fontFile`:单条副标题单独字体
1201
+ - `frames[].subtitles[].strokeColor`:单条副标题单独描边颜色
1202
+ - `frames[].subtitles[].fontSize`:单条副标题单独字号
987
1203
  - 单位都是 `px`,值必须是正数
988
1204
  - `frames[]` 里的字号设置优先级高于 `defaults`
989
- - `subtitle` 只有在 `layout: "device-with-title-subtitle"` 时显示,所以 `subtitleFontSize` 也只会在这个布局下生效
1205
+ - `subtitle` 和 `subtitles` 只有在 `layout: "device-with-title-subtitle"` 时显示
1206
+ - 当 `frames[].subtitles` 存在时,会优先使用它,不再读取 `frames[].subtitle`
990
1207
  - 如果这些字段都不填,渲染器会按画布宽度自动计算字号
1208
+ - 如果配置了 `*StrokeColor`,渲染器会自动给文字加一层与字号成比例的描边
1209
+ - `fontFile` 系列字段优先级高于同级 `fontFamily`
1210
+ - 如果系统里没有安装对应字体,请同时配置 `fontFile`
1211
+
1212
+ 当前支持的字体:
1213
+
1214
+ - `Inter`
1215
+ - `PingFang SC`
1216
+ - `Noto Sans CJK`
1217
+ - `Noto Sans CJK SC`
1218
+ - `Microsoft YaHei`
1219
+ - `HarmonyOS Sans`
1220
+ - `HarmonyOS Sans SC`
1221
+ - `MiSans`
1222
+ - `Roboto`
1223
+
1224
+ 字体解析规则:
1225
+
1226
+ 1. 如果显式配置了 `fontFile`,渲染器优先使用该字体文件。
1227
+ 2. 如果没配 `fontFile`,但 `fontFamily` 命中了上述别名,而且本机存在已知字体文件,渲染器会自动绑定它。
1228
+ 3. 如果本机没有已知字体文件,渲染器会继续按 `fontFamily` 名称尝试系统字体解析。
1229
+
1230
+ 建议:
1231
+
1232
+ - `Roboto` 更适合英文、数字和技术感较强的副标题。
1233
+ - `HarmonyOS Sans`、`MiSans` 如果你的机器上没有安装,建议配合 `fontFile` 一起使用。
1234
+ - 想让标题和副标题使用不同字体时,优先用 `titleFontFamily` 和 `subtitleFontFamily`,不要只共用 `fontFamily`。
991
1235
 
992
1236
  ```json
993
1237
  {
@@ -1009,13 +1253,41 @@ ipa-cli screenshots render ./screenshot.config.json --output ./screenshots --ove
1009
1253
  }
1010
1254
  ```
1011
1255
 
1256
+ 多副标题示例:
1257
+
1258
+ ```json
1259
+ {
1260
+ "name": "02_support",
1261
+ "raw": "raw/{locale}/02_support.png",
1262
+ "layout": "device-with-title-subtitle",
1263
+ "title": "咨询客服",
1264
+ "titleFontFamily": "HarmonyOS Sans",
1265
+ "subtitles": [
1266
+ {
1267
+ "text": "智能秒回",
1268
+ "fontFamily": "HarmonyOS Sans",
1269
+ "fontSize": 38,
1270
+ "color": "#111827",
1271
+ "strokeColor": "#FFFFFF"
1272
+ },
1273
+ {
1274
+ "text": "人工专解",
1275
+ "fontFamily": "Roboto",
1276
+ "fontSize": 26,
1277
+ "color": "#DC2626",
1278
+ "strokeColor": "#111827"
1279
+ }
1280
+ ]
1281
+ }
1282
+ ```
1283
+
1012
1284
  布局类型:
1013
1285
 
1014
1286
  | layout | 画面效果 | 适合场景 | 注意事项 |
1015
1287
  | --- | --- | --- | --- |
1016
1288
  | `raw-fullscreen` | 原图直接铺满整个输出画布,不显示设备外框、标题或副标题 | 已经是完整营销图、KV 图、海报风页面图 | `rawFit: "cover"` 时只输出原图;`rawFit: "contain"` 时才会露出背景层 |
1017
- | `device-with-title` | 顶部显示一段标题,下方展示带手机框的设备截图 | 单卖点页、功能页、流程页 | 即使传了 `subtitle`,当前也不会显示 |
1018
- | `device-with-title-subtitle` | 顶部显示标题和副标题,下方展示带手机框的设备截图 | 首页、详情页、需要两级文案的场景 | 这是默认布局;不填 `subtitle` 时会只显示标题区 |
1289
+ | `device-with-title` | 顶部显示一段标题,下方展示带手机框的设备截图 | 单卖点页、功能页、流程页 | 即使传了 `subtitle` 或 `subtitles`,当前也不会显示 |
1290
+ | `device-with-title-subtitle` | 顶部显示标题和副标题,下方展示带手机框的设备截图 | 首页、详情页、需要两级文案的场景 | 这是默认布局;支持单个 `subtitle` 或多个 `subtitles[]` |
1019
1291
 
1020
1292
  配置写法示例:
1021
1293
 
@@ -2011,10 +2283,13 @@ ipa-cli analyze --help
2011
2283
  ipa-cli compare --help
2012
2284
  ipa-cli scan --help
2013
2285
  ipa-cli smoke --help
2286
+ ipa-cli actions --help
2287
+ ipa-cli actions init-ios-build --help
2014
2288
  ipa-cli assets --help
2015
2289
  ipa-cli assets review --help
2016
2290
  ipa-cli assets manifest --help
2017
2291
  ipa-cli assets obfuscate --help
2292
+ ipa-cli convert --help
2018
2293
  ipa-cli screenshots --help
2019
2294
  ipa-cli screenshots init-config --help
2020
2295
  ipa-cli screenshots from-simulator --help
@@ -2053,6 +2328,9 @@ ipa-cli scan ./YourApp.ipa --library ./data/library --top 5
2053
2328
  # 端到端冒烟
2054
2329
  ipa-cli smoke ./YourApp.ipa --library ./data/library --format text
2055
2330
 
2331
+ # 初始化 GitHub Actions iOS 构建模板
2332
+ ipa-cli actions init-ios-build ./YourProject
2333
+
2056
2334
  # 初始化上架图工作区
2057
2335
  ipa-cli screenshots init-config
2058
2336