gowalk-cicd 1.0.166 → 1.0.168

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 (196) hide show
  1. package/README.md +116 -215
  2. package/action/action.yml +59 -362
  3. package/action/scripts/auto_detect.py +39 -60
  4. package/action/scripts/commit_bot_changes.sh +3 -26
  5. package/action/scripts/creds_store.py +10 -91
  6. package/action/scripts/mobile_inputs.cjs +6 -4
  7. package/action/scripts/native_cache.py +5 -1
  8. package/action/scripts/native_pods.py +44 -15
  9. package/action/scripts/native_pods_diagnostics.py +18 -3
  10. package/action/scripts/native_pods_process.py +5 -0
  11. package/action/scripts/prepare_crashlytics_build.py +22 -5
  12. package/action/scripts/prepare_signing.py +4 -6
  13. package/action/scripts/profile_manager.py +74 -165
  14. package/action/scripts/resolve_info_plist.py +7 -2
  15. package/action/scripts/resolve_marketing_version.py +9 -1
  16. package/action/scripts/set_app_store_whats_new.py +3 -4
  17. package/android-action/action.yml +6 -8
  18. package/android-action/flutter-setup/action.yml +46 -7
  19. package/android-action/scripts/android_build_diagnostics.py +53 -27
  20. package/android-action/scripts/crashlytics_symbols.py +4 -0
  21. package/android-action/scripts/gradle_wrapper.py +2 -1
  22. package/android-action/scripts/sign_bundle.py +10 -7
  23. package/backend-action/action.yml +38 -8
  24. package/backend-action/ensure_certificate.sh +23 -67
  25. package/backend-action/network_inventory.py +1 -1
  26. package/backend-action/network_pools.py +17 -25
  27. package/backend-action/remote_deploy.sh +35 -6
  28. package/bin/cli.mjs +3 -19
  29. package/docs/CANDIDATE_ARTIFACTS.md +11 -6
  30. package/docs/CONFIGURATION.md +3 -33
  31. package/docs/GRADLE_CACHE.md +8 -3
  32. package/package.json +17 -8
  33. package/src/config/build-steps.mjs +2 -1
  34. package/src/config/transaction.mjs +8 -4
  35. package/src/config/validation.mjs +10 -1
  36. package/src/config/workflows.mjs +17 -1
  37. package/src/install.mjs +65 -37
  38. package/templates/deploy-backend.yml +6 -1
  39. package/templates/deploy.yml +63 -78
  40. package/templates/history.json.gz +0 -0
  41. package/templates/mobile-candidate.yml +1 -2
  42. package/action/.daemux-version +0 -1
  43. package/action/prompts/generate_dependent_fields.prompt.yml +0 -78
  44. package/action/prompts/generate_descriptions.prompt.yml +0 -62
  45. package/action/scripts/app_context_scanner.py +0 -239
  46. package/action/scripts/asc_metadata_applier.py +0 -432
  47. package/action/scripts/asc_metadata_detector.py +0 -250
  48. package/action/scripts/autoupdate_check.sh +0 -61
  49. package/action/scripts/autoupdate_stage.py +0 -62
  50. package/action/scripts/cert_factory.py +0 -102
  51. package/action/scripts/certificate_failure.py +0 -47
  52. package/action/scripts/fixtures/single-size-ios-icon.json +0 -38
  53. package/action/scripts/metadata_constants.py +0 -102
  54. package/action/scripts/profile_store.py +0 -87
  55. package/action/scripts/source_maintenance_fixture.py +0 -58
  56. package/action/scripts/test_action_step_order.py +0 -83
  57. package/action/scripts/test_app_context_scanner.py +0 -167
  58. package/action/scripts/test_app_group_requirements.py +0 -83
  59. package/action/scripts/test_asc_build_history.py +0 -221
  60. package/action/scripts/test_asc_builds_prerelease.py +0 -219
  61. package/action/scripts/test_asc_common_timeouts.py +0 -121
  62. package/action/scripts/test_asc_metadata_applier.py +0 -990
  63. package/action/scripts/test_asc_metadata_detector.py +0 -445
  64. package/action/scripts/test_asc_network_diagnostics.py +0 -92
  65. package/action/scripts/test_auto_detect.py +0 -492
  66. package/action/scripts/test_autoupdate_stage.py +0 -107
  67. package/action/scripts/test_capabilities.py +0 -261
  68. package/action/scripts/test_certificate_cap_policy.py +0 -72
  69. package/action/scripts/test_certificate_failure.py +0 -64
  70. package/action/scripts/test_certificate_request.py +0 -62
  71. package/action/scripts/test_cfg_resolve_credentials.py +0 -48
  72. package/action/scripts/test_compute_next_version.py +0 -95
  73. package/action/scripts/test_compute_next_version_patch_backcompat.py +0 -41
  74. package/action/scripts/test_compute_next_version_strictness.py +0 -67
  75. package/action/scripts/test_crashlytics_dsyms.py +0 -211
  76. package/action/scripts/test_crashlytics_phase_effects.py +0 -30
  77. package/action/scripts/test_delivery_icons.py +0 -76
  78. package/action/scripts/test_delivery_preflight.py +0 -354
  79. package/action/scripts/test_fetch_versions.py +0 -163
  80. package/action/scripts/test_flutter_native_preparation.py +0 -103
  81. package/action/scripts/test_ground_truth_floor.py +0 -104
  82. package/action/scripts/test_keychain.py +0 -145
  83. package/action/scripts/test_manage_marketing_version.py +0 -134
  84. package/action/scripts/test_manage_marketing_version_autoroll.py +0 -158
  85. package/action/scripts/test_manage_marketing_version_floor.py +0 -170
  86. package/action/scripts/test_manage_marketing_version_match.py +0 -193
  87. package/action/scripts/test_mmv_409_classifier.py +0 -231
  88. package/action/scripts/test_mmv_autobump_persist_gate.py +0 -131
  89. package/action/scripts/test_mmv_decide_create.py +0 -158
  90. package/action/scripts/test_mmv_floor_check.py +0 -158
  91. package/action/scripts/test_mmv_floor_crosscheck.py +0 -132
  92. package/action/scripts/test_mmv_helpers.py +0 -57
  93. package/action/scripts/test_mmv_testflight_only.py +0 -147
  94. package/action/scripts/test_mobile_artifact_restore.py +0 -93
  95. package/action/scripts/test_mobile_artifacts.py +0 -92
  96. package/action/scripts/test_native_bundle.py +0 -97
  97. package/action/scripts/test_native_cache.py +0 -37
  98. package/action/scripts/test_native_dependency_guard.py +0 -116
  99. package/action/scripts/test_native_dependency_wiring.py +0 -70
  100. package/action/scripts/test_native_pods.py +0 -151
  101. package/action/scripts/test_native_pods_diagnostics.py +0 -86
  102. package/action/scripts/test_native_pods_process.py +0 -41
  103. package/action/scripts/test_native_pods_retry.py +0 -182
  104. package/action/scripts/test_native_pods_timeout.py +0 -34
  105. package/action/scripts/test_native_prepare_evidence.py +0 -76
  106. package/action/scripts/test_next_build_number.py +0 -207
  107. package/action/scripts/test_pbxproj_editor.py +0 -153
  108. package/action/scripts/test_prepare_crashlytics.py +0 -281
  109. package/action/scripts/test_profile_app_groups.py +0 -103
  110. package/action/scripts/test_profile_reuse.py +0 -113
  111. package/action/scripts/test_read_config_auto_detect.py +0 -257
  112. package/action/scripts/test_registry_managed_signing.py +0 -116
  113. package/action/scripts/test_resolve_info_plist.py +0 -120
  114. package/action/scripts/test_resolve_marketing_version.py +0 -298
  115. package/action/scripts/test_retained_signing_identity.py +0 -110
  116. package/action/scripts/test_reuse_stale_editable.py +0 -182
  117. package/action/scripts/test_set_app_store_whats_new.py +0 -71
  118. package/action/scripts/test_signing_source_boundary.py +0 -82
  119. package/action/scripts/test_source_maintenance.py +0 -122
  120. package/action/scripts/test_source_preflight.py +0 -226
  121. package/action/scripts/test_team_fallback_wiring.py +0 -157
  122. package/action/scripts/test_team_resolver.py +0 -249
  123. package/action/scripts/test_testflight_version_slot_wiring.py +0 -75
  124. package/action/scripts/test_upload_build.py +0 -267
  125. package/action/scripts/test_upload_checksums.py +0 -74
  126. package/action/scripts/test_write_marketing_version.py +0 -101
  127. package/action/scripts/tests_common.py +0 -167
  128. package/android-action/.daemux-version +0 -1
  129. package/android-action/scripts/bitrise_deploy.py +0 -160
  130. package/android-action/scripts/crashlytics_bootstrap_test_support.py +0 -60
  131. package/android-action/scripts/fixtures/flutter_test_loopback.dart +0 -58
  132. package/android-action/scripts/flutter_manifest_fixture.py +0 -164
  133. package/android-action/scripts/test_android_build_diagnostics.py +0 -145
  134. package/android-action/scripts/test_android_config.py +0 -281
  135. package/android-action/scripts/test_bitrise_deploy.py +0 -124
  136. package/android-action/scripts/test_crashlytics_bootstrap.py +0 -97
  137. package/android-action/scripts/test_crashlytics_prebuild.py +0 -40
  138. package/android-action/scripts/test_crashlytics_symbols.py +0 -212
  139. package/android-action/scripts/test_early_play_workflow.py +0 -82
  140. package/android-action/scripts/test_flutter_archive.py +0 -109
  141. package/android-action/scripts/test_flutter_archive_cache.py +0 -84
  142. package/android-action/scripts/test_flutter_archive_setup.py +0 -67
  143. package/android-action/scripts/test_flutter_archive_size.py +0 -28
  144. package/android-action/scripts/test_flutter_cache_setup.py +0 -108
  145. package/android-action/scripts/test_flutter_download_budget.py +0 -145
  146. package/android-action/scripts/test_flutter_manifest.py +0 -108
  147. package/android-action/scripts/test_flutter_manifest_scope.py +0 -44
  148. package/android-action/scripts/test_flutter_obfuscation.py +0 -115
  149. package/android-action/scripts/test_flutter_origin_retry.py +0 -66
  150. package/android-action/scripts/test_flutter_release_cache.py +0 -94
  151. package/android-action/scripts/test_flutter_release_cache_scope.py +0 -49
  152. package/android-action/scripts/test_flutter_transfer_progress.py +0 -81
  153. package/android-action/scripts/test_gradle_wrapper.py +0 -261
  154. package/android-action/scripts/test_play_connect_retry.py +0 -115
  155. package/android-action/scripts/test_play_inventory.py +0 -98
  156. package/android-action/scripts/test_play_preflight.py +0 -100
  157. package/android-action/scripts/test_play_preflight_transport.py +0 -96
  158. package/android-action/scripts/test_resolve_play_readiness.py +0 -109
  159. package/android-action/scripts/test_select_jdk.py +0 -213
  160. package/android-action/scripts/test_sign_bundle.py +0 -82
  161. package/android-action/scripts/test_version_override.py +0 -153
  162. package/backend-action/.daemux-version +0 -1
  163. package/backend-action/certificate_vhost.py +0 -259
  164. package/backend-action/network_provider_test_fixture.py +0 -30
  165. package/backend-action/network_test_fixture.py +0 -57
  166. package/backend-action/nginx_test_fixture.py +0 -98
  167. package/backend-action/test_certificate_vhost.py +0 -453
  168. package/backend-action/test_edge_health.py +0 -75
  169. package/backend-action/test_edge_inputs.py +0 -79
  170. package/backend-action/test_edge_nginx.py +0 -125
  171. package/backend-action/test_edge_vhost.py +0 -112
  172. package/backend-action/test_identity_artifact.py +0 -77
  173. package/backend-action/test_identity_diagnostics.py +0 -101
  174. package/backend-action/test_identity_failures.py +0 -56
  175. package/backend-action/test_network_builtins.py +0 -28
  176. package/backend-action/test_network_diagnostics.py +0 -77
  177. package/backend-action/test_network_docker.py +0 -129
  178. package/backend-action/test_network_fixture_evidence.py +0 -23
  179. package/backend-action/test_network_inventory.py +0 -241
  180. package/backend-action/test_network_ipam_gateway.py +0 -46
  181. package/backend-action/test_network_noop.py +0 -64
  182. package/backend-action/test_network_owned_inventory.py +0 -128
  183. package/backend-action/test_network_prepare.py +0 -120
  184. package/backend-action/test_network_provider.py +0 -162
  185. package/backend-action/test_network_races.py +0 -73
  186. package/backend-action/test_network_rule_diagnostics.py +0 -69
  187. package/backend-action/test_network_runtime.py +0 -49
  188. package/backend-action/test_network_selected.py +0 -153
  189. package/backend-action/test_runtime_env.py +0 -229
  190. package/scripts/lib/checks.mjs +0 -73
  191. package/scripts/lib/coordination.mjs +0 -39
  192. package/scripts/lib/discovery.mjs +0 -27
  193. package/scripts/lib/github.mjs +0 -85
  194. package/scripts/lib/repository.mjs +0 -82
  195. package/scripts/lib/state.mjs +0 -39
  196. package/scripts/upgrade-org.mjs +0 -81
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # gowalk-cicd
2
2
 
