fastlane-plugin-fastci 0.0.20 → 0.0.40

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1c8c592c87f4597919d8a5534434ed422a94d0560be2ddb2a82353aefe43648
4
- data.tar.gz: c17fd86d0c3084917668534196fe01053078952f6cdf30e81cf14c50b34dcd2f
3
+ metadata.gz: c5978da99fdc693c5d6f873bee51f791254b236e6eb92d9beaeea064ca730b52
4
+ data.tar.gz: 70cc23a65e930ee3df7359a10ae751544276fb64b0b9dd1db395033e7a12322b
5
5
  SHA512:
6
- metadata.gz: 6cee5ced946a258e71e30e8c7c7a445e6182ecc61aee1e1cf8a524486ec64a96c8b71eec99f259d58502296c4813c03ad915b6e8ae8b69519e85840306793f77
7
- data.tar.gz: ef562edaf613408355e8752fe5104948c5f39b2ea156ce241fd57cacefa36503e5199c6dd21b7afeac2edd8202531c9998bd00f05328ac0b766f02a25228a0ae
6
+ metadata.gz: a4ec254de949a0c02e98985d29d2d77d958da26c6a4925ecc8c151c3d29c1faa3766a3f11e410d4211fc4c20b9cb3e91e45688958fe6b9ca2b3278f1f23cb6cb
7
+ data.tar.gz: bc29f33a3c238d6aa61f7d9500c7eb97969b19c63231e321a8758b2adb7df4c7468b978a7fc5bc3a9aa5d516aefd96ca66b83924dcd6bf06145258202469dfc4
data/README.md CHANGED
@@ -25,14 +25,13 @@
25
25
 
26
26
  ## 使用方法
27
27
 
28
- 参考 [` Fastfile `](fastlane/Fastfile) 和 [` .env.default `](fastlane/.env.default) 替换项目内 fastlane 文件夹下文件;
28
+ 参考 [Fastfile](fastlane/Fastfile),将[配置模板](fastlane/.env.default.example)复制为项目中的 `fastlane/.env.default`,再填写项目参数;
29
29
  项目根目录新建 ` PACKAGE_FILE_FOLDER_NAME ` 配置对应名字文件夹,将描述文件、p12 证书、p8 密钥等文件放入该文件夹下;
30
30
  然后终端进入项目根目录即可使用 ` fastlane `
31
31
 
32
- 如果是同一个 xcworkspace 多 xcodeproj 的情况,可以采用多配置文件的方式。同样也是参考 [` .env.default `](fastlane/.env.default) 根据多个 xcodeproj 创建多个配置文件 ` .env.project1 ` 、 ` .env.project2 `,注意文件的隐藏扩展,名字不能是 ` .env.project1.default `;
32
+ 如果是同一个 xcworkspace 多 xcodeproj 的情况,可以采用多配置文件的方式。同样参考[配置模板](fastlane/.env.default.example),根据多个 xcodeproj 创建配置文件 ` .env.project1 `、` .env.project2 `;运行配置不保留 `.example` 后缀,名字也不能是 ` .env.project1.default `;
33
33
  不同的配置文件配置不同的 ` PROJECT_NAME `;
34
- 执行的时候指定环境文件 ` fastlane --env project1 package ` 来运行
35
-
34
+ 使用本仓库示例 Fastfile 时,指定环境文件执行 `bundle exec fastlane ios ipaPackage --env project1`。
36
35
 
37
36
  ### 使用后会在项目根目录生成文件夹
38
37
 
@@ -53,6 +52,8 @@ fastlane_cache/ # 插件缓存文件夹
53
52
  功能:自动编译并导出 ipa 包,支持多种打包方式和集成多项检查。
54
53
  生成完的 ipa 会放在桌面上,非 ` app-store ` 配置了蒲公英或 fir 参数会自动上传蒲公英或 fir。` app-store ` 和 ` testFlight ` 配置了商店参数会自动上传商店。
55
54
  配置了 Sentry 参数会自动上传符号表。