3
- One installer for iOS TestFlight, Android Google Play, and Flutter web release
4
- artifacts. It supports native Swift/SwiftUI iOS projects, native Kotlin/Gradle
3
+ One installer for iOS TestFlight and Android Google Play delivery, plus the
4
+ optional backend deploy. It supports native Swift/SwiftUI iOS projects, native Kotlin/Gradle
5
5
  Android projects, and Flutter apps. Provision encrypted repository secrets, validate a
6
6
  task candidate, and promote its release intent. Store builds and uploads run only in GitHub Actions.
7
7
 
8
8
  App-specific workflow settings live in `.gowalk-cicd.yml`. The installer migrates supported
9
9
  existing settings once and regenerates managed workflows without replacing custom behavior.
10
- See [configuration and parallel organization upgrades](docs/CONFIGURATION.md).
10
+ See [configuration](docs/CONFIGURATION.md).
11
11
 
12
12
  ## Install
13
13
 
@@ -23,12 +23,18 @@ Writes three core things into your repo:
23
23
  - `.github/actions/android-app/` — Android build/sign action (Flutter or Gradle)
24
24
  - `.github/workflows/deploy.yml` — workflow that builds and deploys both platforms
25
25
 
26
- Flutter projects that contain `web/index.html` also get
27
- `.github/workflows/deploy-web.yml`. It creates a version-stamped release build
28
- and retains `build/web` as a 30-day workflow artifact. Native-only repositories
29
- are unaffected.
26
+ The installer no longer adds `.github/workflows/deploy-web.yml`. On re-adoption it
27
+ removes a `deploy-web.yml` that is still byte-for-byte the generated file and has no
28
+ overrides in `.gowalk-cicd.yml`; a customized one keeps upgrading (see
29
+ [Flutter web release](#flutter-web-release)).
30
30
 
31
- Re-run the same command anytime to pull the latest version.
31
+ Re-run the same command anytime to pull the latest version. The vendored action
32
+ directories then mirror the package: a file the package no longer ships is deleted,
33
+ except the `.daemux-version` markers and any file a workflow under
34
+ `.github/workflows/` still names by path (the installer prints what it kept and
35
+ removed; `--dry-run` deletes nothing). Tests and fixtures are not shipped. A
36
+ workflow the package does not manage that fails to parse is skipped with
37
+ `[warn] skipped .github/workflows/<file>: <code>` instead of aborting the install.
32
38
 
33
39
  Ready task PRs build shipping binaries alongside validation; the protected merge promotes matching
34
40
  binaries through the existing store upload paths. See [candidate artifact reuse](docs/CANDIDATE_ARTIFACTS.md)
@@ -92,6 +98,11 @@ job. This bounds individual cache segments, not the total cache or SDK download,
92
98
  and keeps existing cache keys. A successfully installed SDK is saved before app
93
99
  checks run, so a later test/build failure does not force another cold SDK download.
94
100
  Failed setup never saves an SDK cache; pub dependencies retain their job-end save.
101
+ Pull-request runs (`refs/pull/*`, which includes every candidate build) restore these caches
102
+ but never save them; task branches and the default branch still save. When the caller names
103
+ no Flutter version, a committed `.flutter-version` (`x.y.z`, a regular file of at most 64 bytes)
104
+ pins the SDK; a symlink or malformed pin fails with `Invalid project Flutter pin`, and no file
105
+ keeps the channel's latest release.
95
106
 
96
107
  Native dependency preparation uses a committed `Gemfile` and `Gemfile.lock` beside
97
108
  the selected iOS project, falling back to the repository root. It installs missing
@@ -109,9 +120,16 @@ Pods and verifies the selected workspace before release configuration. Preserve
109
120
  order in customized validation and deployment workflows; updating action directories
110
121
  alone cannot repair a configuration step that runs before the action.
111
122
 
123
+ The iOS action then skips `bundle install` and `pod install` when the Pods are already in
124
+ sync: `Pods/Manifest.lock` equals `Podfile.lock`, the lock's `PODFILE CHECKSUM` matches the
125
+ Podfile, every local `:path:` pod exists, a workspace exists and xcodegen did not just
126
+ generate the project. Its receipt then reports `pods: in_sync`, and its own native Pods
127
+ cache is not restored or saved for that run.
128
+
112
129
  ## Flutter web release
113
130
 
114
- The conditional `deploy-web.yml` workflow runs on changes to Flutter source,
131
+ A repository whose `.gowalk-cicd.yml` customizes `deploy-web.yml` keeps it.
132
+ That workflow runs on changes to Flutter source,
115
133
  web assets, package metadata, localization config, or the workflow itself. It
116
134
  also supports manual dispatch. The build uses `GITHUB_RUN_NUMBER` as its build
117
135
  number and uploads the complete `build/web` directory as a release artifact.
@@ -211,13 +229,24 @@ wires an nginx vhost + Let's Encrypt cert for the API domain, and health-checks.
211
229
  Hosts with several existing Let's Encrypt accounts select one deterministically,
212
230
  and a domain deploy fails unless its public HTTPS certificate and health route validate.
213
231
  Changing a container's published port preserves its existing HTTPS configuration; nginx must accept
214
- the change, or the previous vhost is restored and the deploy fails. An existing certificate is
215
- installed for the requested domain on redeploy, repairing older HTTP-only vhosts too.
216
- A redeploy whose vhost already holds that certificate behind Certbot's redirect, whose certificate and
217
- key form a pair, whose running nginx presents it for the domain, and whose name no other server block in
218
- nginx's loaded configuration declares, skips the install, which would otherwise save a checkpoint copy of
219
- every nginx file on the host. It still tests and reloads nginx as the install did, and fails the deploy
220
- when nginx refuses either.
232
+ the change, or the previous vhost is restored and the deploy fails.
233
+ When the domain's vhost has exactly one `ssl_certificate`, and that file exists, stays valid for more
234
+ than a day and lists `DNS:<domain>` in its subjectAltName, the deploy runs neither Certbot nor an nginx
235
+ reload and logs `certificate state=current`. Otherwise an existing certificate is installed for the
236
+ domain (repairing older HTTP-only vhosts too) or a new one is issued, both with `certbot --nginx`.
237
+ Every Certbot call runs under `flock -w 900 /run/gowalk-certbot.lock`, so deploys on one host take
238
+ turns; when the host's renew timer holds Certbot's own lock, the call retries every 10 s for up to
239
+ 10 minutes. Vhost writes with their nginx test and reload, and the host port reservation, run under
240
+ the host nginx lock `/run/app-robot-nginx.lock` (`flock -w 120`), which the panel's domain applier also
241
+ takes. After a healthy deploy the replaced build's images are pruned
242
+ (`docker image prune -f --filter label=com.docker.compose.project=<app>`); a refusal because another
243
+ prune is running defers it to the next deploy.
244
+
245
+ Each app gets one stable loopback port, reserved once in the host's `.env` as
246
+ `BACKEND_HTTP_PORT` (starting at `20000 + cksum(app) % 10000`, skipping ports other apps reserved or
247
+ already bound). A Compose file opts in by publishing `127.0.0.1:${BACKEND_HTTP_PORT:-}:<container-port>`;
248
+ a redeploy then leaves the nginx upstream unchanged. A literal `127.0.0.1:<port>:<container-port>`
249
+ keeps working as before.
221
250
 
222
251
  Requirements on the consumer repo:
223
252
 
@@ -228,6 +257,11 @@ Requirements on the consumer repo:
228
257
  never bake secrets in. A generated `.env` (with `POSTGRES_PASSWORD`) is created
229
258
  on the host on first deploy and preserved across deploys (rsync excludes it).
230
259
  - Repo **secret** `BACKEND_DEPLOY_SSH_KEY` (a private key with access to the host).
260
+ - Repo **variable** `BACKEND_DEPLOY_KNOWN_HOSTS` (recommended): `known_hosts` lines for the exact
261
+ host string the workflow connects to (`BACKEND_DEPLOY_HOST`, for example
262
+ `138.197.36.107 ssh-ed25519 AAAA...`). Deploys then require that key
263
+ (`StrictHostKeyChecking=yes`). While it is empty the job trusts `ssh-keyscan` and prints one
264
+ `backend_host_key_unpinned` warning.
231
265
  - Repo **secret** `BACKEND_RUNTIME_ENV` (optional): newline-delimited `KEY=value`
232
266
  runtime secrets. The action writes it as mode 0600 on the host and passes it
233
267
  to Compose without copying values into the repository or action log.
@@ -238,10 +272,15 @@ Requirements on the consumer repo:
238
272
  Malformed or ambiguous multiline structures refuse before deployment. Transport is bounded
239
273
  to 1 MiB of UTF-8 input and 512 KiB per value, uses private SSH stdin, and atomically replaces
240
274
  `.runtime.env`; application sync preserves the last installed file if validation fails.
241
- - Repo **variables** (optional): `BACKEND_API_DOMAIN` (its DNS A record must point
242
- at the host for the cert; empty = deploy the container only, skip nginx/cert),
243
- `BACKEND_APP_NAME` (defaults to the repo name), `BACKEND_HEALTH_PATH`
244
- (default `/health`), `BACKEND_DEPLOY_HOST` (default `138.197.36.107`).
275
+ - Repo **variable** `BACKEND_API_DOMAIN`: the bound API host name (for example
276
+ `api.<app-domain>`); its DNS A record must point at the host for the cert.
277
+ **Backend Deploy runs only when it is set**: a repository with no bound backend
278
+ origin holds no deploy key, so `deploy-backend.yml` is skipped and the feature-branch
279
+ `backend-preview` deploy ends with a `backend_origin_unbound` notice.
280
+ - Repo **variables** (optional): `BACKEND_APP_NAME` (defaults to the repo name),
281
+ `BACKEND_HEALTH_PATH` (default `/health`), `BACKEND_DEPLOY_HOST` (default
282
+ `138.197.36.107`). The action refuses an `api-domain` that is not a lowercase DNS
283
+ name, a `health-path` outside `/[A-Za-z0-9_./-]*` and a malformed `cert-email`.
245
284
 
246
285
  The action directory is canonical here (like the iOS/Android actions): edit it in
247
286
  `backend-action/`, never in a consumer's vendored copy.
@@ -354,7 +393,17 @@ rather than a failure, so it reads as a flake rather than an out-of-memory.
354
393
 
355
394
  Before building, the Android job caps `-Xmx` at 4 GB and `MaxMetaspaceSize` at
356
395
  2 GB in the checkout, with a `::warning::` naming the original value. Projects
357
- already at or below those numbers are left untouched. Linux only.
396
+ already at or below those numbers are left untouched. A runner with at least
397
+ 16 GiB of `MemTotal` (for example an 8-core `ANDROID_RUNNER`) keeps the app's own
398
+ settings. Linux only.
399
+
400
+ ### Android runner size
401
+
402
+ The Android job runs on `${{ vars.ANDROID_RUNNER || 'ubuntu-24.04' }}`. Set the
403
+ repository or organization variable `ANDROID_RUNNER` to a larger hosted runner
404
+ label to shorten the release build; unset it before deleting that runner. The
405
+ variable is not a build input: setting, changing or removing it never discards a
406
+ candidate binary.
358
407
 
359
408
  ### When Play will not accept a release for review
360
409
 
@@ -428,12 +477,11 @@ the situation and whose **message** is one line of JSON carrying a versioned
428
477
  | `source_maintenance_pending` | `gowalk-cicd/source-maintenance-pending.v1` | `repo`, `base_sha`, `head_sha`, `branch`, `run_id` |
429
478
  | `apple_build_upload_failed` (error) | `gowalk-cicd/apple-build-upload-failed.v2` | `category`, `stage`, `receipt_available`, receipt identifiers (`cfBundleVersion`, `upload_id`, `file_id`, `state`, `provider_status`, `provider_code`, `provider_pointer`), `action` |
430
479
  | `flutter_download_failed` (error) | `gowalk-cicd/flutter-download-failed.v1` | `phase`, `curl_exit`, `http_status`, `proxy_status`, `attempts`, `retained_bytes` |
431
- | `android_build_failed` (error) | `gowalk-cicd/android-build-failed.v1` | `phase`, `exit_code`, `signals` (fixed observed patterns; empty means unclassified) |
480
+ | `android_build_failed` (error) | `gowalk-cicd/android-build-failed.v1` | `phase`, `exit_code`, `signals` (fixed observed patterns; empty means unclassified), optional `hosts`, `artifacts` and `excerpt` (error lines plus the next line, URL userinfo removed, at most 4 KB) |
432
481
 
433
- Android release compilers retain only a bounded private output window. Their failure signals
434
- describe observed error patterns, not retry permission or a unique root cause. Silent builds
435
- emit `android_build_progress` notices (`gowalk-cicd/android-build-progress.v1`) every minute
436
- with `phase` and `elapsed_seconds`; successful compiler output is not republished.
482
+ Android release compilers stream their output to the job log inside `::stop-commands::` markers,
483
+ so no build line can act as a workflow command. Their failure signals describe observed error
484
+ patterns, not retry permission or a unique root cause.
437
485
 
438
486
  Native bundle and Pods failures emit `native_prepare_failed` with schema
439
487
  `gowalk-cicd/native-prepare-failed.v1`, `phase`, a fixed refusal `code` and fixed observed `signals`.
@@ -565,10 +613,11 @@ reliably fit. The failure surfaces far from its cause:
565
613
  zip I/O error: No space left on device
566
614
  ```
567
615
 
568
- The Android job removes the preinstalled toolchains a Flutter build never uses
569
- (.NET, GHC, PowerShell, Swift, Chromium) and prunes Docker
570
- images, reclaiming roughly 25 GB in a few seconds. It prints `df -h /` before
571
- and after. Linux only; skipped in Bitrise mode. npm survives on purpose: the
616
+ The Android job prints `df -h /` and does nothing more when `/` already has
617
+ 40 GB or more free (a larger runner). Otherwise it removes the preinstalled
618
+ toolchains a Flutter build never uses (.NET, GHC, PowerShell, Swift, Chromium)
619
+ and prunes Docker images in the background, reclaiming roughly 25 GB while the
620
+ JDK, Gradle restore and Flutter setup run. Linux only. npm survives on purpose: the
572
621
  [Crashlytics symbol upload](#crashlytics-symbol-delivery-firebase_app_id) runs
573
622
  the Firebase CLI through `npx`.
574
623
 
@@ -621,56 +670,12 @@ Both stores pick up per-locale release notes from committed files:
621
670
  the deploy workflow passes it to the Play upload and the notes ship with
622
671
  every release; when absent, releases upload without notes as before.
623
672
 
624
- ## Android delivery via Bitrise (alternative mode)
625
-
626
- Use this when the Android **upload keystore password lives in Bitrise** (not in
627
- the repo) — the GitHub runner then cannot sign the AAB locally. In this mode the
628
- Android job triggers a Bitrise workflow (which holds the keystore + Play
629
- service-account secrets) over the Bitrise API and waits for it, mirroring the
630
- build result back into the GitHub Actions run. iOS still ships via GitHub
631
- Actions as normal.
632
-
633
- Enable it by committing `creds/bitrise.json`:
634
-
635
- ```json
636
- {
637
- "enabled": true,
638
- "app_slug": "8b2a7a24-....",
639
- "workflow": "android-deploy",
640
- "branch": "main",
641
- "api_token": "bitpat_...."
642
- }
643
- ```
644
-
645
- - `app_slug` — the Bitrise app slug (from the app's Bitrise URL or
646
- `GET https://api.bitrise.io/v0.1/apps`).
647
- - `workflow` — the Bitrise workflow that builds, signs, and runs
648
- `google-play-deploy` (default `android-deploy`).
649
- - `branch` — optional; defaults to the pushed ref.
650
- - `api_token` — a Bitrise personal access token. Committing it to a **private**
651
- repo's `creds/` follows the same trust model as the ASC `.p8`. For non-private
652
- repos, omit it here and set the `BITRISE_API_TOKEN` Actions secret instead.
653
-
654
- When `creds/bitrise.json` is present with `enabled: true`, the local
655
- build/sign/upload steps are skipped and `bitrise_deploy.py` drives the Bitrise
656
- build; otherwise the local path above runs unchanged.
657
-
658
- The Bitrise `android-deploy` workflow must produce a versionCode higher than
659
- what is already on Play. The convention used by this fleet is a `+200` offset
660
- over `$BITRISE_BUILD_NUMBER` via a `change-android-versioncode-and-versionname`
661
- step, with the versionName read from `pubspec.yaml`.
662
-
663
673
  ## Sharing one distribution cert across a team's apps
664
674
 
665
- The iOS action caches the Apple Distribution cert under `creds/` and reuses it
666
- across runs (cert alive + NotAfter > 30 days). But Apple caps a team at **2**
667
- distribution certs, and the cache is per-repo. If **three or more apps share one
668
- Apple Developer team**, they cannot each hold their own cert — every run finds
669
- its cached cert already revoked by a sibling, mints a new one, and revokes
670
- another. The symptom is a "Your Certificate Has Been Revoked" email on nearly
671
- every deploy.
672
-
673
- Fix: apps in the same Apple team should **share one cert**. A fleet registry can
675
+ The iOS action signs with the retained Apple Distribution identity in `creds/`
676
+ (cert alive + NotAfter > 30 days) and never creates or revokes a certificate.
677
+ Apple caps a team at **2** distribution certs, so apps in the same Apple team
678
+ should **share one cert**. A fleet registry can
674
679
  provision the same `creds/cert.p12` + `creds/cert.meta.json` into every repo of
675
680
  that team (an Apple Distribution cert is team-scoped, not app-scoped, so one
676
681
  cert signs them all). Registry provisioning must also write
@@ -683,9 +688,10 @@ The marker is a fail-closed ownership boundary: the action may reuse the
683
688
  identity and create per-app provisioning profiles, but it never creates,
684
689
  replaces, or revokes a registry-managed distribution certificate. Missing,
685
690
  partially written, corrupt, expired, Apple-revoked, or resource-ID/P12-mismatched
686
- managed material aborts with a reconciliation error. Legacy repos without the
687
- marker may create a certificate in an available slot, but never revoke another
688
- identity. A full certificate cap stops for account-registry reconciliation.
691
+ managed material aborts with a reconciliation error. A legacy repo without the
692
+ marker whose retained identity is missing or unusable fails with
693
+ `apple_signing_identity_required` (`gowalk-cicd/apple-signing-identity-required.v1`)
694
+ and keeps its existing signing files.
689
695
 
690
696
  ## How it works
691
697
 
@@ -770,9 +776,7 @@ package produces — the Android App Bundle and the iOS archive — is compiled
770
776
  config key that turns it off. Native Swift and Kotlin/Gradle apps are
771
777
  unaffected: the flag belongs to the Dart compiler and they have no Dart code.
772
778
  Flutter web builds are also untouched — `flutter build web` has no such flag;
773
- dart2js minifies release output instead. An Android app delivered through
774
- [Bitrise](#android-delivery-via-bitrise-alternative-mode) is built by Bitrise's
775
- workflow, not by this action; put the same two flags in that workflow.
779
+ dart2js minifies release output instead.
776
780
 
777
781
  What it does: the Dart AOT compiler replaces class, function and library names
778
782
  with short random identifiers, so the app's Dart symbols cannot be read out of
@@ -877,19 +881,16 @@ preserving each app's custom checks and artifact retention settings. An isolated
877
881
  4. **Computes the next build number** by querying ASC for the latest uploaded
878
882
  build and incrementing.
879
883
  5. **Provisions signing** at runtime: reconciles the App ID's capabilities with
880
- the target's entitlements (see below), then generates a throwaway Apple
881
- Distribution cert + a per-target App Store provisioning profile named
882
- `CI-<bundle_id>`. Patches the `.pbxproj` to use Manual signing against those
883
- profiles.
884
+ the target's entitlements (see below), then signs with the retained Apple
885
+ Distribution identity and the per-target App Store provisioning profile named
886
+ `CI-<bundle_id>` that App Store Connect already holds, creating the profile
887
+ when it is missing or stale. Patches the `.pbxproj` to use Manual signing
888
+ against those profiles.
884
889
  6. **Archives** with `xcodebuild archive`, exports the IPA, and uploads via
885
890
  Apple's build-upload REST API.
886
891
  7. **Sets "What's New"** on every declared localization (reads
887
892
  `fastlane/metadata/ios/<locale>/release_notes.txt` if present, or from the
888
893
  `app-store-whats-new` input).
889
- 8. **Auto-fills empty App Store metadata** (name, subtitle, keywords,
890
- description, promotional text, what's new) via GitHub Models AI, on every
891
- locale that has gaps. See [AI metadata auto-fill](#ai-metadata-auto-fill)
892
- below.
893
894
 
894
895
  The iOS action requires only the p8. Everything else is derived.
895
896
 
@@ -919,17 +920,16 @@ Before creating a profile, the action reads the target's
919
920
  ID — Push Notifications, Associated Domains, HealthKit, SiriKit, HomeKit,
920
921
  network extensions and the other plain on/off toggles.
921
922
 
922
- The cached profile under `creds/profiles/` is checked against those same
923
- entitlements and regenerated when it does not carry them. The cache manifest
924
- records only a UUID and an expiry, so without that check a profile issued
925
- before a capability was turned on — whether by this action or by you in the
926
- developer portal — would be reused indefinitely, and the archive would keep
927
- failing on a capability the App ID already has.
923
+ An existing `CI-<bundle_id>` profile is found by name and reused only when it
924
+ is ACTIVE, issued for the current certificate, more than 30 days from expiry,
925
+ and carries those same entitlements. Otherwise it is deleted and regenerated, so
926
+ a profile issued before a capability was turned on — whether by this action or
927
+ by you in the developer portal — is never reused.
928
928
 
929
929
  App Groups require registration and assignment to each participating App ID through
930
930
  the account's Apple Developer console. The action reads the literal identifiers from
931
- each archive target's entitlements and verifies their exact membership in both cached
932
- and fresh profiles. A stale cache regenerates; a fresh profile missing a required group
931
+ each archive target's entitlements and verifies their exact membership in both reused
932
+ and fresh profiles. A stale profile regenerates; a fresh profile missing a required group
933
933
  fails before installation with `apple_app_groups_required`, schema
934
934
  `gowalk-cicd/apple-app-groups-required.v1`, containing the bundle ID and missing groups.
935
935
  Complete those assignments through the scoped console and rerun CI. This check creates
@@ -948,63 +948,6 @@ Two other classes of capability are **not** enabled automatically, and each gets
948
948
  → &lt;your App ID&gt;**. Until then the archive fails with `Provisioning profile
949
949
  "CI-<bundle>" doesn't include the App Attest capability`.
950
950
 
951
- ## AI metadata auto-fill
952
-
953
- On every run, after the TestFlight upload succeeds, the action:
954
-
955
- 1. Queries App Store Connect for every `appInfoLocalization` and
956
- `appStoreVersionLocalization` on the editable version.
957
- 2. Computes the set of empty fields per locale (URL fields are always
958
- skipped — you must set those manually in ASC).
959
- 3. Scans your repo for context (README, Info.plist, dependency files, top
960
- Swift files) and feeds it to `openai/gpt-4o` via GitHub Models with a
961
- strict JSON schema.
962
- 4. PATCHes only the fields that were empty — never overwrites existing
963
- content.
964
-
965
- Fully idempotent: a second run with no empty fields skips the AI step
966
- entirely (zero requests, zero PATCHes).
967
-
968
- ### Requirement: `permissions: models: read`
969
-
970
- `actions/ai-inference` needs the `models: read` permission. The template
971
- workflow written by `npx gowalk-cicd` already includes it:
972
-
973
- ```yaml
974
- permissions:
975
- contents: read
976
- models: read
977
- ```
978
-
979
- **Existing consumers must add this block to their `deploy.yml`** at the
980
- workflow or job level. If it's missing, the AI step fails open with a
981
- `::warning::` and the rest of the workflow continues unaffected.
982
-
983
- ### Rate limits
984
-
985
- GitHub Models free tier allows **50 `gpt-4o` requests per day** (10 per
986
- minute). One workflow run = one request. If you run many apps from the
987
- same GitHub account, or trigger several builds per day, switch to the
988
- cheaper mini model:
989
-
990
- ```yaml
991
- - uses: ./.github/actions/swift-app
992
- with:
993
- ai-metadata-model: openai/gpt-4o-mini
994
- ```
995
-
996
- `gpt-4o-mini` has a much higher free-tier quota.
997
-
998
- ### Disabling AI metadata
999
-
1000
- Pass `ai-metadata: 'false'` to skip the AI steps entirely:
1001
-
1002
- ```yaml
1003
- - uses: ./.github/actions/swift-app
1004
- with:
1005
- ai-metadata: 'false'
1006
- ```
1007
-
1008
951
  ## Update
1009
952
 
1010
953
  ```bash
@@ -1012,7 +955,8 @@ npx --yes gowalk-cicd
1012
955
  ```
1013
956
 
1014
957
  Overwrites both vendored actions and `.github/workflows/deploy.yml` with the
1015
- latest versions. Because the actions are vendored locally, builds use the
958
+ latest versions, and deletes vendored files the package no longer ships (see
959
+ [Install](#install)). Because the actions are vendored locally, builds use the
1016
960
  reviewed copy in your app repository; updates arrive through this npm package.
1017
961
 
1018
962
  ## Override config (rare)
@@ -1040,22 +984,14 @@ common ones:
1040
984
  | `bundle-id` | Override the auto-detected bundle identifier |
1041
985
  | `team-id` | Override the auto-detected team ID |
1042
986
  | `app-store-apple-id` | Numeric ASC app ID (override auto-lookup) |
1043
- | `certificate-cap-policy` | Only `fail` is accepted (the default); existing identities are always preserved. |
987
+ | `certificate-cap-policy` | Deprecated and ignored: CI never creates or revokes certificates. |
1044
988
  | `run-tests` | `false` to skip the simulator test stage |
1045
989
  | `uses-non-exempt-encryption` | Value for `ITSAppUsesNonExemptEncryption` |
1046
990
  | `archive` | `false` to build-only (PR runs without secrets) |
1047
991
  | `upload` | `false` to archive but not upload to TestFlight |
1048
992
  | `manage-app-store-version` | `false` to upload without creating/editing the App Store release slot |
1049
993
  | `app-store-whats-new` | Inline "What's New" text (overrides files) |
1050
- | `ai-metadata` | `false` to disable AI auto-fill of empty ASC metadata |
1051
- | `ai-metadata-model` | GitHub Models model id (default `openai/gpt-4o`) |
1052
-
1053
- Certificate creation sends the complete PEM CSR in Apple's `csrContent` field, including its framing and newlines.
1054
- Certificate creation refusals emit `apple_certificate_rejected` with schema
1055
- `gowalk-cicd/apple-certificate-rejected.v1`, `operation`, `http_status`, `reason` and `error_codes`.
1056
- HTTP 409 is classified as `certificate_limit` only when Apple's returned detail identifies the limit;
1057
- otherwise it remains `unclassified_conflict`. Codes come from a fixed allowlist. Raw provider text and
1058
- CSR data are excluded. Neither outcome permits automatic certificate deletion or another creation attempt.
994
+ | `ai-metadata`, `ai-metadata-model` | Deprecated and ignored: the action no longer writes App Store metadata with AI. |
1059
995
 
1060
996
  ## First-time app setup
1061
997
 
@@ -1067,10 +1003,9 @@ inside GitHub Actions.
1067
1003
  Once the app exists, all subsequent builds and uploads are fully automated via
1068
1004
  the ASC API key.
1069
1005
 
1070
- Automatic certificate revocation is disabled for every account. The action may add a certificate when Apple has a free
1071
- slot, but a full-cap response aborts without listing or revoking existing identities. If
1072
- signing preparation fails after creating a certificate, the default-branch workflow first
1073
- commits any completed cache files, then re-raises the failure so the private key is not lost.
1006
+ CI never creates, lists for deletion or revokes an Apple Distribution certificate; it
1007
+ signs only with the retained identity (see
1008
+ [Sharing one distribution cert](#sharing-one-distribution-cert-across-a-teams-apps)).
1074
1009
 
1075
1010
  ## Troubleshooting
1076
1011
 
@@ -1162,29 +1097,13 @@ generated pbxproj). Either move `MARKETING_VERSION` under
1162
1097
  `settings.base` in `project.yml`, or pin
1163
1098
  `marketing-version-auto-bump: 'none'` and bump manually.
1164
1099
 
1165
- ## Auto-updates
1166
-
1167
- The vendored action ships with a per-run autoupdate check. On every
1168
- push to your default branch, the action queries npm for the latest
1169
- `gowalk-cicd`, compares against the local marker at
1170
- `.github/actions/swift-app/.daemux-version`, and if newer, re-vendors
1171
- the package via `npx --yes` in a temporary detached worktree and preserves its action files
1172
- on a verified task branch (under both `.github/actions/swift-app/` and
1173
- `.github/actions/android-app/`).
1174
- `.github/workflows/deploy.yml` is NEVER auto-committed — see
1175
- "deploy.yml is not auto-updated" below.
1176
-
1177
- | Aspect | Behaviour |
1178
- |--------|-----------|
1179
- | Trigger | Push to default branch only (PR / branch runs do nothing) |
1180
- | Landing | The app session adopts the exact receipt on its current task branch and uses the protected PR |
1181
- | Isolation | CI leaves HEAD and worktree bytes intact; no default-branch push or rebase |
1182
- | Candidates | Action updates and later version changes have separate verified receipts |
1183
- | Failure mode | npm discovery remains optional; unconfirmed source preservation fails the job |
1184
-
1185
- The shipped `deploy.yml` watches only the release intent. Adopting an action-directory
1186
- update on its own therefore does not launch another mobile upload. Review and adopt the template
1187
- as well as the action directories to enable this behavior in an existing consumer.
1100
+ ## Updates
1101
+
1102
+ CI never updates the vendored actions: the `auto-update` input is deprecated and
1103
+ ignored. A repository adopts a new release only when its task runs
1104
+ `npx --yes gowalk-cicd` and lands the result through the protected PR. The
1105
+ `.daemux-version` markers the installer writes name the release the vendored
1106
+ actions came from.
1188
1107
 
1189
1108
  ### deploy.yml is not auto-updated
1190
1109
 
@@ -1194,9 +1113,9 @@ of `contents: write` — this is a built-in safeguard against CI
1194
1113
  self-modification. When a new version of `gowalk-cicd`
1195
1114
  requires `deploy.yml` schema changes (e.g., new permissions, new
1196
1115
  paths-ignore entries), the action's release notes will call this out
1197
- and the app session runs the pinned `npx --yes gowalk-cicd` adoption in its task
1116
+ and the app session runs the `npx --yes gowalk-cicd` adoption in its task
1198
1117
  workspace, preserving app-specific workflow inputs and landing the full change through
1199
- its checked PR. The CI-side update does not commit the workflow.
1118
+ its checked PR.
1200
1119
 
1201
1120
  Changes that need that manual run:
1202
1121
 
@@ -1207,24 +1126,6 @@ Changes that need that manual run:
1207
1126
  passed to both actions. Until it is, setting the variable does nothing.
1208
1127
  See [Crashlytics symbol upload](#crashlytics-symbol-delivery-firebase_app_id).
1209
1128
 
1210
- ### Opt out
1211
-
1212
- Pin the vendored copy by passing `auto-update: 'false'` to the action:
1213
-
1214
- ```yaml
1215
- - uses: ./.github/actions/swift-app
1216
- with:
1217
- auto-update: 'false'
1218
- ```
1219
-
1220
- ### First-run bootstrap
1221
-
1222
- The marker is written by `npx gowalk-cicd` itself. A repo
1223
- without a marker (e.g. an old hand-vendored copy) will be treated as
1224
- out-of-date on its first run, after which updates land incrementally.
1225
- Run `npx --yes gowalk-cicd` once locally if you want to skip
1226
- even that first auto-bootstrap.
1227
-
1228
1129
  ## License
1229
1130
 
1230
1131
  MIT