55
+ 配置了 Zealot 参数的非商店包也可自动上传,详见下方 Zealot 配置。
56
+ App Store 元数据、截图和更新说明默认不上传,必须单独开启商店资源总开关并配置资源 JSON;详见 [App Store 元数据与截图](#app-store-resources)。TestFlight 不使用商店资源配置。
56
57
 
57
58
  ` build `: 不指定的话内部有递增逻辑,格式为 ` 20250905.15(日期+当天包的次数) `
58
59
 
@@ -70,8 +71,10 @@ package(
70
71
  is_detect_duplicity_code: false, # 是否检测重复代码
71
72
  is_detect_unused_code: false, # 是否检测未使用代码
72
73
  is_detect_unused_image: false, # 是否检测未使用图片
73
- changelog: options[:changelog], # fir 更新日志
74
- release_notes: options[:release_notes], # 配合 jenkins 传参上传 appstore 格式为 { \"zh-Hans\": \"修复问题\", \"en-US\": \"bugfix\"} JSON 字符串
74
+ changelog: options[:changelog], # fir / Zealot 更新日志
75
+ app_store_resources_enabled: options[:app_store_resources_enabled], # 总开关;未传时读取 APP_STORE_RESOURCES_ENABLED,默认 false
76
+ app_store_resources_config_path: options[:app_store_resources_config_path], # 未传时读取 APP_STORE_RESOURCES_CONFIG_PATH
77
+ release_notes: options[:release_notes], # JSON 字符串,例如 '{"zh-Hans":"修复问题","en-US":"Bug fixes"}';仅资源功能开启且上传 metadata 时生效
75
78
  is_notice_dingding: options[:is_notice_dingding] # 配置钉钉token了,通过该参数控制是否发送通知
76
79
  )
77
80
  ```
@@ -124,6 +127,307 @@ detect_unused_image(
124
127
 
125
128
  ---
126
129
 
130
+ <a id="app-store-resources"></a>
131
+
132
+ ### 6. App Store 元数据与截图(配置驱动,仅 iOS)
133
+
134
+ 通过独立 `app_store_resources` Action 管理商店名称、副标题、描述、关键词、更新说明、宣传文本、支持/隐私链接、分类、审核信息及截图,沿用 Fastlane 的 metadata 目录格式。也可以接入现有 `package` / `upload_store` 流程:先上传 IPA,再上传配置指定的资源,最后按需选择 Build、提交审核。
135
+
136
+ **商店资源总开关默认关闭,必须设置 `APP_STORE_RESOURCES_ENABLED=true`,或显式传入 `app_store_resources_enabled: true` 才能使用。** 只配置 JSON 路径、资源目录、`metadata_changed` 或 `release_notes` 均不会开启功能。关闭时,`package` / `upload_store` 仅上传 IPA,明确跳过元数据、截图和更新说明;独立 `app_store_resources` 直接跳过,不读取资源配置、不认证、不上传下载、不选择构建或提交审核。TestFlight 和测试包不使用商店资源流程。
137
+
138
+ | 入口 | 用途 | 开启后的配置要求 |
139
+ | --- | --- | --- |
140
+ | `package` | 打包并上传 IPA,再执行资源流程 | 必须提供 `app_store_resources_config_path`;自动传入本次 Bundle ID、版本和 Build |
141
+ | `upload_store` | 上传已有 gym 产物,再执行资源流程 | 必须提供 `app_store_resources_config_path`;需要选择构建时自行提供版本与 Build |
142
+ | `app_store_resources` | 独立更新商店资源,不上传 IPA | 使用 `config_path` 或直接传入资源参数 |
143
+
144
+ #### 配置与认证
145
+
146
+ 将 [`fastlane/app_store_resources.example.json`](fastlane/app_store_resources.example.json) 复制到业务项目,例如 `fastlane/app_store_resources.json`,修改 Bundle ID、版本和资源路径。使用**扁平 JSON**;项目自有的嵌套 Jenkins 配置需要先转换为模板中的字段。
147
+
148
+ ```dotenv
149
+ # 总开关,未设置或 false 时均不能使用商店资源功能。
150
+ APP_STORE_RESOURCES_ENABLED=true
151
+ # 相对路径以执行 Action 时的项目根目录为基准,不以 JSON 所在目录为基准。
152
+ APP_STORE_RESOURCES_CONFIG_PATH=fastlane/app_store_resources.json
153
+
154
+ # 认证方式一:App Store Connect API Key JSON(本机或 CI 密钥文件)。
155
+ APP_STORE_CONNECT_API_KEY_PATH=/secure/app-store-connect.json
156
+
157
+ # 认证方式二:沿用 CONNECT_KEY_ID、CONNECT_ISSUER_ID 和证书目录下的 P8。
158
+ # 可选 APP_STORE_CONNECT_P8_PATH=/secure/AuthKey_KEYID.p8 覆盖默认路径。
159
+ ```
160
+
161
+ | 环境变量 | 默认值 | 说明 |
162
+ | --- | --- | --- |
163
+ | `APP_STORE_RESOURCES_ENABLED` | `false` | 商店资源总开关;未设置与关闭效果相同 |
164
+ | `APP_STORE_RESOURCES_CONFIG_PATH` | 未设置 | 资源 JSON 路径;`package` / `upload_store` 开启资源功能后必填 |
165
+ | `APP_STORE_CONNECT_API_KEY_PATH` | 未设置 | 认证用 API Key JSON 路径,与资源 JSON 是两个不同文件 |
166
+ | `APP_STORE_CONNECT_P8_PATH` | 未设置 | 自定义 P8 路径,配合 `CONNECT_KEY_ID`、`CONNECT_ISSUER_ID` 使用;不配置时从 `PACKAGE_FILE_FOLDER_NAME/AuthKey_<KEY_ID>.p8` 读取 |
167
+
168
+ 认证方式二的环境配置示例:
169
+
170
+ ```dotenv
171
+ CONNECT_KEY_ID=your-key-id
172
+ CONNECT_ISSUER_ID=your-issuer-id
173
+ APP_STORE_CONNECT_P8_PATH=/secure/AuthKey_your-key-id.p8
174
+ ```
175
+
176
+ 使用 P8 方式时不设置 `APP_STORE_CONNECT_API_KEY_PATH`。资源 JSON 或显式参数中的认证优先,其次是环境中的 API Key JSON、已有 lane 的 API Key,最后使用 P8 配置。已有 API Key 可直接复用,无需每次重新创建。
177
+
178
+ 资源配置示例:
179
+
180
+ ```json
181
+ {
182
+ "app_identifier": "com.example.app",
183
+ "app_version": "1.0.0",
184
+ "metadata_path": "fastlane/metadata",
185
+ "screenshots_path": "fastlane/screenshots",
186
+ "metadata_changed": true,
187
+ "screenshots_changed": false,
188
+ "upload_metadata_on_new_version": true,
189
+ "select_build": false,
190
+ "submit_for_review": false,
191
+ "automatic_release": false
192
+ }
193
+ ```
194
+
195
+ #### 元数据与截图目录
196
+
197
+ 元数据内容存放于文本文件,语言目录名与 App Store Connect 语言代码对应,例如:
198
+
199
+ ```text
200
+ fastlane/metadata/
201
+ ├── copyright.txt
202
+ ├── primary_category.txt
203
+ ├── review_information/notes.txt
204
+ ├── zh-Hans/
205
+ │ ├── name.txt
206
+ │ ├── subtitle.txt
207
+ │ ├── description.txt
208
+ │ ├── keywords.txt
209
+ │ ├── promotional_text.txt
210
+ │ ├── support_url.txt
211
+ │ ├── privacy_url.txt
212
+ │ └── release_notes.txt
213
+ └── en-US/
214
+ ├── description.txt
215
+ └── release_notes.txt
216
+ fastlane/screenshots/zh-Hans/1.png
217
+ ```
218
+
219
+ 只更新文案时设置 `metadata_changed=true`、`screenshots_changed=false`;只更新截图时设置 `metadata_changed=false`、`screenshots_changed=true`。如果还要禁止新版本自动补传文案,需同时设置 `upload_metadata_on_new_version=false`。上传的资源类别必须有非空目录,截图支持 `.png`、`.jpg`、`.jpeg`,图片格式和设备尺寸由 deliver 校验。
220
+
221
+ #### Action 与命令行调用
222
+
223
+ 总开关开启后,在已有打包 lane 中配置资源路径即可使用;也可以显式传入开关和路径:
224
+
225
+ ```ruby
226
+ package(
227
+ export_method: "app-store",
228
+ app_store_resources_enabled: true,
229
+ app_store_resources_config_path: "fastlane/app_store_resources.json"
230
+ )
231
+
232
+ # 只更新商店资源,不重新构建、不上传 IPA。
233
+ app_store_resources(
234
+ app_store_resources_enabled: true,
235
+ config_path: "fastlane/app_store_resources.json"
236
+ )
237
+
238
+ # 已有 gym 产物时,上传 IPA 后更新资源。
239
+ upload_store(
240
+ app_store_resources_enabled: true,
241
+ app_store_resources_config_path: "fastlane/app_store_resources.json"
242
+ )
243
+ ```
244
+
245
+ 总开关只通过环境变量或 Action 参数配置,不放在资源 JSON 中;显式 Action 参数优先于环境变量,因此传 `app_store_resources_enabled: false` 可以关闭环境中已开启的功能。`package` / `upload_store` 开启后必须同时提供资源配置路径,否则报错;独立 Action 开启后也可直接传入资源参数。
246
+
247
+ 使用本仓库示例 [Fastfile](fastlane/Fastfile) 时,可以在业务项目根目录运行以下命令。命令会执行实际上传,运行前应替换配置中的示例值并准备对应资源和认证文件。
248
+
249
+ ```bash
250
+ # 打包、上传 App Store IPA,再更新资源;开关、资源路径和认证读取环境文件。
251
+ bundle exec fastlane ios ipaPackage export_method:app-store
252
+
253
+ # 只更新商店资源;开关、资源路径和认证读取环境文件。
254
+ bundle exec fastlane ios appStoreResources
255
+
256
+ # 也可以在调用时明确开启,并为独立资源 lane 指定配置文件。
257
+ bundle exec fastlane ios appStoreResources app_store_resources_enabled:true config_path:fastlane/app_store_resources.json
258
+
259
+ # 本次仅上传 IPA,即使环境文件已开启资源功能也不更新资源。
260
+ bundle exec fastlane ios ipaPackage export_method:app-store app_store_resources_enabled:false
261
+ ```
262
+
263
+ 多项目环境可在命令后追加 `--env project1`。示例 lane 名是 `ipaPackage` 和 `appStoreResources`;已有项目使用自定义 lane 时,在对应 lane 中调用上面的 Action 即可。
264
+
265
+ #### 资源 JSON 参数
266
+
267
+ | JSON 参数 | 默认值 | 用途 |
268
+ | --- | --- | --- |
269
+ | `app_identifier` | `BUNDLE_ID` | 应用的 Bundle ID,最终不能为空 |
270
+ | `app_version` | 未设置 | 目标商店版本;选择构建或提交审核时必填 |
271
+ | `build_number` | 未设置 | 精确 Build 号;选择构建或提交审核时必填,使用字符串 |
272
+ | `metadata_path` / `screenshots_path` | `fastlane/metadata` / `fastlane/screenshots` | 相对路径以项目根目录为基准;未启用的资源类别不要求目录存在 |
273
+ | `metadata_changed` | `true` | 上传 metadata;设为 `false` 可跳过 |
274
+ | `screenshots_changed` | `false` | 上传截图,需显式开启 |
275
+ | `upload_metadata_on_new_version` | `true` | 指定 `app_version` 且目标可编辑版本不存在时补传 metadata,即使 `metadata_changed=false` |
276
+ | `download_missing_metadata` / `download_missing_screenshots` | `false` | 从 App Store Connect 初始化空的资源目录;不覆盖已有本地资源 |
277
+ | `use_live_version` | `false` | 初始化截图时使用线上版本 |
278
+ | `sync_screenshots` / `overwrite_screenshots` | `false` | 同步或替换远端截图,仅 `screenshots_changed=true` 时生效;同步还需 `FASTLANE_ENABLE_BETA_DELIVER_SYNC_SCREENSHOTS=true` |
279
+ | `select_build` | `false` | 按 `app_version` + `build_number` 选择处理完成的精确构建 |
280
+ | `wait_for_build_processing` | `true` | 选择构建前等待 Apple 处理 |
281
+ | `build_processing_timeout` / `build_processing_poll_interval` | `3600` / `30` | 等待超时和轮询间隔,单位秒 |
282
+ | `build_processing_retry_limit` / `build_processing_retry_interval` | `3` / `15` | 临时网络错误的重试次数和间隔,仍受总超时约束 |
283
+ | `submit_for_review` / `automatic_release` | `false` | 显式开启审核提交、审核通过后自动发布 |
284
+ | `release_notes` | 未设置 | 按语言提供更新说明的 JSON 对象,覆盖对应 `release_notes.txt` |
285
+ | `submission_information` | 未设置 | 提交审核所需的出口合规等信息 |
286
+ | `api_key_path` / `api_key` | 未设置 | API Key JSON 路径或 Key Hash,二者互斥 |
287
+ | `username` | 未设置 | 使用 Apple ID 登录时的用户名 |
288
+ | `platform` | `ios` | 当前资源 Action 仅支持 iOS |
289
+ | `force` | `true` | 跳过 deliver 的 HTML 预览确认 |
290
+
291
+ 配置优先级为**显式 Action 参数 > JSON > 默认值**。`package` 会用本次实际的 Bundle ID、版本号和 Build 号覆盖 JSON,避免选择旧构建;独立调用需要自行填写目标版本和构建号。开启 `select_build` 或 `submit_for_review` 时,两者均为必填。传给 `package` / `upload_store` 的 `release_notes` 仍沿用原 JSON 字符串格式,仅在上传 metadata 时生效。
292
+
293
+ #### 常见使用场景
294
+
295
+ 以下资源操作均以总开关已开启为前提。资源的变更开关由项目或 CI 设置,插件不自行运行 Git 差异检测。
296
+
297
+ | 场景 | 关键配置 |
298
+ | --- | --- |
299
+ | 只上传 IPA | 总开关关闭;不执行资源流程 |
300
+ | 只修改文案 | `metadata_changed=true`,`screenshots_changed=false`,调用独立资源 Action |
301
+ | 只更新截图 | `metadata_changed=false`,`screenshots_changed=true`,`upload_metadata_on_new_version=false` |
302
+ | 上传 IPA 后绑定本次构建 | 调用 `package`,资源 JSON 中配置 `select_build=true`;版本和 Build 使用本次产物值 |
303
+ | 仅选择已有构建 | 独立调用时提供 `app_version`、`build_number`,设置 `select_build=true`、两个 `*_changed=false`、`upload_metadata_on_new_version=false` |
304
+ | 提交审核 | 设置 `submit_for_review=true` 并提供版本与 Build;先确认构建处理成功并绑定,再提交审核 |
305
+
306
+ `select_build`、`submit_for_review`、`automatic_release` 均默认关闭。等待处理超时、构建处理失败或可编辑版本不匹配时会报错,不会改选其他构建。`automatic_release` 只控制本次审核通过后的发布方式,需配合 `submit_for_review=true` 使用。
307
+
308
+ 首次只下载资源时,可以直接调用:
309
+
310
+ ```ruby
311
+ app_store_resources(
312
+ app_store_resources_enabled: true,
313
+ app_identifier: "com.example.app",
314
+ metadata_path: "fastlane/metadata",
315
+ screenshots_path: "fastlane/screenshots",
316
+ metadata_changed: false,
317
+ screenshots_changed: false,
318
+ upload_metadata_on_new_version: false,
319
+ download_missing_metadata: true,
320
+ download_missing_screenshots: true,
321
+ use_live_version: true,
322
+ select_build: false,
323
+ submit_for_review: false,
324
+ automatic_release: false
325
+ )
326
+ ```
327
+
328
+ 此例的认证仍读取环境配置,截图使用已有线上版本;尚无线上版本时,需使用可编辑版本并设置 `use_live_version=false`。下载只初始化空的资源类别,不覆盖本地已有文案或截图。
329
+
330
+ #### 旧用法与失败处理
331
+
332
+ - 旧 lane 仅传 `release_notes` 时,现在只上传 IPA;要上传更新说明,必须开启总开关并提供资源配置,且允许上传 metadata。
333
+ - 总开关关闭时,资源路径和资源子开关不会生效,`Deliverfile` 中开启截图同步也不会绕过 IPA 上传阶段的禁用设置。
334
+ - `package` 开启资源功能但未配置路径时,会在构建前报错;`upload_store` 会在认证或上传前报错。
335
+ - 开启后的 JSON 格式错误、未知字段、无效参数类型或空资源目录会终止任务。若资源上传失败,后续构建选择和审核提交不会继续;已成功上传的 IPA 不会回滚。
336
+ - TestFlight、Zealot、Fir 和蒲公英不使用商店资源总开关。
337
+
338
+ 仅查看参数说明、不会触发上传的命令:
339
+
340
+ ```bash
341
+ bundle exec fastlane action package
342
+ bundle exec fastlane action upload_store
343
+ bundle exec fastlane action app_store_resources
344
+ ```
345
+
346
+ 目录格式和字段遵循 [Fastlane deliver 文档](https://docs.fastlane.tools/actions/deliver/)。
347
+
348
+ ### 7. Zealot(仅 iOS)
349
+
350
+ 通过官方 [fastlane-plugin-zealot](https://github.com/tryzealot/fastlane-plugin-zealot) 封装以下独立 Action:
351
+
352
+ | Action | 用途 |
353
+ | --- | --- |
354
+ | `upload_zealot` | 上传 IPA,返回版本与下载地址 |
355
+ | `upload_zealot_dsym` | 上传 iOS dSYM,支持 zip 或 xcarchive |
356
+ | `check_zealot_version` | 按 Bundle ID、版本号或 commit 查询版本 |
357
+ | `sync_zealot_devices` | 将 Apple 开发者账号中的 iOS 设备同步到 Zealot |
358
+
359
+ 在环境文件或 CI 环境变量中配置:
360
+
361
+ ```dotenv
362
+ ZEALOT_ENDPOINT=https://zealot.example.com
363
+ ZEALOT_TOKEN=your-user-token
364
+ ZEALOT_CHANNEL_KEY=your-ios-channel-key
365
+ ZEALOT_UPLOAD_DSYM=false
366
+ ```
367
+
368
+ `package` 与 Fir 一样按 token 判断是否上传:配置 `ZEALOT_TOKEN` 后,自动上传 `development`、`ad-hoc`、`enterprise` 包;token 留空时跳过。上传时必须同时提供 `ZEALOT_ENDPOINT` 和 `ZEALOT_CHANNEL_KEY`。`app-store` 和 `testFlight` 保持原上传流程。
369
+
370
+ ```ruby
371
+ package(
372
+ export_method: "development",
373
+ is_upload_zealot_dsym: true, # 默认 false;仅 IPA 上传成功后执行
374
+ changelog: "iOS 更新说明"
375
+ )
376
+ ```
377
+
378
+ 也可以单独调用,不需要重新构建:
379
+
380
+ ```ruby
381
+ result = upload_zealot(file: "./App.ipa", changelog: "iOS 更新说明")
382
+ # result:app_id、release_id、release_url、install_url、qrcode_url
383
+
384
+ upload_zealot_dsym(zip_file: "./App.app.dSYM.zip")
385
+ # 或指定 path: "./App.xcarchive";均未指定时使用本次 gym 产物。
386
+
387
+ exists = check_zealot_version(
388
+ bundle_id: "com.example.app",
389
+ release_version: "1.0",
390
+ build_version: "1"
391
+ )
392
+ # 也可用 git_commit 替代 release_version / build_version。
393
+
394
+ sync_zealot_devices(api_key_path: "./app_store_connect_api_key.json")
395
+ # 也可使用 username / team_id;设备同步不会在 package 中自动执行。
396
+ ```
397
+
398
+ 请求失败默认终止 lane,可传 `fail_on_error: false` 或设置 `ZEALOT_FAIL_ON_ERROR=false` 继续执行。版本查询返回 `true`(存在)、`false`(不存在)、`nil`(请求失败且允许继续);设备同步返回 `total`、`succeeded`、`failed`。token 在请求摘要中始终隐藏。
399
+
400
+ 上传参数支持官方的 `password`、`timeout`、`verify_ssl`、`branch`、`git_commit` 等选项,使用 `bundle exec fastlane action upload_zealot` 查看。示例 Fastfile 也提供 `uploadZealot`、`uploadZealotDsym`、`checkZealotVersion`、`syncZealotDevices` 四个 lane。
401
+
402
+ 依赖兼容:Zealot 的 `debug_file` 依赖 `ruby-macho < 3`,不能与 CocoaPods 1.17(依赖 4.x)放在同一 bundle。此类工程应将 CocoaPods 放入独立 Gemfile;使用 Faraday 1 的 Fastlane 工程还应将 `faraday-net_http` 限制为 `~> 1.0`。
403
+
404
+ ## 发布到 RubyGems
405
+
406
+ 工作流位于 [`.github/workflows/release.yml`](.github/workflows/release.yml)。使用 `1.0.0` 这样的纯版本标签,无需 `v` 前缀。推送数字开头的标签后,自动校验标签与 `lib/fastlane/plugin/fastci/version.rb` 中的 `VERSION` 完全一致,检查 Ruby 语法、构建 gem 并发布到 RubyGems。
407
+
408
+ 首次使用需完成一次 [RubyGems Trusted Publishing 配置](https://guides.rubygems.org/trusted-publishing/):
409
+
410
+ 用 gem 所有者账号打开 [fastlane-plugin-fastci](https://rubygems.org/gems/fastlane-plugin-fastci),进入 **Trusted publishers → Create**,填写:
411
+
412
+ | 字段 | 值 |
413
+ | --- | --- |
414
+ | Repository owner | `watermelon520` |
415
+ | Repository name | `fastlane-plugin-fastci` |
416
+ | Workflow filename | `release.yml` |
417
+ | Environment | 留空(可选) |
418
+
419
+ 工作流使用官方 OIDC 短期凭据,不需要配置 `RUBYGEMS_API_KEY` 或创建 GitHub Environment。工作流文件在当前仓库内,Workflow Repository 两项留空。若以后设置 Environment,RubyGems 配置与工作流的 `environment` 必须保持一致。
420
+
421
+ 每次发布先修改 `VERSION`,执行 `bundle install` 同步 `Gemfile.lock`,并提交本次拟发布的代码和工作流。然后推送对应标签,例如版本更新为 `0.0.40` 后:
422
+
423
+ ```bash
424
+ git push origin main
425
+ git tag -a 0.0.40 -m "Release 0.0.40"
426
+ git push origin 0.0.40
427
+ ```
428
+
429
+ 到 GitHub **Actions → Release to RubyGems** 查看结果。已发布版本不能覆盖;失败重跑前先确认该版本是否已经发布。只有打入标签提交的文件会被发布,本地未提交改动不会包含在内。
430
+
127
431
  ## 贡献与支持
128
432
 
129
433
  如需更多帮助或贡献,请提交 Issue 或 PR。
@@ -0,0 +1,300 @@
1
+ require 'fastlane/action'
2
+ require 'fastlane/plugin/fastci/helper/app_store_resources_helper'
3
+ include Fastlane::Helper
4
+
5
+ module Fastlane
6
+ module Actions
7
+ # 校验并上传 App Store Connect metadata 和截图
8
+ class AppStoreResourcesAction < Action
9
+ # 独立资源入口:准备资源 -> 上传 -> 可选构建选择 -> 可选提交审核,全程不上传 IPA。
10
+ def self.run(params)
11
+ return nil unless resources_enabled?(params)
12
+
13
+ params = prepare_resources(params)
14
+ upload_resources(params)
15
+ select_build_and_submit_review(params)
16
+ end
17
+
18
+ # 总开关先于配置读取和认证检查,单独指定路径或资源子开关不能启用此能力。
19
+ def self.resources_enabled?(params)
20
+ return true if params[:app_store_resources_enabled] == true
21
+
22
+ UI.message('*************| 商店资源功能未开启,跳过(APP_STORE_RESOURCES_ENABLED=false)|*************')
23
+ false
24
+ end
25
+
26
+ # 统一读取配置、建立认证并准备资源目录,下载与校验规则由 Helper 负责。
27
+ def self.prepare_resources(params)
28
+ params = AppStoreResourcesHelper.load_params(params)
29
+ params.merge!(AppStoreResourcesHelper.authentication_options(params, other_action))
30
+ AppStoreResourcesHelper.prepare(params)
31
+ end
32
+
33
+ # 只上传配置指定的资源类别,无资源变化时跳过上传。
34
+ def self.upload_resources(params)
35
+ if params[:metadata_changed] || params[:screenshots_changed]
36
+ UI.message('*************| 开始上传 App Store Connect 资源 |*************')
37
+ other_action.upload_to_app_store(
38
+ AppStoreResourcesHelper.resource_options(params, params[:metadata_path], params[:screenshots_path])
39
+ )
40
+ UI.success('App Store Connect 资源上传完成')
41
+ else
42
+ UI.message('*************| 没有 metadata 或截图变更,跳过资源上传 |*************')
43
+ end
44
+ end
45
+
46
+ # 资源上传完成后,按配置选择精确构建并提交审核;两个步骤默认均不执行。
47
+ def self.select_build_and_submit_review(params)
48
+ # 提交审核也必须先确认精确构建,不能依赖 deliver 自动选择最新 Build。
49
+ if params[:select_build] || params[:submit_for_review]
50
+ AppStoreResourcesHelper.select_build(params)
51
+ end
52
+
53
+ if params[:submit_for_review]
54
+ UI.message('*************| 开始提交 App Store 审核 |*************')
55
+ other_action.upload_to_app_store(AppStoreResourcesHelper.review_options(params))
56
+ UI.success('App Store 审核提交完成')
57
+ end
58
+ end
59
+
60
+ private_class_method :resources_enabled?, :prepare_resources, :upload_resources, :select_build_and_submit_review
61
+
62
+ def self.description
63
+ '校验并上传 App Store Connect metadata 和截图'
64
+ end
65
+
66
+ # JSON 合并后也通过这组参数校验,使文件配置与直接调用拥有相同约束。
67
+ def self.available_options
68
+ [
69
+ FastlaneCore::ConfigItem.new(
70
+ key: :app_store_resources_enabled,
71
+ env_name: 'APP_STORE_RESOURCES_ENABLED',
72
+ description: '商店资源总开关,必须显式开启后才能使用资源上传、下载及发布流程',
73
+ optional: true,
74
+ default_value: false,
75
+ type: Boolean
76
+ ),
77
+ FastlaneCore::ConfigItem.new(
78
+ key: :config_path,
79
+ env_name: 'APP_STORE_RESOURCES_CONFIG_PATH',
80
+ description: '资源发布 JSON 配置文件路径,显式参数优先于配置文件',
81
+ optional: true,
82
+ default_value: nil,
83
+ # 路径校验由开启后的 load_params 执行,关闭功能时不读取或校验资源文件。
84
+ type: String
85
+ ),
86
+ FastlaneCore::ConfigItem.new(
87
+ key: :app_identifier,
88
+ description: 'App Store Connect App 的 Bundle ID',
89
+ optional: true,
90
+ type: String
91
+ ),
92
+ FastlaneCore::ConfigItem.new(
93
+ key: :app_version,
94
+ description: '要更新的 App Store 版本号',
95
+ optional: true,
96
+ default_value: nil,
97
+ type: String
98
+ ),
99
+ FastlaneCore::ConfigItem.new(
100
+ key: :metadata_path,
101
+ description: 'metadata 目录路径',
102
+ optional: true,
103
+ default_value: 'fastlane/metadata',
104
+ type: String
105
+ ),
106
+ FastlaneCore::ConfigItem.new(
107
+ key: :screenshots_path,
108
+ description: '截图目录路径',
109
+ optional: true,
110
+ default_value: 'fastlane/screenshots',
111
+ type: String
112
+ ),
113
+ FastlaneCore::ConfigItem.new(
114
+ key: :metadata_changed,
115
+ description: '是否上传 metadata',
116
+ optional: true,
117
+ default_value: true,
118
+ type: Boolean
119
+ ),
120
+ FastlaneCore::ConfigItem.new(
121
+ key: :screenshots_changed,
122
+ description: '是否上传截图',
123
+ optional: true,
124
+ default_value: false,
125
+ type: Boolean
126
+ ),
127
+ FastlaneCore::ConfigItem.new(
128
+ key: :upload_metadata_on_new_version,
129
+ description: '目标 App Store 版本不存在时是否强制上传 metadata',
130
+ optional: true,
131
+ default_value: true,
132
+ type: Boolean
133
+ ),
134
+ FastlaneCore::ConfigItem.new(
135
+ key: :download_missing_metadata,
136
+ description: 'metadata 缺失时是否从 App Store Connect 下载',
137
+ optional: true,
138
+ default_value: false,
139
+ type: Boolean
140
+ ),
141
+ FastlaneCore::ConfigItem.new(
142
+ key: :download_missing_screenshots,
143
+ description: '截图缺失时是否从 App Store Connect 下载',
144
+ optional: true,
145
+ default_value: false,
146
+ type: Boolean
147
+ ),
148
+ FastlaneCore::ConfigItem.new(
149
+ key: :use_live_version,
150
+ description: '下载截图时是否使用线上版本',
151
+ optional: true,
152
+ default_value: false,
153
+ type: Boolean
154
+ ),
155
+ FastlaneCore::ConfigItem.new(
156
+ key: :api_key,
157
+ description: 'App Store Connect API Key Hash',
158
+ optional: true,
159
+ sensitive: true,
160
+ type: Hash,
161
+ conflicting_options: [:api_key_path]
162
+ ),
163
+ FastlaneCore::ConfigItem.new(
164
+ key: :api_key_path,
165
+ description: 'App Store Connect API Key JSON 文件路径',
166
+ optional: true,
167
+ type: String,
168
+ conflicting_options: [:api_key],
169
+ verify_block: proc do |value|
170
+ UI.user_error!("API Key JSON 不存在: #{value}") unless File.file?(value)
171
+ end
172
+ ),
173
+ FastlaneCore::ConfigItem.new(
174
+ key: :username,
175
+ description: 'Apple ID 用户名,仅在未提供 API Key 时使用',
176
+ optional: true,
177
+ type: String
178
+ ),
179
+ FastlaneCore::ConfigItem.new(
180
+ key: :platform,
181
+ description: 'App Store Connect 平台',
182
+ optional: true,
183
+ default_value: 'ios',
184
+ type: String,
185
+ verify_block: proc { |value| UI.user_error!('资源上传仅支持 ios 平台') unless value == 'ios' }
186
+ ),
187
+ FastlaneCore::ConfigItem.new(
188
+ key: :sync_screenshots,
189
+ description: '是否让远端截图与本地截图同步',
190
+ optional: true,
191
+ default_value: false,
192
+ type: Boolean
193
+ ),
194
+ FastlaneCore::ConfigItem.new(
195
+ key: :overwrite_screenshots,
196
+ description: '上传前是否清空远端截图',
197
+ optional: true,
198
+ default_value: false,
199
+ type: Boolean
200
+ ),
201
+ FastlaneCore::ConfigItem.new(
202
+ key: :submit_for_review,
203
+ description: '上传资源后是否提交审核',
204
+ optional: true,
205
+ default_value: false,
206
+ type: Boolean
207
+ ),
208
+ FastlaneCore::ConfigItem.new(
209
+ key: :automatic_release,
210
+ description: '审核通过后是否自动发布',
211
+ optional: true,
212
+ default_value: false,
213
+ type: Boolean
214
+ ),
215
+ FastlaneCore::ConfigItem.new(
216
+ key: :select_build,
217
+ description: '是否按 app_version 和 build_number 自动选择构建',
218
+ optional: true,
219
+ default_value: false,
220
+ type: Boolean
221
+ ),
222
+ FastlaneCore::ConfigItem.new(
223
+ key: :build_number,
224
+ description: '要选择的精确 Build Number',
225
+ optional: true,
226
+ default_value: nil,
227
+ type: String
228
+ ),
229
+ FastlaneCore::ConfigItem.new(
230
+ key: :wait_for_build_processing,
231
+ description: '选择构建前是否等待 App Store Connect 处理完成',
232
+ optional: true,
233
+ default_value: true,
234
+ type: Boolean
235
+ ),
236
+ FastlaneCore::ConfigItem.new(
237
+ key: :build_processing_timeout,
238
+ description: '等待构建处理完成的超时时间,单位秒',
239
+ optional: true,
240
+ default_value: 3600,
241
+ type: Integer,
242
+ verify_block: proc { |value| UI.user_error!('构建处理超时必须大于 0') unless value.positive? }
243
+ ),
244
+ FastlaneCore::ConfigItem.new(
245
+ key: :build_processing_poll_interval,
246
+ description: '查询构建处理状态的时间间隔,单位秒',
247
+ optional: true,
248
+ default_value: 30,
249
+ type: Integer,
250
+ verify_block: proc { |value| UI.user_error!('构建轮询间隔必须大于 0') unless value.positive? }
251
+ ),
252
+ FastlaneCore::ConfigItem.new(
253
+ key: :build_processing_retry_limit,
254
+ description: '查询构建处理状态遇到网络错误时的最大重试次数',
255
+ optional: true,
256
+ default_value: 3,
257
+ type: Integer,
258
+ verify_block: proc { |value| UI.user_error!('构建查询重试次数不能小于 0') if value.negative? }
259
+ ),
260
+ FastlaneCore::ConfigItem.new(
261
+ key: :build_processing_retry_interval,
262
+ description: '查询构建处理状态网络错误后的重试等待时间,单位秒',
263
+ optional: true,
264
+ default_value: 15,
265
+ type: Integer,
266
+ verify_block: proc { |value| UI.user_error!('构建查询重试间隔不能小于 0') if value.negative? }
267
+ ),
268
+ FastlaneCore::ConfigItem.new(
269
+ key: :submission_information,
270
+ description: 'deliver 提交审核信息,例如出口合规声明',
271
+ optional: true,
272
+ default_value: nil,
273
+ type: Hash
274
+ ),
275
+ FastlaneCore::ConfigItem.new(
276
+ key: :release_notes,
277
+ description: '按语言配置的更新说明;优先于 metadata 中的 release_notes.txt',
278
+ optional: true,
279
+ type: Hash
280
+ ),
281
+ FastlaneCore::ConfigItem.new(
282
+ key: :force,
283
+ description: '是否跳过 HTML 预览确认',
284
+ optional: true,
285
+ default_value: true,
286
+ type: Boolean
287
+ )
288
+ ]
289
+ end
290
+
291
+ def self.is_supported?(platform)
292
+ platform == :ios
293
+ end
294
+
295
+ def self.category
296
+ :production
297
+ end
298
+ end
299
+ end
300
+ end