@invarn/cibuild 2.9.6 → 2.9.8

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 (559) hide show
  1. package/dist/chunk-35SI42CD.mjs +1 -0
  2. package/dist/chunk-IC45KVLJ.mjs +1 -0
  3. package/dist/chunk-XDY4V23V.mjs +1 -0
  4. package/dist/cli.cjs +14 -14
  5. package/dist/commands.mjs +1 -0
  6. package/dist/lib.mjs +3934 -0
  7. package/package.json +6 -10
  8. package/dist/src/cli.d.ts +0 -3
  9. package/dist/src/cli.d.ts.map +0 -1
  10. package/dist/src/cli.js +0 -399
  11. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts +0 -38
  12. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts.map +0 -1
  13. package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.js +0 -222
  14. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts +0 -41
  15. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts.map +0 -1
  16. package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.js +0 -324
  17. package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts +0 -2
  18. package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts.map +0 -1
  19. package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.js +0 -382
  20. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts +0 -29
  21. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts.map +0 -1
  22. package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.js +0 -160
  23. package/dist/src/commands/android-gms-guard.test.d.ts +0 -9
  24. package/dist/src/commands/android-gms-guard.test.d.ts.map +0 -1
  25. package/dist/src/commands/android-gms-guard.test.js +0 -78
  26. package/dist/src/commands/android-java-version.test.d.ts +0 -12
  27. package/dist/src/commands/android-java-version.test.d.ts.map +0 -1
  28. package/dist/src/commands/android-java-version.test.js +0 -209
  29. package/dist/src/commands/android-scanner-java.test.d.ts +0 -2
  30. package/dist/src/commands/android-scanner-java.test.d.ts.map +0 -1
  31. package/dist/src/commands/android-scanner-java.test.js +0 -130
  32. package/dist/src/commands/android-scanner.d.ts +0 -250
  33. package/dist/src/commands/android-scanner.d.ts.map +0 -1
  34. package/dist/src/commands/android-scanner.js +0 -1639
  35. package/dist/src/commands/build.d.ts +0 -215
  36. package/dist/src/commands/build.d.ts.map +0 -1
  37. package/dist/src/commands/build.js +0 -1433
  38. package/dist/src/commands/build.test.d.ts +0 -13
  39. package/dist/src/commands/build.test.d.ts.map +0 -1
  40. package/dist/src/commands/build.test.js +0 -121
  41. package/dist/src/commands/detect-platform.d.ts +0 -13
  42. package/dist/src/commands/detect-platform.d.ts.map +0 -1
  43. package/dist/src/commands/detect-platform.js +0 -51
  44. package/dist/src/commands/detect-project.d.ts +0 -7
  45. package/dist/src/commands/detect-project.d.ts.map +0 -1
  46. package/dist/src/commands/detect-project.js +0 -12
  47. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts +0 -26
  48. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts.map +0 -1
  49. package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.js +0 -196
  50. package/dist/src/commands/edit.d.ts +0 -12
  51. package/dist/src/commands/edit.d.ts.map +0 -1
  52. package/dist/src/commands/edit.js +0 -652
  53. package/dist/src/commands/edit.test.d.ts +0 -6
  54. package/dist/src/commands/edit.test.d.ts.map +0 -1
  55. package/dist/src/commands/edit.test.js +0 -45
  56. package/dist/src/commands/fidelity-trim.d.ts +0 -13
  57. package/dist/src/commands/fidelity-trim.d.ts.map +0 -1
  58. package/dist/src/commands/fidelity-trim.js +0 -21
  59. package/dist/src/commands/fidelity-trim.test.d.ts +0 -8
  60. package/dist/src/commands/fidelity-trim.test.d.ts.map +0 -1
  61. package/dist/src/commands/fidelity-trim.test.js +0 -79
  62. package/dist/src/commands/file-secret-collector.d.ts +0 -50
  63. package/dist/src/commands/file-secret-collector.d.ts.map +0 -1
  64. package/dist/src/commands/file-secret-collector.js +0 -217
  65. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts +0 -38
  66. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts.map +0 -1
  67. package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.js +0 -300
  68. package/dist/src/commands/github-workflow.d.ts +0 -5
  69. package/dist/src/commands/github-workflow.d.ts.map +0 -1
  70. package/dist/src/commands/github-workflow.js +0 -45
  71. package/dist/src/commands/index.d.ts +0 -33
  72. package/dist/src/commands/index.d.ts.map +0 -1
  73. package/dist/src/commands/index.js +0 -33
  74. package/dist/src/commands/init.d.ts +0 -20
  75. package/dist/src/commands/init.d.ts.map +0 -1
  76. package/dist/src/commands/init.js +0 -264
  77. package/dist/src/commands/ios-scanner.d.ts +0 -104
  78. package/dist/src/commands/ios-scanner.d.ts.map +0 -1
  79. package/dist/src/commands/ios-scanner.js +0 -492
  80. package/dist/src/commands/ios-scheme-ranking.test.d.ts +0 -2
  81. package/dist/src/commands/ios-scheme-ranking.test.d.ts.map +0 -1
  82. package/dist/src/commands/ios-scheme-ranking.test.js +0 -264
  83. package/dist/src/commands/local-properties-reaches-every-workflow.test.d.ts +0 -30
  84. package/dist/src/commands/local-properties-reaches-every-workflow.test.d.ts.map +0 -1
  85. package/dist/src/commands/local-properties-reaches-every-workflow.test.js +0 -146
  86. package/dist/src/commands/reset.d.ts +0 -7
  87. package/dist/src/commands/reset.d.ts.map +0 -1
  88. package/dist/src/commands/reset.js +0 -81
  89. package/dist/src/commands/run.d.ts +0 -22
  90. package/dist/src/commands/run.d.ts.map +0 -1
  91. package/dist/src/commands/run.js +0 -141
  92. package/dist/src/commands/secrets-sync-workflow.d.ts +0 -15
  93. package/dist/src/commands/secrets-sync-workflow.d.ts.map +0 -1
  94. package/dist/src/commands/secrets-sync-workflow.js +0 -255
  95. package/dist/src/commands/secrets-upload.d.ts +0 -21
  96. package/dist/src/commands/secrets-upload.d.ts.map +0 -1
  97. package/dist/src/commands/secrets-upload.js +0 -177
  98. package/dist/src/commands/secrets-upload.test.d.ts +0 -5
  99. package/dist/src/commands/secrets-upload.test.d.ts.map +0 -1
  100. package/dist/src/commands/secrets-upload.test.js +0 -60
  101. package/dist/src/commands/validate.d.ts +0 -10
  102. package/dist/src/commands/validate.d.ts.map +0 -1
  103. package/dist/src/commands/validate.js +0 -46
  104. package/dist/src/config.d.ts +0 -3
  105. package/dist/src/config.d.ts.map +0 -1
  106. package/dist/src/config.js +0 -47
  107. package/dist/src/envman/cli.d.ts +0 -21
  108. package/dist/src/envman/cli.d.ts.map +0 -1
  109. package/dist/src/envman/cli.js +0 -240
  110. package/dist/src/envman/envman.d.ts +0 -83
  111. package/dist/src/envman/envman.d.ts.map +0 -1
  112. package/dist/src/envman/envman.js +0 -361
  113. package/dist/src/envman/envman.test.d.ts +0 -5
  114. package/dist/src/envman/envman.test.d.ts.map +0 -1
  115. package/dist/src/envman/envman.test.js +0 -236
  116. package/dist/src/envman/envstore-round-trip.test.d.ts +0 -27
  117. package/dist/src/envman/envstore-round-trip.test.d.ts.map +0 -1
  118. package/dist/src/envman/envstore-round-trip.test.js +0 -130
  119. package/dist/src/envman/index.d.ts +0 -23
  120. package/dist/src/envman/index.d.ts.map +0 -1
  121. package/dist/src/envman/index.js +0 -23
  122. package/dist/src/envman/shim.d.ts +0 -16
  123. package/dist/src/envman/shim.d.ts.map +0 -1
  124. package/dist/src/envman/shim.js +0 -66
  125. package/dist/src/envman/types.d.ts +0 -55
  126. package/dist/src/envman/types.d.ts.map +0 -1
  127. package/dist/src/envman/types.js +0 -12
  128. package/dist/src/lib.d.ts +0 -33
  129. package/dist/src/lib.d.ts.map +0 -1
  130. package/dist/src/lib.js +0 -37
  131. package/dist/src/pipeline.d.ts +0 -3
  132. package/dist/src/pipeline.d.ts.map +0 -1
  133. package/dist/src/pipeline.js +0 -57
  134. package/dist/src/runner-node-step.test.d.ts +0 -12
  135. package/dist/src/runner-node-step.test.d.ts.map +0 -1
  136. package/dist/src/runner-node-step.test.js +0 -61
  137. package/dist/src/runner.d.ts +0 -31
  138. package/dist/src/runner.d.ts.map +0 -1
  139. package/dist/src/runner.js +0 -240
  140. package/dist/src/shared/detect-project.d.ts +0 -57
  141. package/dist/src/shared/detect-project.d.ts.map +0 -1
  142. package/dist/src/shared/detect-project.js +0 -219
  143. package/dist/src/shared/detect-project.test.d.ts +0 -2
  144. package/dist/src/shared/detect-project.test.d.ts.map +0 -1
  145. package/dist/src/shared/detect-project.test.js +0 -161
  146. package/dist/src/shared/git.d.ts +0 -25
  147. package/dist/src/shared/git.d.ts.map +0 -1
  148. package/dist/src/shared/git.js +0 -53
  149. package/dist/src/shared/git.test.d.ts +0 -9
  150. package/dist/src/shared/git.test.d.ts.map +0 -1
  151. package/dist/src/shared/git.test.js +0 -66
  152. package/dist/src/shared/gitignore.d.ts +0 -6
  153. package/dist/src/shared/gitignore.d.ts.map +0 -1
  154. package/dist/src/shared/gitignore.js +0 -26
  155. package/dist/src/shared/gradle-settings.d.ts +0 -49
  156. package/dist/src/shared/gradle-settings.d.ts.map +0 -1
  157. package/dist/src/shared/gradle-settings.js +0 -138
  158. package/dist/src/shared/gradle-settings.test.d.ts +0 -2
  159. package/dist/src/shared/gradle-settings.test.d.ts.map +0 -1
  160. package/dist/src/shared/gradle-settings.test.js +0 -65
  161. package/dist/src/shared/prompts.d.ts +0 -30
  162. package/dist/src/shared/prompts.d.ts.map +0 -1
  163. package/dist/src/shared/prompts.js +0 -121
  164. package/dist/src/shared/xcode-container.test.d.ts +0 -2
  165. package/dist/src/shared/xcode-container.test.d.ts.map +0 -1
  166. package/dist/src/shared/xcode-container.test.js +0 -123
  167. package/dist/src/step-marker.d.ts +0 -12
  168. package/dist/src/step-marker.d.ts.map +0 -1
  169. package/dist/src/step-marker.js +0 -29
  170. package/dist/src/step-marker.test.d.ts +0 -11
  171. package/dist/src/step-marker.test.d.ts.map +0 -1
  172. package/dist/src/step-marker.test.js +0 -45
  173. package/dist/src/types.d.ts +0 -58
  174. package/dist/src/types.d.ts.map +0 -1
  175. package/dist/src/types.js +0 -2
  176. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts +0 -28
  177. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts.map +0 -1
  178. package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.js +0 -223
  179. package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.d.ts +0 -20
  180. package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.d.ts.map +0 -1
  181. package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.js +0 -201
  182. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts +0 -37
  183. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts.map +0 -1
  184. package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.js +0 -255
  185. package/dist/src/yaml/a-missing-credential-is-loud.test.d.ts +0 -32
  186. package/dist/src/yaml/a-missing-credential-is-loud.test.d.ts.map +0 -1
  187. package/dist/src/yaml/a-missing-credential-is-loud.test.js +0 -301
  188. package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts +0 -18
  189. package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts.map +0 -1
  190. package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.js +0 -66
  191. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts +0 -34
  192. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts.map +0 -1
  193. package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.js +0 -218
  194. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts +0 -27
  195. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts.map +0 -1
  196. package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.js +0 -126
  197. package/dist/src/yaml/bitrise-compat.d.ts +0 -65
  198. package/dist/src/yaml/bitrise-compat.d.ts.map +0 -1
  199. package/dist/src/yaml/bitrise-compat.js +0 -206
  200. package/dist/src/yaml/bitrise-compat.test.d.ts +0 -5
  201. package/dist/src/yaml/bitrise-compat.test.d.ts.map +0 -1
  202. package/dist/src/yaml/bitrise-compat.test.js +0 -347
  203. package/dist/src/yaml/cache-key-missing-file.test.d.ts +0 -12
  204. package/dist/src/yaml/cache-key-missing-file.test.d.ts.map +0 -1
  205. package/dist/src/yaml/cache-key-missing-file.test.js +0 -116
  206. package/dist/src/yaml/converter.d.ts +0 -103
  207. package/dist/src/yaml/converter.d.ts.map +0 -1
  208. package/dist/src/yaml/converter.js +0 -420
  209. package/dist/src/yaml/converter.test.d.ts +0 -5
  210. package/dist/src/yaml/converter.test.d.ts.map +0 -1
  211. package/dist/src/yaml/converter.test.js +0 -548
  212. package/dist/src/yaml/e2e.test.d.ts +0 -6
  213. package/dist/src/yaml/e2e.test.d.ts.map +0 -1
  214. package/dist/src/yaml/e2e.test.js +0 -446
  215. package/dist/src/yaml/env-resolver.d.ts +0 -218
  216. package/dist/src/yaml/env-resolver.d.ts.map +0 -1
  217. package/dist/src/yaml/env-resolver.js +0 -591
  218. package/dist/src/yaml/env-resolver.test.d.ts +0 -5
  219. package/dist/src/yaml/env-resolver.test.d.ts.map +0 -1
  220. package/dist/src/yaml/env-resolver.test.js +0 -502
  221. package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts +0 -16
  222. package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts.map +0 -1
  223. package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.js +0 -126
  224. package/dist/src/yaml/fidelity-input-digest.d.ts +0 -37
  225. package/dist/src/yaml/fidelity-input-digest.d.ts.map +0 -1
  226. package/dist/src/yaml/fidelity-input-digest.js +0 -208
  227. package/dist/src/yaml/fidelity-input-digest.test.d.ts +0 -2
  228. package/dist/src/yaml/fidelity-input-digest.test.d.ts.map +0 -1
  229. package/dist/src/yaml/fidelity-input-digest.test.js +0 -167
  230. package/dist/src/yaml/fidelity-manifest.d.ts +0 -126
  231. package/dist/src/yaml/fidelity-manifest.d.ts.map +0 -1
  232. package/dist/src/yaml/fidelity-manifest.js +0 -134
  233. package/dist/src/yaml/fidelity-manifest.test.d.ts +0 -12
  234. package/dist/src/yaml/fidelity-manifest.test.d.ts.map +0 -1
  235. package/dist/src/yaml/fidelity-manifest.test.js +0 -251
  236. package/dist/src/yaml/interactive-prompts.d.ts +0 -71
  237. package/dist/src/yaml/interactive-prompts.d.ts.map +0 -1
  238. package/dist/src/yaml/interactive-prompts.js +0 -260
  239. package/dist/src/yaml/meta-helpers.d.ts +0 -28
  240. package/dist/src/yaml/meta-helpers.d.ts.map +0 -1
  241. package/dist/src/yaml/meta-helpers.js +0 -46
  242. package/dist/src/yaml/missing-env-handler.d.ts +0 -45
  243. package/dist/src/yaml/missing-env-handler.d.ts.map +0 -1
  244. package/dist/src/yaml/missing-env-handler.js +0 -64
  245. package/dist/src/yaml/parser.d.ts +0 -33
  246. package/dist/src/yaml/parser.d.ts.map +0 -1
  247. package/dist/src/yaml/parser.js +0 -148
  248. package/dist/src/yaml/parser.test.d.ts +0 -5
  249. package/dist/src/yaml/parser.test.d.ts.map +0 -1
  250. package/dist/src/yaml/parser.test.js +0 -83
  251. package/dist/src/yaml/pipeline-with-secrets.d.ts +0 -25
  252. package/dist/src/yaml/pipeline-with-secrets.d.ts.map +0 -1
  253. package/dist/src/yaml/pipeline-with-secrets.js +0 -76
  254. package/dist/src/yaml/platform-detector.d.ts +0 -83
  255. package/dist/src/yaml/platform-detector.d.ts.map +0 -1
  256. package/dist/src/yaml/platform-detector.js +0 -189
  257. package/dist/src/yaml/platform-detector.test.d.ts +0 -5
  258. package/dist/src/yaml/platform-detector.test.d.ts.map +0 -1
  259. package/dist/src/yaml/platform-detector.test.js +0 -525
  260. package/dist/src/yaml/preflight-validation.d.ts +0 -29
  261. package/dist/src/yaml/preflight-validation.d.ts.map +0 -1
  262. package/dist/src/yaml/preflight-validation.js +0 -21
  263. package/dist/src/yaml/secrets-manager.d.ts +0 -87
  264. package/dist/src/yaml/secrets-manager.d.ts.map +0 -1
  265. package/dist/src/yaml/secrets-manager.js +0 -230
  266. package/dist/src/yaml/shell-assignments.d.ts +0 -40
  267. package/dist/src/yaml/shell-assignments.d.ts.map +0 -1
  268. package/dist/src/yaml/shell-assignments.js +0 -77
  269. package/dist/src/yaml/step-validator-check-failure.test.d.ts +0 -24
  270. package/dist/src/yaml/step-validator-check-failure.test.d.ts.map +0 -1
  271. package/dist/src/yaml/step-validator-check-failure.test.js +0 -130
  272. package/dist/src/yaml/step-validator-self-assigned.test.d.ts +0 -23
  273. package/dist/src/yaml/step-validator-self-assigned.test.d.ts.map +0 -1
  274. package/dist/src/yaml/step-validator-self-assigned.test.js +0 -191
  275. package/dist/src/yaml/step-validator.d.ts +0 -67
  276. package/dist/src/yaml/step-validator.d.ts.map +0 -1
  277. package/dist/src/yaml/step-validator.js +0 -466
  278. package/dist/src/yaml/step-validator.test.d.ts +0 -18
  279. package/dist/src/yaml/step-validator.test.d.ts.map +0 -1
  280. package/dist/src/yaml/step-validator.test.js +0 -140
  281. package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.d.ts +0 -2
  282. package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.d.ts.map +0 -1
  283. package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.js +0 -96
  284. package/dist/src/yaml/steps/android-sign.d.ts +0 -35
  285. package/dist/src/yaml/steps/android-sign.d.ts.map +0 -1
  286. package/dist/src/yaml/steps/android-sign.js +0 -147
  287. package/dist/src/yaml/steps/android-variant-resolution.test.d.ts +0 -2
  288. package/dist/src/yaml/steps/android-variant-resolution.test.d.ts.map +0 -1
  289. package/dist/src/yaml/steps/android-variant-resolution.test.js +0 -260
  290. package/dist/src/yaml/steps/android-version.d.ts +0 -26
  291. package/dist/src/yaml/steps/android-version.d.ts.map +0 -1
  292. package/dist/src/yaml/steps/android-version.js +0 -128
  293. package/dist/src/yaml/steps/android-version.test.d.ts +0 -5
  294. package/dist/src/yaml/steps/android-version.test.d.ts.map +0 -1
  295. package/dist/src/yaml/steps/android-version.test.js +0 -196
  296. package/dist/src/yaml/steps/android.d.ts +0 -156
  297. package/dist/src/yaml/steps/android.d.ts.map +0 -1
  298. package/dist/src/yaml/steps/android.js +0 -1100
  299. package/dist/src/yaml/steps/app-store-deploy.d.ts +0 -48
  300. package/dist/src/yaml/steps/app-store-deploy.d.ts.map +0 -1
  301. package/dist/src/yaml/steps/app-store-deploy.js +0 -167
  302. package/dist/src/yaml/steps/base.d.ts +0 -263
  303. package/dist/src/yaml/steps/base.d.ts.map +0 -1
  304. package/dist/src/yaml/steps/base.js +0 -380
  305. package/dist/src/yaml/steps/bitrise-android-tools.d.ts +0 -26
  306. package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +0 -1
  307. package/dist/src/yaml/steps/bitrise-android-tools.js +0 -200
  308. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +0 -5
  309. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +0 -1
  310. package/dist/src/yaml/steps/bitrise-android-tools.test.js +0 -280
  311. package/dist/src/yaml/steps/bitrise-apk-info.d.ts +0 -22
  312. package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +0 -1
  313. package/dist/src/yaml/steps/bitrise-apk-info.js +0 -144
  314. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +0 -5
  315. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +0 -1
  316. package/dist/src/yaml/steps/bitrise-apk-info.test.js +0 -331
  317. package/dist/src/yaml/steps/bitrise-slack.d.ts +0 -49
  318. package/dist/src/yaml/steps/bitrise-slack.d.ts.map +0 -1
  319. package/dist/src/yaml/steps/bitrise-slack.js +0 -280
  320. package/dist/src/yaml/steps/bitrise-slack.test.d.ts +0 -5
  321. package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +0 -1
  322. package/dist/src/yaml/steps/bitrise-slack.test.js +0 -484
  323. package/dist/src/yaml/steps/bitrise-ssh.d.ts +0 -27
  324. package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +0 -1
  325. package/dist/src/yaml/steps/bitrise-ssh.js +0 -134
  326. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +0 -5
  327. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +0 -1
  328. package/dist/src/yaml/steps/bitrise-ssh.test.js +0 -205
  329. package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts +0 -27
  330. package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts.map +0 -1
  331. package/dist/src/yaml/steps/cache-pull-daemon.test.js +0 -212
  332. package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts +0 -42
  333. package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts.map +0 -1
  334. package/dist/src/yaml/steps/cache-pull-restore-roots.test.js +0 -209
  335. package/dist/src/yaml/steps/cache-push-daemon.test.d.ts +0 -27
  336. package/dist/src/yaml/steps/cache-push-daemon.test.d.ts.map +0 -1
  337. package/dist/src/yaml/steps/cache-push-daemon.test.js +0 -171
  338. package/dist/src/yaml/steps/cache.d.ts +0 -59
  339. package/dist/src/yaml/steps/cache.d.ts.map +0 -1
  340. package/dist/src/yaml/steps/cache.js +0 -1027
  341. package/dist/src/yaml/steps/capture-injection.d.ts +0 -30
  342. package/dist/src/yaml/steps/capture-injection.d.ts.map +0 -1
  343. package/dist/src/yaml/steps/capture-injection.js +0 -88
  344. package/dist/src/yaml/steps/capture-injection.test.d.ts +0 -2
  345. package/dist/src/yaml/steps/capture-injection.test.d.ts.map +0 -1
  346. package/dist/src/yaml/steps/capture-injection.test.js +0 -163
  347. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts +0 -42
  348. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts.map +0 -1
  349. package/dist/src/yaml/steps/cocoapods-deployment-floor.js +0 -140
  350. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts +0 -11
  351. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts.map +0 -1
  352. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.js +0 -176
  353. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +0 -22
  354. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +0 -1
  355. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +0 -97
  356. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts +0 -17
  357. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts.map +0 -1
  358. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.js +0 -282
  359. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts +0 -17
  360. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts.map +0 -1
  361. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.js +0 -132
  362. package/dist/src/yaml/steps/envstore-loader.test.d.ts +0 -15
  363. package/dist/src/yaml/steps/envstore-loader.test.d.ts.map +0 -1
  364. package/dist/src/yaml/steps/envstore-loader.test.js +0 -143
  365. package/dist/src/yaml/steps/fastlane.d.ts +0 -27
  366. package/dist/src/yaml/steps/fastlane.d.ts.map +0 -1
  367. package/dist/src/yaml/steps/fastlane.js +0 -79
  368. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts +0 -53
  369. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts.map +0 -1
  370. package/dist/src/yaml/steps/fidelity-aspect-guard.js +0 -72
  371. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts +0 -11
  372. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts.map +0 -1
  373. package/dist/src/yaml/steps/fidelity-aspect-guard.test.js +0 -50
  374. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts +0 -87
  375. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts.map +0 -1
  376. package/dist/src/yaml/steps/fidelity-reference-provenance.js +0 -111
  377. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts +0 -22
  378. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts.map +0 -1
  379. package/dist/src/yaml/steps/fidelity-reference-provenance.test.js +0 -118
  380. package/dist/src/yaml/steps/file.d.ts +0 -52
  381. package/dist/src/yaml/steps/file.d.ts.map +0 -1
  382. package/dist/src/yaml/steps/file.js +0 -108
  383. package/dist/src/yaml/steps/flutter.d.ts +0 -63
  384. package/dist/src/yaml/steps/flutter.d.ts.map +0 -1
  385. package/dist/src/yaml/steps/flutter.js +0 -215
  386. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts +0 -16
  387. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts.map +0 -1
  388. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.js +0 -127
  389. package/dist/src/yaml/steps/git-clone.d.ts +0 -32
  390. package/dist/src/yaml/steps/git-clone.d.ts.map +0 -1
  391. package/dist/src/yaml/steps/git-clone.js +0 -171
  392. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts +0 -10
  393. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts.map +0 -1
  394. package/dist/src/yaml/steps/git-fetch-retry-exec.test.js +0 -162
  395. package/dist/src/yaml/steps/git-fetch-retry.d.ts +0 -62
  396. package/dist/src/yaml/steps/git-fetch-retry.d.ts.map +0 -1
  397. package/dist/src/yaml/steps/git-fetch-retry.js +0 -155
  398. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts +0 -5
  399. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts.map +0 -1
  400. package/dist/src/yaml/steps/git-fetch-retry.test.js +0 -86
  401. package/dist/src/yaml/steps/google-play-deploy.d.ts +0 -37
  402. package/dist/src/yaml/steps/google-play-deploy.d.ts.map +0 -1
  403. package/dist/src/yaml/steps/google-play-deploy.js +0 -193
  404. package/dist/src/yaml/steps/google-play-deploy.test.d.ts +0 -5
  405. package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +0 -1
  406. package/dist/src/yaml/steps/google-play-deploy.test.js +0 -310
  407. package/dist/src/yaml/steps/gradle-memory.d.ts +0 -91
  408. package/dist/src/yaml/steps/gradle-memory.d.ts.map +0 -1
  409. package/dist/src/yaml/steps/gradle-memory.js +0 -495
  410. package/dist/src/yaml/steps/gradle-memory.test.d.ts +0 -2
  411. package/dist/src/yaml/steps/gradle-memory.test.d.ts.map +0 -1
  412. package/dist/src/yaml/steps/gradle-memory.test.js +0 -344
  413. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts +0 -28
  414. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts.map +0 -1
  415. package/dist/src/yaml/steps/gradle-sdk-cmake.js +0 -107
  416. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts +0 -2
  417. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts.map +0 -1
  418. package/dist/src/yaml/steps/gradle-sdk-cmake.test.js +0 -183
  419. package/dist/src/yaml/steps/index.d.ts +0 -10
  420. package/dist/src/yaml/steps/index.d.ts.map +0 -1
  421. package/dist/src/yaml/steps/index.js +0 -1461
  422. package/dist/src/yaml/steps/ios-deps.d.ts +0 -78
  423. package/dist/src/yaml/steps/ios-deps.d.ts.map +0 -1
  424. package/dist/src/yaml/steps/ios-deps.js +0 -279
  425. package/dist/src/yaml/steps/ios-deps.test.d.ts +0 -5
  426. package/dist/src/yaml/steps/ios-deps.test.d.ts.map +0 -1
  427. package/dist/src/yaml/steps/ios-deps.test.js +0 -90
  428. package/dist/src/yaml/steps/ios-signing.d.ts +0 -31
  429. package/dist/src/yaml/steps/ios-signing.d.ts.map +0 -1
  430. package/dist/src/yaml/steps/ios-signing.js +0 -144
  431. package/dist/src/yaml/steps/ios-version.d.ts +0 -47
  432. package/dist/src/yaml/steps/ios-version.d.ts.map +0 -1
  433. package/dist/src/yaml/steps/ios-version.js +0 -151
  434. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts +0 -10
  435. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts.map +0 -1
  436. package/dist/src/yaml/steps/java-home-version-spec.test.js +0 -58
  437. package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.d.ts +0 -2
  438. package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.d.ts.map +0 -1
  439. package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.js +0 -139
  440. package/dist/src/yaml/steps/linting.d.ts +0 -47
  441. package/dist/src/yaml/steps/linting.d.ts.map +0 -1
  442. package/dist/src/yaml/steps/linting.js +0 -148
  443. package/dist/src/yaml/steps/phase2.test.d.ts +0 -6
  444. package/dist/src/yaml/steps/phase2.test.d.ts.map +0 -1
  445. package/dist/src/yaml/steps/phase2.test.js +0 -197
  446. package/dist/src/yaml/steps/phase3.test.d.ts +0 -5
  447. package/dist/src/yaml/steps/phase3.test.d.ts.map +0 -1
  448. package/dist/src/yaml/steps/phase3.test.js +0 -144
  449. package/dist/src/yaml/steps/phase4.test.d.ts +0 -5
  450. package/dist/src/yaml/steps/phase4.test.d.ts.map +0 -1
  451. package/dist/src/yaml/steps/phase4.test.js +0 -166
  452. package/dist/src/yaml/steps/phase5.test.d.ts +0 -6
  453. package/dist/src/yaml/steps/phase5.test.d.ts.map +0 -1
  454. package/dist/src/yaml/steps/phase5.test.js +0 -263
  455. package/dist/src/yaml/steps/registry.d.ts +0 -88
  456. package/dist/src/yaml/steps/registry.d.ts.map +0 -1
  457. package/dist/src/yaml/steps/registry.js +0 -125
  458. package/dist/src/yaml/steps/registry.test.d.ts +0 -5
  459. package/dist/src/yaml/steps/registry.test.d.ts.map +0 -1
  460. package/dist/src/yaml/steps/registry.test.js +0 -235
  461. package/dist/src/yaml/steps/release.d.ts +0 -50
  462. package/dist/src/yaml/steps/release.d.ts.map +0 -1
  463. package/dist/src/yaml/steps/release.js +0 -154
  464. package/dist/src/yaml/steps/render-post-processor.d.ts +0 -100
  465. package/dist/src/yaml/steps/render-post-processor.d.ts.map +0 -1
  466. package/dist/src/yaml/steps/render-post-processor.js +0 -270
  467. package/dist/src/yaml/steps/render-post-processor.test.d.ts +0 -13
  468. package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +0 -1
  469. package/dist/src/yaml/steps/render-post-processor.test.js +0 -220
  470. package/dist/src/yaml/steps/script.d.ts +0 -23
  471. package/dist/src/yaml/steps/script.d.ts.map +0 -1
  472. package/dist/src/yaml/steps/script.js +0 -63
  473. package/dist/src/yaml/steps/spec-validation.test.d.ts +0 -6
  474. package/dist/src/yaml/steps/spec-validation.test.d.ts.map +0 -1
  475. package/dist/src/yaml/steps/spec-validation.test.js +0 -130
  476. package/dist/src/yaml/steps/steps.test.d.ts +0 -6
  477. package/dist/src/yaml/steps/steps.test.d.ts.map +0 -1
  478. package/dist/src/yaml/steps/steps.test.js +0 -881
  479. package/dist/src/yaml/steps/structural-facts.d.ts +0 -992
  480. package/dist/src/yaml/steps/structural-facts.d.ts.map +0 -1
  481. package/dist/src/yaml/steps/structural-facts.js +0 -3276
  482. package/dist/src/yaml/steps/structural-facts.test.d.ts +0 -12
  483. package/dist/src/yaml/steps/structural-facts.test.d.ts.map +0 -1
  484. package/dist/src/yaml/steps/structural-facts.test.js +0 -3039
  485. package/dist/src/yaml/steps/test-config.d.ts +0 -5
  486. package/dist/src/yaml/steps/test-config.d.ts.map +0 -1
  487. package/dist/src/yaml/steps/test-config.js +0 -25
  488. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +0 -28
  489. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +0 -1
  490. package/dist/src/yaml/steps/trim-align-corpus.test.js +0 -383
  491. package/dist/src/yaml/steps/trim-align.d.ts +0 -87
  492. package/dist/src/yaml/steps/trim-align.d.ts.map +0 -1
  493. package/dist/src/yaml/steps/trim-align.js +0 -367
  494. package/dist/src/yaml/steps/trim-align.test.d.ts +0 -11
  495. package/dist/src/yaml/steps/trim-align.test.d.ts.map +0 -1
  496. package/dist/src/yaml/steps/trim-align.test.js +0 -261
  497. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +0 -66
  498. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +0 -1
  499. package/dist/src/yaml/steps/ui-fidelity-preview-android.js +0 -251
  500. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts +0 -13
  501. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts.map +0 -1
  502. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +0 -302
  503. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts +0 -134
  504. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +0 -1
  505. package/dist/src/yaml/steps/ui-fidelity-preview.js +0 -173
  506. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts +0 -11
  507. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts.map +0 -1
  508. package/dist/src/yaml/steps/ui-fidelity-preview.test.js +0 -210
  509. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +0 -155
  510. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +0 -1
  511. package/dist/src/yaml/steps/ui-fidelity-render-android.js +0 -1389
  512. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts +0 -2
  513. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts.map +0 -1
  514. package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +0 -1106
  515. package/dist/src/yaml/steps/ui-fidelity-render.d.ts +0 -230
  516. package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +0 -1
  517. package/dist/src/yaml/steps/ui-fidelity-render.js +0 -1597
  518. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts +0 -18
  519. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts.map +0 -1
  520. package/dist/src/yaml/steps/ui-fidelity-render.test.js +0 -1871
  521. package/dist/src/yaml/steps/xcode-app-product.d.ts +0 -90
  522. package/dist/src/yaml/steps/xcode-app-product.d.ts.map +0 -1
  523. package/dist/src/yaml/steps/xcode-app-product.js +0 -247
  524. package/dist/src/yaml/steps/xcode-app-product.test.d.ts +0 -2
  525. package/dist/src/yaml/steps/xcode-app-product.test.d.ts.map +0 -1
  526. package/dist/src/yaml/steps/xcode-app-product.test.js +0 -202
  527. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts +0 -30
  528. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts.map +0 -1
  529. package/dist/src/yaml/steps/xcode-derived-data.test.js +0 -279
  530. package/dist/src/yaml/steps/xcode-destination.d.ts +0 -47
  531. package/dist/src/yaml/steps/xcode-destination.d.ts.map +0 -1
  532. package/dist/src/yaml/steps/xcode-destination.js +0 -202
  533. package/dist/src/yaml/steps/xcode-destination.test.d.ts +0 -9
  534. package/dist/src/yaml/steps/xcode-destination.test.d.ts.map +0 -1
  535. package/dist/src/yaml/steps/xcode-destination.test.js +0 -227
  536. package/dist/src/yaml/steps/xcode-new.test.d.ts +0 -5
  537. package/dist/src/yaml/steps/xcode-new.test.d.ts.map +0 -1
  538. package/dist/src/yaml/steps/xcode-new.test.js +0 -211
  539. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts +0 -9
  540. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts.map +0 -1
  541. package/dist/src/yaml/steps/xcode-resolve-retry.test.js +0 -146
  542. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts +0 -53
  543. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts.map +0 -1
  544. package/dist/src/yaml/steps/xcode-simulator-archs.js +0 -98
  545. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts +0 -21
  546. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts.map +0 -1
  547. package/dist/src/yaml/steps/xcode-simulator-archs.test.js +0 -155
  548. package/dist/src/yaml/steps/xcode.d.ts +0 -230
  549. package/dist/src/yaml/steps/xcode.d.ts.map +0 -1
  550. package/dist/src/yaml/steps/xcode.js +0 -1350
  551. package/dist/src/yaml/types.d.ts +0 -76
  552. package/dist/src/yaml/types.d.ts.map +0 -1
  553. package/dist/src/yaml/types.js +0 -5
  554. package/dist/src/yaml/validation-types.d.ts +0 -102
  555. package/dist/src/yaml/validation-types.d.ts.map +0 -1
  556. package/dist/src/yaml/validation-types.js +0 -8
  557. package/dist/src/yaml/yaml-updater.d.ts +0 -24
  558. package/dist/src/yaml/yaml-updater.d.ts.map +0 -1
  559. package/dist/src/yaml/yaml-updater.js +0 -128
@@ -1,1433 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
- import prompts from "prompts";
4
- import { scanAndroidProject, formatScanResult, runnableJavaVersion, javaVersionProblem, } from "./android-scanner.js";
5
- import { scanIosProject, formatIosScanResult } from "./ios-scanner.js";
6
- import { SecretsManager } from "../yaml/secrets-manager.js";
7
- import { collectFileSecret } from "./file-secret-collector.js";
8
- import { generateGitHubActionsWorkflow } from "./github-workflow.js";
9
- import { detectDefaultBranch, FALLBACK_DEFAULT_BRANCH } from "../shared/git.js";
10
- function variantFromName(name) {
11
- const buildType = name.charAt(0).toUpperCase() + name.slice(1);
12
- return { variant: name, buildType, gradleTask: `assemble${buildType}` };
13
- }
14
- const DEFAULT_VARIANTS = {
15
- primary: variantFromName("debug"),
16
- pullRequest: variantFromName("debug"),
17
- release: variantFromName("release"),
18
- };
19
- async function promptScheme(workflowLabel, defaultScheme, detectedSchemes) {
20
- const choices = detectedSchemes.map((s) => ({ title: s, value: s }));
21
- choices.push({ title: "Other (enter custom scheme)", value: "__custom__" });
22
- const defaultIndex = detectedSchemes.indexOf(defaultScheme);
23
- const initial = defaultIndex !== -1 ? defaultIndex : 0;
24
- const { choice } = await prompts({
25
- type: "select",
26
- name: "choice",
27
- message: `Xcode scheme for ${workflowLabel}:`,
28
- choices,
29
- initial,
30
- });
31
- if (!choice)
32
- return defaultScheme;
33
- if (choice === "__custom__") {
34
- const { name } = await prompts({
35
- type: "text",
36
- name: "name",
37
- message: "Enter scheme name:",
38
- validate: (v) => (v.trim().length > 0 ? true : "Scheme name is required"),
39
- });
40
- return name?.trim() || defaultScheme;
41
- }
42
- return choice;
43
- }
44
- async function promptConfiguration(workflowLabel, defaultConfig) {
45
- const choices = [
46
- { title: "Debug", value: "Debug" },
47
- { title: "Release", value: "Release" },
48
- { title: "Other (enter custom configuration)", value: "__custom__" },
49
- ];
50
- const defaultIndex = choices.findIndex((c) => c.value === defaultConfig);
51
- const initial = defaultIndex !== -1 ? defaultIndex : 0;
52
- const { choice } = await prompts({
53
- type: "select",
54
- name: "choice",
55
- message: `Build configuration for ${workflowLabel}:`,
56
- choices,
57
- initial,
58
- });
59
- if (!choice)
60
- return defaultConfig;
61
- if (choice === "__custom__") {
62
- const { name } = await prompts({
63
- type: "text",
64
- name: "name",
65
- message: "Enter configuration name (e.g. Staging, AdHoc):",
66
- validate: (v) => (v.trim().length > 0 ? true : "Configuration name is required"),
67
- });
68
- return name?.trim() || defaultConfig;
69
- }
70
- return choice;
71
- }
72
- async function promptDistributionMethod(workflowLabel, defaultMethod) {
73
- const choices = [
74
- { title: "development — development signing, internal use", value: "development" },
75
- { title: "ad-hoc — Ad Hoc distribution (specific devices)", value: "ad-hoc" },
76
- { title: "app-store — App Store / TestFlight upload", value: "app-store" },
77
- { title: "enterprise — Enterprise in-house distribution", value: "enterprise" },
78
- ];
79
- const defaultIndex = choices.findIndex((c) => c.value === defaultMethod);
80
- const initial = defaultIndex !== -1 ? defaultIndex : 0;
81
- const { choice } = await prompts({
82
- type: "select",
83
- name: "choice",
84
- message: `Distribution method for ${workflowLabel}:`,
85
- choices,
86
- initial,
87
- });
88
- return choice || defaultMethod;
89
- }
90
- async function promptVariant(workflowLabel, defaultName, detectedVariants) {
91
- const choices = detectedVariants.map((v) => ({ title: v, value: v }));
92
- choices.push({ title: "Other (enter custom variant)", value: "__custom__" });
93
- const defaultIndex = detectedVariants.indexOf(defaultName);
94
- const initial = defaultIndex !== -1 ? defaultIndex : 0;
95
- const { choice } = await prompts({
96
- type: "select",
97
- name: "choice",
98
- message: `Build variant for ${workflowLabel}:`,
99
- choices,
100
- initial,
101
- });
102
- if (!choice)
103
- return variantFromName(defaultName);
104
- if (choice === "__custom__") {
105
- const { name } = await prompts({
106
- type: "text",
107
- name: "name",
108
- message: "Enter variant name (e.g. stagingDebug, freeRelease):",
109
- validate: (v) => (v.trim().length > 0 ? true : "Variant name is required"),
110
- });
111
- return variantFromName(name?.trim() || defaultName);
112
- }
113
- return variantFromName(choice);
114
- }
115
- /**
116
- * The properties file the Secrets Gradle Plugin reads, out of the warnings the
117
- * scanner produced for it.
118
- *
119
- * The two `secrets-plugin` warnings disagree about `location`, and reading the
120
- * wrong one aims the `file@` step at a build file. The per-key warning carries
121
- * the **properties file** there; the fallback warning carries the *build file
122
- * that applies the plugin* and names the properties file only in its message.
123
- */
124
- export function secretsPropertiesFileFrom(warnings) {
125
- const plugin = warnings.filter((w) => w.category === "secrets-plugin");
126
- if (plugin.length === 0)
127
- return null;
128
- const fromLocation = plugin.find((w) => w.location?.endsWith(".properties"));
129
- if (fromLocation?.location)
130
- return fromLocation.location;
131
- for (const w of plugin) {
132
- const named = /reads '([^']+)'/.exec(w.message);
133
- if (named)
134
- return named[1];
135
- }
136
- return "local.properties";
137
- }
138
- /**
139
- * The product flavor a variant builds, out of the flavors the scanner found —
140
- * `ciDynamicDebug` is a build of `ciDynamic`, `prodRelease` of `prod`.
141
- *
142
- * An Android variant name is the flavor followed by the capitalised build
143
- * type, so the character after the flavor has to be an upper-case one. That
144
- * check is load-bearing: without it a flavor called `pro` would claim
145
- * `prodRelease`. The longest match wins, so `ciDynamic` beats `ci` on a
146
- * repository that declares both.
147
- */
148
- export function flavorOfVariant(variant, productFlavors) {
149
- const matches = productFlavors
150
- .filter((flavor) => {
151
- if (flavor.length === 0 || !variant.startsWith(flavor))
152
- return false;
153
- const head = variant.slice(flavor.length)[0];
154
- return head !== undefined && head === head.toUpperCase() && head !== head.toLowerCase();
155
- })
156
- .sort((a, b) => b.length - a.length);
157
- return matches[0] ?? null;
158
- }
159
- /** Where a flavor's `google-services.json` belongs inside the app module. */
160
- export function googleServicesPathFor(flavor, module = "app") {
161
- return flavor
162
- ? `${module}/src/${flavor}/google-services.json`
163
- : `${module}/google-services.json`;
164
- }
165
- /**
166
- * The per-workflow path and key maps for `SetupOptions`, from the variants the
167
- * workflows build and the flavors the scanner found.
168
- *
169
- * `app/google-services.json` used to be hardcoded here. For a flavored build
170
- * that is the plugin's *fallback* location, not the right one, and writing
171
- * there is silently wrong rather than loudly wrong: the plugin finds a file,
172
- * parses it, and fails on a package name — which reads as a Firebase
173
- * misconfiguration rather than as the wrong file having been delivered. That
174
- * is what `bld_8e60d90ab4e55f85d2f72459` spent 1292 Gradle tasks proving.
175
- */
176
- export function googleServicesTargets(variants, productFlavors, module = "app") {
177
- const paths = {};
178
- for (const [workflow, config] of variantByWorkflow(variants)) {
179
- const flavor = flavorOfVariant(config.variant, productFlavors);
180
- paths[workflow] = googleServicesPathFor(flavor, module);
181
- }
182
- return paths;
183
- }
184
- /** The three workflows every generated pipeline has, in the order they are emitted. */
185
- export const GENERATED_WORKFLOWS = ["primary", "pull-request", "release"];
186
- /** The variant each workflow builds, keyed by the workflow's name in the YAML. */
187
- function variantByWorkflow(variants) {
188
- return [
189
- ["primary", variants.primary],
190
- ["pull-request", variants.pullRequest],
191
- ["release", variants.release],
192
- ];
193
- }
194
- /**
195
- * The `workflowFlavors` map for `SetupOptions`, from the variants the
196
- * workflows build and the flavors the scanner found.
197
- */
198
- export function variantFlavors(variants, productFlavors) {
199
- const flavors = {};
200
- for (const [workflow, config] of variantByWorkflow(variants)) {
201
- flavors[workflow] = flavorOfVariant(config.variant, productFlavors);
202
- }
203
- return flavors;
204
- }
205
- /**
206
- * The `envs` slots one credential needs: one when every workflow that reads it
207
- * wants the same bytes, one per workflow when they do not.
208
- *
209
- * What "the same bytes" means is the credential's own business — a keystore
210
- * follows the target path the pipeline declares for it, `google-services.json`
211
- * the flavor's source set, `local.properties` the flavor itself — so the
212
- * caller passes the discriminator it already computed, one entry per workflow
213
- * that reads the credential, and this decides only whether they agree. A
214
- * credential only one workflow reads can never disagree, which is why the
215
- * genuinely release-only ones keep one slot however flavored the repository
216
- * is.
217
- *
218
- * **The name never changes — the block it sits in does.** An agreed credential
219
- * is declared once in `app.envs`; a disagreement is declared under the plain
220
- * credential name in **each workflow's own `envs`**, which is where the YAML
221
- * already says which workflow something belongs to. `EnvResolver` merges
222
- * `app.envs` and then the running workflow's own, so `release`'s declaration is
223
- * invisible to `primary` without anything being encoded.
224
- *
225
- * `CIBUILD_SW__<WORKFLOW>__<VAR>` is deliberately *not* used here. That prefix
226
- * is the transport encoding `ci secrets upload` carries a workflow-scoped
227
- * **value** under — Invarn's secret scopes have no workflow column, so the
228
- * workflow travels in the key. A pipeline has structure instead, and spending
229
- * the encoding there would be a second way to say what the block already says
230
- * (Alex, 2026-09-10).
231
- *
232
- * Every workflow gets a slot, not only the odd one out: a workflow left without
233
- * one would have neither a value nor the declaration `file@1.0.0` refuses over.
234
- *
235
- * The `file@` step's `var_name` is the same plain name either way.
236
- */
237
- export function credentialSlots(varName, discriminatorByWorkflow) {
238
- const distinct = new Set(Object.values(discriminatorByWorkflow));
239
- if (distinct.size <= 1)
240
- return [{ slot: varName, workflow: null }];
241
- return Object.keys(discriminatorByWorkflow).map((workflow) => ({
242
- slot: varName,
243
- workflow,
244
- }));
245
- }
246
- /**
247
- * One discriminator per workflow, defaulted the way the step that writes the
248
- * credential defaults its own input — so a repository whose scan resolved
249
- * nothing does not read as three workflows that disagree.
250
- */
251
- function discriminators(map, fallback) {
252
- const out = {};
253
- for (const workflow of GENERATED_WORKFLOWS)
254
- out[workflow] = map[workflow] ?? fallback;
255
- return out;
256
- }
257
- /**
258
- * The groups `ci init` collects a credential's value in: one per distinct value
259
- * of whatever that credential follows, in workflow order.
260
- *
261
- * The same discriminator `credentialSlots` splits the *declaration* by, and
262
- * deliberately the same function computing it, so what `ci init` asks for and
263
- * what the pipeline declares cannot drift apart. One group means one slot and
264
- * one question; two groups mean one slot per workflow and a question per group.
265
- */
266
- export function collectionGroups(discriminatorByWorkflow, label) {
267
- const byDiscriminator = new Map();
268
- for (const [workflow, value] of Object.entries(discriminatorByWorkflow)) {
269
- byDiscriminator.set(value, [...(byDiscriminator.get(value) ?? []), workflow]);
270
- }
271
- return [...byDiscriminator].map(([discriminator, workflows]) => ({
272
- label: label(discriminator, workflows),
273
- workflows,
274
- discriminator,
275
- }));
276
- }
277
- /**
278
- * The groups the Secrets Gradle Plugin's properties file is collected in: one
279
- * per flavor.
280
- *
281
- * Unlike `google-services.json`, nothing about this credential's target says
282
- * the bytes differ — every workflow writes the same file to the same path.
283
- * What differs is the content: the plugin turns the file into `BuildConfig`,
284
- * and a flavor is an environment. Asking once and storing one value gives
285
- * `prod` the CI backend's base URL, on a green build.
286
- */
287
- export function propertiesFileGroups(workflowFlavors) {
288
- return collectionGroups(discriminators(workflowFlavors, ""), (flavor, workflows) => flavor ? `the ${flavor} flavor (${workflows.join(", ")})` : "every workflow");
289
- }
290
- export function detectSetupNeeds(warnings) {
291
- const secretsPropertiesPath = secretsPropertiesFileFrom(warnings);
292
- return {
293
- secretsProperties: secretsPropertiesPath !== null,
294
- secretsPropertiesPath: secretsPropertiesPath ?? "local.properties",
295
- keystore: warnings.some((w) => w.category === "missing-file" && /keystore/i.test(w.message)),
296
- keystoreProperties: warnings.some((w) => w.category === "missing-file" && /keystore\.properties/i.test(w.message)),
297
- googleServices: warnings.some((w) => w.category === "firebase" ||
298
- (w.category === "missing-file" && /google-services/i.test(w.message))),
299
- googlePlayDeploy: false,
300
- googlePlayPackageName: '',
301
- artifactType: 'apk',
302
- };
303
- }
304
- /**
305
- * The `trigger_map` block appended to every generated pipeline.
306
- *
307
- * Without it the pipeline is inert on the Invarn side: `dispatchRepoEvent`
308
- * skips any pipeline whose YAML declares no trigger_map, so a scaffolded repo
309
- * receives pushes and builds nothing, with no error to explain it.
310
- *
311
- * `release` is deliberately absent — it deploys to Google Play / App Store,
312
- * and a webhook must never reach it.
313
- */
314
- export function triggerMapBlock(defaultBranch = FALLBACK_DEFAULT_BRANCH) {
315
- const branch = defaultBranch.trim() || FALLBACK_DEFAULT_BRANCH;
316
- // JSON.stringify gives a valid double-quoted YAML scalar for any branch
317
- // name git will accept. Quoting is not cosmetic: a branch called `no` or
318
- // `y` parses as a boolean unquoted, and one called `1.0` as a number.
319
- return `
320
- # Which GitHub events build which workflow. Without this block the pipeline
321
- # is never triggered by a push or a pull request.
322
- trigger_map:
323
- - push_branch: ${JSON.stringify(branch)}
324
- workflow: primary
325
- - pull_request_target_branch: "*"
326
- workflow: pull-request
327
- `;
328
- }
329
- /**
330
- * The JDK a generated pipeline names when the project's own constraints have
331
- * no answer — nothing declared and no readable wrapper, or a declaration no
332
- * installed JDK can meet. A generated pipeline has to write something; what it
333
- * must not do is write a number the wrapper cannot start on and call that a
334
- * choice.
335
- */
336
- export const DEFAULT_PIPELINE_JAVA_VERSION = 17;
337
- export function generateAndroidPipeline(javaVersion = DEFAULT_PIPELINE_JAVA_VERSION, setup = { keystore: false, keystoreProperties: false, googleServices: false, googlePlayDeploy: false, googlePlayPackageName: '', artifactType: 'apk', keystorePaths: {}, googleServicesPaths: {}, workflowFlavors: {}, secretsProperties: false, secretsPropertiesPath: 'local.properties' }, variants = DEFAULT_VARIANTS, cacheTechnology = "gradle", metaNamespace = 'cibuild.io', defaultBranch = FALLBACK_DEFAULT_BRANCH) {
338
- const keystoreStepFor = (wf) => {
339
- if (!setup.keystore)
340
- return "";
341
- const path = setup.keystorePaths[wf] ?? "keystore.jks";
342
- return `
343
- - file@1.0.0:
344
- title: Setup Keystore
345
- is_skippable: true
346
- inputs:
347
- target_path: ${path}
348
- var_name: KEYSTORE_BASE64
349
- base64_encoded: true
350
- `;
351
- };
352
- // Add BUILD_TYPE override to workflow envs when it differs from the global default ("Debug")
353
- const buildTypeEnvOverride = (v) => v.buildType !== "Debug" ? ` - BUILD_TYPE: ${v.buildType}\n` : "";
354
- // Optional env var stubs (empty defaults — filled via ci secrets add).
355
- //
356
- // Declared and not merely used by a step: `unsetEnvKeys` — what `invarn
357
- // setup` searches the developer's machine by — reads `envs`, never step
358
- // inputs. An undeclared credential is never discovered and never offered for
359
- // upload, however many steps name it. The same declaration is what
360
- // `file@1.0.0` refuses over when nobody supplied the value.
361
- //
362
- // One slot per credential where every workflow that reads it wants the same
363
- // bytes, and one per workflow where they do not — see `credentialSlots`. The
364
- // name is the plain credential either way; a per-workflow one is declared in
365
- // that workflow's own `envs` rather than under an encoded name.
366
- const appEnvDeclarations = [];
367
- const workflowEnvDeclarations = {};
368
- const declare = (varName, discriminatorByWorkflow, comment) => {
369
- for (const { slot, workflow } of credentialSlots(varName, discriminatorByWorkflow)) {
370
- if (workflow === null) {
371
- appEnvDeclarations.push(` - ${slot}: ""${comment(null)}\n`);
372
- continue;
373
- }
374
- // Six spaces, not four: a workflow's `envs` sits one level deeper.
375
- (workflowEnvDeclarations[workflow] ??= []).push(` - ${slot}: ""${comment(workflow)}\n`);
376
- }
377
- };
378
- /** Every credential declaration this workflow owns, after its own variables. */
379
- const workflowEnvFor = (workflow) => (workflowEnvDeclarations[workflow] ?? []).join("");
380
- // Declared in the order they appear in `app.envs`, which the byte-identity
381
- // test on an unflavored pipeline pins.
382
- if (setup.keystore) {
383
- declare("KEYSTORE_BASE64", discriminators(setup.keystorePaths, "keystore.jks"), (wf) => wf
384
- ? ` # Set via: ci secrets add KEYSTORE_BASE64 -w ${wf}`
385
- : ` # Set via: ci secrets add KEYSTORE_BASE64 -w primary`);
386
- }
387
- // Release-only, and legitimately so: nothing but `release` signs a release
388
- // or talks to Google Play, so there is no second workflow to disagree with.
389
- if (setup.keystoreProperties) {
390
- appEnvDeclarations.push(` - KEYSTORE_PROPERTIES: "" # Release signing — set via: ci secrets add KEYSTORE_PROPERTIES -w release\n`);
391
- }
392
- if (setup.googleServices) {
393
- declare("GOOGLE_SERVICES_JSON", discriminators(setup.googleServicesPaths, "app/google-services.json"), (wf) => wf
394
- ? ` # Set via: ci secrets add GOOGLE_SERVICES_JSON -w ${wf}`
395
- : ` # Set via: ci secrets add GOOGLE_SERVICES_JSON`);
396
- }
397
- if (setup.googlePlayDeploy) {
398
- appEnvDeclarations.push(` - GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: "" # Set via: ci secrets add GOOGLE_PLAY_SERVICE_ACCOUNT_JSON -w release\n`);
399
- }
400
- if (setup.secretsProperties) {
401
- declare("LOCAL_PROPERTIES", discriminators(setup.workflowFlavors, ""), (wf) => wf
402
- ? ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES -w ${wf}`
403
- : ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES`);
404
- }
405
- const appEnv = appEnvDeclarations.join("");
406
- /**
407
- * The Secrets Gradle Plugin's properties file, written into every workflow
408
- * that builds. Its evaluation listener runs for every variant, so a debug
409
- * build fails to *configure* without it — the error names a module of the
410
- * user's own project and nothing connects it to a missing credential.
411
- */
412
- const secretsPropertiesStep = setup.secretsProperties
413
- ? `
414
- - file@1.0.0:
415
- title: Setup ${setup.secretsPropertiesPath}
416
- is_skippable: true
417
- inputs:
418
- target_path: ${setup.secretsPropertiesPath}
419
- var_name: LOCAL_PROPERTIES
420
- `
421
- : "";
422
- /**
423
- * Per workflow, exactly as the keystore is: the flavor a workflow builds
424
- * decides where the file is written. A workflow whose variant names no
425
- * flavor is unchanged — `app/google-services.json`.
426
- *
427
- * The `var_name` is the same in every workflow on purpose. Scoping a value
428
- * to a workflow is the *secret's* job, not the step's: `CIBUILD_SW__<WF>__X`
429
- * is decoded by `SecretsManager` and resolved by `EnvResolver` for the
430
- * workflow being run, so one name here reads a different value there.
431
- */
432
- const googleServicesStepFor = (wf) => {
433
- if (!setup.googleServices)
434
- return "";
435
- const path = setup.googleServicesPaths[wf] ?? "app/google-services.json";
436
- return `
437
- - file@1.0.0:
438
- is_skippable: true
439
- inputs:
440
- target_path: ${path}
441
- var_name: GOOGLE_SERVICES_JSON
442
- `;
443
- };
444
- const googlePlayStep = setup.googlePlayDeploy
445
- ? `
446
- - file@1.0.0:
447
- title: Setup Google Play Service Account
448
- is_skippable: true
449
- inputs:
450
- target_path: .ci/keys/google-play-service-account.json
451
- var_name: GOOGLE_PLAY_SERVICE_ACCOUNT_JSON
452
-
453
- - google-play-deploy@1.0.0:
454
- inputs:
455
- service_account_json_key_path: .ci/keys/google-play-service-account.json
456
- package_name: ${setup.googlePlayPackageName}
457
- artifact_type: ${setup.artifactType}
458
- track: internal
459
- `
460
- : "";
461
- const keystorePropertiesStep = setup.keystoreProperties
462
- ? `
463
- - script@1.0.0:
464
- title: Setup keystore.properties
465
- is_skippable: true
466
- inputs:
467
- content: |
468
- if [ -z "\$KEYSTORE_PROPERTIES" ]; then
469
- echo "KEYSTORE_PROPERTIES not set — skipping keystore.properties setup"
470
- exit 0
471
- fi
472
- printf '%s' "\$KEYSTORE_PROPERTIES" > "\$PROJECT_LOCATION/keystore.properties"
473
- echo "✓ keystore.properties created"
474
- `
475
- : "";
476
- const { primary: pv, pullRequest: prv, release: rv } = variants;
477
- // When deploying to Google Play as AAB, override the release gradle task and labels
478
- const isAab = setup.googlePlayDeploy && setup.artifactType === 'aab';
479
- const releaseGradleTask = isAab ? `bundle${rv.buildType}` : rv.gradleTask;
480
- const releaseArtifactLabel = isAab ? 'AAB' : 'APK';
481
- const releaseArtifactExt = isAab ? '*.aab' : '*.apk';
482
- const stack = cacheTechnology === "kmm" ? "macos-xcode-26.4" : "linux-docker-android-22.04";
483
- const platformMeta = cacheTechnology === "kmm" ? "kmm" : "android";
484
- return `format_version: '1'
485
-
486
- meta:
487
- ${metaNamespace}:
488
- stack: ${stack}
489
- machine_type: standard
490
- platform: ${platformMeta}
491
-
492
- app:
493
- envs:
494
- - PROJECT_LOCATION: .
495
- - MODULE: app
496
- - BUILD_TYPE: Debug
497
- - GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.parallel=true
498
- ${appEnv}
499
- workflows:
500
- primary:
501
- envs:
502
- - VARIANT: ${pv.variant}
503
- ${buildTypeEnvOverride(pv)}${workflowEnvFor("primary")}
504
- steps:
505
- ${keystoreStepFor("primary")}${googleServicesStepFor("primary")}${secretsPropertiesStep}
506
- - set-java-version@1.0.0:
507
- inputs:
508
- java_version: '${javaVersion}'
509
-
510
- - cache-pull@1.0.0:
511
- inputs:
512
- technology: ${cacheTechnology}
513
-
514
- - android-lint@1.0.0:
515
- is_skippable: true
516
- inputs:
517
- project_location: \$PROJECT_LOCATION
518
- module: \$MODULE
519
- variant: \$VARIANT
520
-
521
- - gradle-build@1.0.0:
522
- title: Build ${pv.buildType} APK
523
- inputs:
524
- project_location: \$PROJECT_LOCATION
525
- build_type: ${pv.buildType}
526
- gradle_task: ${pv.gradleTask}
527
- gradle_options: --stacktrace
528
-
529
- - android-unit-test@1.0.0:
530
- is_skippable: true
531
- inputs:
532
- project_location: \$PROJECT_LOCATION
533
- module: \$MODULE
534
- variant: \$VARIANT
535
-
536
- - cache-push@1.0.0:
537
- inputs:
538
- technology: ${cacheTechnology}
539
-
540
- pull-request:
541
- envs:
542
- - VARIANT: ${prv.variant}
543
- ${buildTypeEnvOverride(prv)}${workflowEnvFor("pull-request")}
544
- steps:
545
- ${keystoreStepFor("pull-request")}${googleServicesStepFor("pull-request")}${secretsPropertiesStep}
546
- - set-java-version@1.0.0:
547
- inputs:
548
- java_version: '${javaVersion}'
549
-
550
- - cache-pull@1.0.0:
551
- inputs:
552
- technology: ${cacheTechnology}
553
-
554
- - android-lint@1.0.0:
555
- is_skippable: true
556
- inputs:
557
- project_location: \$PROJECT_LOCATION
558
- module: \$MODULE
559
- variant: \$VARIANT
560
-
561
- - android-unit-test@1.0.0:
562
- is_skippable: true
563
- inputs:
564
- project_location: \$PROJECT_LOCATION
565
- module: \$MODULE
566
- variant: \$VARIANT
567
-
568
- - gradle-build@1.0.0:
569
- title: Build PR APK
570
- inputs:
571
- project_location: \$PROJECT_LOCATION
572
- build_type: ${prv.buildType}
573
- gradle_task: ${prv.gradleTask}
574
-
575
- - cache-push@1.0.0:
576
- inputs:
577
- technology: ${cacheTechnology}
578
-
579
- release:
580
- envs:
581
- - VARIANT: ${rv.variant}
582
- ${buildTypeEnvOverride(rv)}${workflowEnvFor("release")}
583
- steps:
584
- ${keystoreStepFor("release")}${keystorePropertiesStep}${googleServicesStepFor("release")}${secretsPropertiesStep}
585
- - set-java-version@1.0.0:
586
- inputs:
587
- java_version: '${javaVersion}'
588
-
589
- - gradle-build@1.0.0:
590
- title: Build ${rv.buildType} ${releaseArtifactLabel}
591
- inputs:
592
- project_location: \$PROJECT_LOCATION
593
- build_type: ${rv.buildType}
594
- gradle_task: ${releaseGradleTask}
595
- gradle_options: --stacktrace
596
-
597
- - android-unit-test@1.0.0:
598
- is_skippable: true
599
- inputs:
600
- project_location: \$PROJECT_LOCATION
601
- module: \$MODULE
602
- variant: \$VARIANT
603
- ${googlePlayStep}
604
- - script@1.0.0:
605
- title: Build Summary
606
- inputs:
607
- content: |
608
- echo "Build Complete"
609
- if [ -d ".ci/artifacts" ]; then
610
- find .ci/artifacts -name "${releaseArtifactExt}" | while read -r artifact; do
611
- echo "${releaseArtifactLabel}: \$artifact"
612
- done
613
- fi
614
- ${triggerMapBlock(defaultBranch)}`;
615
- }
616
- export function generateIosPipeline(projectPath, setup, variants, metaNamespace = 'cibuild.io', defaultBranch = FALLBACK_DEFAULT_BRANCH) {
617
- const { primary: pv, pullRequest: prv, release: rv } = variants;
618
- // Global scheme is the primary scheme (used as default across workflows)
619
- const globalScheme = pv.scheme;
620
- // Only emit per-workflow scheme env if it differs from global
621
- const schemeEnvFor = (scheme) => scheme !== globalScheme ? ` - SCHEME: ${scheme}\n` : "";
622
- // Configuration override env (global default is Debug)
623
- const configEnvFor = (cfg) => cfg !== "Debug" ? ` - CONFIGURATION: ${cfg}\n` : "";
624
- // Optional env var stubs
625
- const certEnv = setup.codeSigning
626
- ? ` - CERTIFICATE_P12: "" # Set via: ci secrets add CERTIFICATE_P12 -w release\n` +
627
- ` - CERTIFICATE_PASSWORD: "" # Set via: ci secrets add CERTIFICATE_PASSWORD -w release\n` +
628
- ` - PROVISIONING_PROFILE: "" # Set via: ci secrets add PROVISIONING_PROFILE -w release\n`
629
- : "";
630
- // CocoaPods install step
631
- const podInstallStep = setup.cocoaPods
632
- ? `
633
- - script@1.0.0:
634
- title: Install CocoaPods
635
- inputs:
636
- content: |
637
- pod install --repo-update
638
- `
639
- : "";
640
- const podInstallStepPR = setup.cocoaPods
641
- ? `
642
- - script@1.0.0:
643
- title: Install CocoaPods
644
- inputs:
645
- content: |
646
- pod install
647
- `
648
- : "";
649
- // Code signing setup step (release only)
650
- const codeSigningStep = setup.codeSigning
651
- ? `
652
- - script@1.0.0:
653
- title: Setup Code Signing
654
- is_skippable: true
655
- inputs:
656
- content: |
657
- if [ -z "\$CERTIFICATE_P12" ]; then
658
- echo "CERTIFICATE_P12 not set — skipping code signing setup"
659
- exit 0
660
- fi
661
- echo "\$CERTIFICATE_P12" | base64 --decode > /tmp/certificate.p12
662
- security create-keychain -p "" ci-build.keychain
663
- security import /tmp/certificate.p12 -k ci-build.keychain -P "\$CERTIFICATE_PASSWORD" -A -T /usr/bin/codesign
664
- security list-keychains -s ci-build.keychain
665
- security default-keychain -s ci-build.keychain
666
- security unlock-keychain -p "" ci-build.keychain
667
- security set-keychain-settings ci-build.keychain
668
- if [ -n "\$PROVISIONING_PROFILE" ]; then
669
- mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles
670
- echo "\$PROVISIONING_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\\ Profiles/profile.mobileprovision
671
- echo "✓ Provisioning profile installed"
672
- fi
673
- rm -f /tmp/certificate.p12
674
- echo "✓ Code signing setup complete"
675
- `
676
- : "";
677
- // App Store Connect deploy step (release only)
678
- const appStoreDeployStep = setup.appStoreDeploy
679
- ? `
680
- - app-store-deploy@1.0.0:
681
- inputs:
682
- skip_metadata: 'yes'
683
- skip_screenshots: 'yes'
684
- submit_for_review: 'no'
685
- `
686
- : "";
687
- // Cache technology depends on whether CocoaPods is used
688
- const iosCacheTechnology = setup.cocoaPods ? "cocoapods" : "spm";
689
- return `format_version: '1'
690
-
691
- meta:
692
- ${metaNamespace}:
693
- stack: macos-xcode-26.4
694
- machine_type: performance
695
- platform: ios
696
-
697
- app:
698
- envs:
699
- - PROJECT_PATH: ${projectPath}
700
- - SCHEME: ${globalScheme}
701
- - CONFIGURATION: Debug
702
- ${certEnv}
703
- workflows:
704
- primary:
705
- envs:
706
- ${schemeEnvFor(pv.scheme)}${configEnvFor(pv.configuration)}
707
- steps:
708
- - cache-pull@1.0.0:
709
- inputs:
710
- technology: ${iosCacheTechnology}
711
- ${podInstallStep}
712
- - xcode-archive@1.0.0:
713
- inputs:
714
- project_path: \$PROJECT_PATH
715
- scheme: \$SCHEME
716
- configuration: \$CONFIGURATION
717
- distribution_method: ${pv.distributionMethod}
718
- output_dir: .ci/artifacts
719
-
720
- - xcode-test@1.0.0:
721
- is_skippable: true
722
- inputs:
723
- project_path: \$PROJECT_PATH
724
- scheme: \$SCHEME
725
- destination: platform=iOS Simulator,OS=latest
726
- is_code_coverage_enabled: true
727
-
728
- - cache-push@1.0.0:
729
- inputs:
730
- technology: ${iosCacheTechnology}
731
-
732
- pull-request:
733
- envs:
734
- ${schemeEnvFor(prv.scheme)}${configEnvFor(prv.configuration)}
735
- steps:
736
- - cache-pull@1.0.0:
737
- inputs:
738
- technology: ${iosCacheTechnology}
739
- ${podInstallStepPR}
740
- - xcode-test@1.0.0:
741
- is_skippable: true
742
- inputs:
743
- project_path: \$PROJECT_PATH
744
- scheme: \$SCHEME
745
- destination: platform=iOS Simulator,OS=latest
746
-
747
- - xcodebuild@1.0.0:
748
- inputs:
749
- project_path: \$PROJECT_PATH
750
- scheme: \$SCHEME
751
- configuration: \$CONFIGURATION
752
- destination: generic/platform=iOS Simulator
753
-
754
- - cache-push@1.0.0:
755
- inputs:
756
- technology: ${iosCacheTechnology}
757
-
758
- release:
759
- envs:
760
- - CONFIGURATION: Release
761
- ${schemeEnvFor(rv.scheme)}
762
- steps:
763
- ${codeSigningStep}
764
- - cache-pull@1.0.0:
765
- inputs:
766
- technology: ${iosCacheTechnology}
767
- ${podInstallStep}
768
- - xcode-archive@1.0.0:
769
- inputs:
770
- project_path: \$PROJECT_PATH
771
- scheme: \$SCHEME
772
- configuration: \$CONFIGURATION
773
- distribution_method: ${rv.distributionMethod}
774
- perform_clean_action: 'yes'
775
- automatic_code_signing: '${setup.codeSigning || setup.appStoreDeploy ? 'on' : 'off'}'
776
- compile_bitcode: 'no'
777
- upload_bitcode: 'no'
778
- output_dir: .ci/artifacts
779
- ${appStoreDeployStep}
780
- - script@1.0.0:
781
- title: Build Summary
782
- inputs:
783
- content: |
784
- echo "Build Complete"
785
- if [ -n "\$CIBUILD_IPA_PATH" ]; then
786
- echo "IPA: \$CIBUILD_IPA_PATH"
787
- fi
788
- if [ -n "\$CIBUILD_XCARCHIVE_PATH" ]; then
789
- echo "Archive: \$CIBUILD_XCARCHIVE_PATH"
790
- fi
791
- ${triggerMapBlock(defaultBranch)}`;
792
- }
793
- /**
794
- * Prints a hint listing env vars / Gradle properties detected during scanning
795
- * that haven't been handled by the setup flow (keystore, google-services, etc.).
796
- * Prompts the user to add them as secrets before running the pipeline.
797
- */
798
- function showMissingSecretsHint(scanResult, setupOptions) {
799
- // Build a set of var names already configured by the setup flow
800
- const alreadyHandled = new Set();
801
- if (setupOptions.keystore)
802
- alreadyHandled.add("KEYSTORE_BASE64");
803
- if (setupOptions.keystoreProperties)
804
- alreadyHandled.add("KEYSTORE_PROPERTIES");
805
- if (setupOptions.googleServices)
806
- alreadyHandled.add("GOOGLE_SERVICES_JSON");
807
- // Only show actionable warnings that have a "ci secrets add VAR" hint
808
- const ACTIONABLE = new Set(["build-config", "env-var", "signing-config", "gradle-property", "secrets-plugin"]);
809
- const remaining = scanResult.warnings.filter((w) => {
810
- if (!ACTIONABLE.has(w.category) || w.severity !== "warning")
811
- return false;
812
- const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1];
813
- return varName && !alreadyHandled.has(varName);
814
- });
815
- if (remaining.length === 0)
816
- return;
817
- const LABELS = {
818
- "build-config": "BuildConfig env vars",
819
- "env-var": "Environment variables",
820
- "signing-config": "Signing credentials",
821
- "gradle-property": "Gradle properties",
822
- "secrets-plugin": "Google Secrets Gradle Plugin (local.properties keys)",
823
- };
824
- // Group by category — secrets-plugin keys are grouped separately and shown with a file hint
825
- const byCategory = new Map();
826
- for (const w of remaining) {
827
- if (!byCategory.has(w.category))
828
- byCategory.set(w.category, []);
829
- byCategory.get(w.category).push(w);
830
- }
831
- console.log("\n──────────────────────────────────────────────────");
832
- console.log("⚠ Additional secrets detected in your Gradle files");
833
- console.log("──────────────────────────────────────────────────");
834
- console.log(" These variables were found but not yet configured.\n");
835
- for (const [cat, items] of byCategory) {
836
- console.log(` ${LABELS[cat] ?? cat}:`);
837
- if (cat === "secrets-plugin") {
838
- // These all go into a single properties file — show as a block with a file-step hint
839
- const propertiesFile = items[0]?.location ?? "local.properties";
840
- console.log(` → These keys are read from '${propertiesFile}' by the Secrets Gradle Plugin.`);
841
- console.log(` → The file is gitignored — inject it as a file step before building.\n`);
842
- console.log(` Suggested step (add via: ci edit → Add step → file):`);
843
- console.log(` target_path: ${propertiesFile}`);
844
- console.log(` var_name: <SECRET_NAME_HOLDING_FILE_CONTENT>\n`);
845
- console.log(` Keys needed in '${propertiesFile}':`);
846
- for (const w of items) {
847
- const key = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
848
- console.log(` ${key}`);
849
- }
850
- }
851
- else {
852
- for (const w of items) {
853
- const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
854
- const loc = w.location ? ` (${w.location})` : "";
855
- console.log(` ci secrets add ${varName}${loc}`);
856
- }
857
- }
858
- console.log("");
859
- }
860
- }
861
- async function handleIosBuildCommand(cwd, options = {}) {
862
- // 1. Scan the project
863
- console.log("\n🔍 Scanning iOS project for potential unknowns...\n");
864
- const iosScanResult = await scanIosProject(cwd);
865
- console.log(formatIosScanResult(iosScanResult));
866
- // 2. Determine available schemes
867
- const detectedSchemes = iosScanResult.detectedSchemes.length > 0
868
- ? iosScanResult.detectedSchemes
869
- : ["MyApp"];
870
- if (iosScanResult.detectedSchemes.length > 1) {
871
- console.log(`ℹ Detected schemes: ${iosScanResult.detectedSchemes.join(", ")}\n`);
872
- }
873
- // 3. Prompt for scheme + configuration + distribution method per workflow
874
- const ni = options.nonInteractive;
875
- if (!ni)
876
- console.log("⚙ Configure build schemes and configurations\n");
877
- const primaryScheme = ni ? detectedSchemes[0] : await promptScheme("primary", detectedSchemes[0], detectedSchemes);
878
- const primaryConfig = ni ? "Debug" : await promptConfiguration("primary", "Debug");
879
- const primaryMethod = ni ? "development" : await promptDistributionMethod("primary", "development");
880
- const pullRequestScheme = ni ? detectedSchemes[0] : await promptScheme("pull-request", primaryScheme, detectedSchemes);
881
- const pullRequestConfig = ni ? "Debug" : await promptConfiguration("pull-request", "Debug");
882
- const pullRequestMethod = ni ? "development" : await promptDistributionMethod("pull-request", "development");
883
- const releaseScheme = ni ? detectedSchemes[0] : await promptScheme("release", primaryScheme, detectedSchemes);
884
- const releaseConfig = ni ? "Release" : await promptConfiguration("release", "Release");
885
- const releaseMethod = ni ? "app-store" : await promptDistributionMethod("release", "app-store");
886
- const iosVariants = {
887
- primary: { scheme: primaryScheme, configuration: primaryConfig, distributionMethod: primaryMethod },
888
- pullRequest: { scheme: pullRequestScheme, configuration: pullRequestConfig, distributionMethod: pullRequestMethod },
889
- release: { scheme: releaseScheme, configuration: releaseConfig, distributionMethod: releaseMethod },
890
- };
891
- // 4. Setup options
892
- const iosSetupOptions = {
893
- cocoaPods: false,
894
- codeSigning: false,
895
- appStoreDeploy: false,
896
- bundleId: "",
897
- };
898
- const secretsManager = new SecretsManager();
899
- // CocoaPods
900
- if (iosScanResult.hasCocoaPods) {
901
- if (ni) {
902
- iosSetupOptions.cocoaPods = true;
903
- }
904
- else {
905
- const { create } = await prompts({
906
- type: "confirm",
907
- name: "create",
908
- message: "Generate a pod install step for CocoaPods dependencies?",
909
- initial: true,
910
- });
911
- iosSetupOptions.cocoaPods = !!create;
912
- }
913
- }
914
- // Code signing
915
- if (iosScanResult.hasSigningConfig) {
916
- if (ni) {
917
- iosSetupOptions.codeSigning = true;
918
- }
919
- else {
920
- const { create } = await prompts({
921
- type: "confirm",
922
- name: "create",
923
- message: "Generate a code signing setup step for the release workflow?",
924
- initial: true,
925
- });
926
- iosSetupOptions.codeSigning = !!create;
927
- }
928
- if (iosSetupOptions.codeSigning) {
929
- await collectFileSecret({
930
- displayName: "certificate (.p12)",
931
- secretName: "CERTIFICATE_P12",
932
- fileMatch: (name) => name.endsWith(".p12"),
933
- readAs: "base64",
934
- workflowGroups: [{ label: "release", workflows: ["release"] }],
935
- }, cwd, secretsManager, ni);
936
- if (ni) {
937
- console.log(" Add later: ci secrets add CERTIFICATE_PASSWORD -w release");
938
- }
939
- else {
940
- const { password } = await prompts({
941
- type: "password",
942
- name: "password",
943
- message: "Certificate password (leave blank to add later):",
944
- });
945
- if (password?.trim()) {
946
- secretsManager.storeSecret("CERTIFICATE_PASSWORD", password.trim(), "release");
947
- console.log(" ✅ CERTIFICATE_PASSWORD stored for workflow: release");
948
- }
949
- else {
950
- console.log(" Add later: ci secrets add CERTIFICATE_PASSWORD -w release");
951
- }
952
- }
953
- await collectFileSecret({
954
- displayName: "provisioning profile (.mobileprovision)",
955
- secretName: "PROVISIONING_PROFILE",
956
- fileMatch: (name) => name.endsWith(".mobileprovision"),
957
- readAs: "base64",
958
- workflowGroups: [{ label: "release", workflows: ["release"] }],
959
- }, cwd, secretsManager, ni);
960
- }
961
- }
962
- // App Store Connect deploy — skip in non-interactive mode (requires credentials)
963
- if (!ni) {
964
- const { deployToAppStore } = await prompts({
965
- type: "confirm",
966
- name: "deployToAppStore",
967
- message: "Add App Store Connect / TestFlight upload step to release workflow?",
968
- initial: false,
969
- });
970
- iosSetupOptions.appStoreDeploy = !!deployToAppStore;
971
- if (iosSetupOptions.appStoreDeploy) {
972
- await collectFileSecret({
973
- displayName: "App Store Connect API key (.p8)",
974
- secretName: "APPLE_API_KEY_PATH",
975
- fileMatch: (name) => name.endsWith(".p8"),
976
- readAs: "base64",
977
- workflowGroups: [{ label: "release", workflows: ["release"] }],
978
- }, cwd, secretsManager);
979
- const { keyId } = await prompts({
980
- type: "text",
981
- name: "keyId",
982
- message: "Apple API Key ID (leave blank to add later):",
983
- });
984
- if (keyId?.trim()) {
985
- secretsManager.storeSecret("APPLE_API_KEY_ID", keyId.trim(), "release");
986
- console.log(" ✅ APPLE_API_KEY_ID stored for workflow: release");
987
- }
988
- else {
989
- console.log(" Add later: ci secrets add APPLE_API_KEY_ID -w release");
990
- }
991
- const { issuerId } = await prompts({
992
- type: "text",
993
- name: "issuerId",
994
- message: "Apple API Issuer ID (leave blank to add later):",
995
- });
996
- if (issuerId?.trim()) {
997
- secretsManager.storeSecret("APPLE_API_ISSUER_ID", issuerId.trim(), "release");
998
- console.log(" ✅ APPLE_API_ISSUER_ID stored for workflow: release");
999
- }
1000
- else {
1001
- console.log(" Add later: ci secrets add APPLE_API_ISSUER_ID -w release");
1002
- }
1003
- const detectedTeamId = iosScanResult.developmentTeam;
1004
- let teamIdToStore = detectedTeamId;
1005
- if (detectedTeamId) {
1006
- console.log(` ℹ Detected Apple Team ID from project: ${detectedTeamId}`);
1007
- secretsManager.storeSecret("APPLE_TEAM_ID", detectedTeamId, "release");
1008
- console.log(" ✅ APPLE_TEAM_ID stored for workflow: release");
1009
- }
1010
- else {
1011
- const { teamId } = await prompts({
1012
- type: "text",
1013
- name: "teamId",
1014
- message: "Apple Team ID (leave blank to add later):",
1015
- });
1016
- teamIdToStore = teamId?.trim() || "";
1017
- if (teamIdToStore) {
1018
- secretsManager.storeSecret("APPLE_TEAM_ID", teamIdToStore, "release");
1019
- console.log(" ✅ APPLE_TEAM_ID stored for workflow: release");
1020
- }
1021
- else {
1022
- console.log(" Add later: ci secrets add APPLE_TEAM_ID -w release");
1023
- }
1024
- }
1025
- }
1026
- }
1027
- // 5. Ensure .ci/pipelines/ exists
1028
- const pipelinesDir = resolve(cwd, ".ci", "pipelines");
1029
- if (!existsSync(pipelinesDir)) {
1030
- if (options.createPipelinesDir) {
1031
- mkdirSync(pipelinesDir, { recursive: true });
1032
- console.log("✅ Created .ci/pipelines/");
1033
- }
1034
- else {
1035
- console.error("Project not initialised. Run 'ci init' first.");
1036
- process.exit(1);
1037
- }
1038
- }
1039
- // 6. Check if cibuild.yml already exists
1040
- const outputPath = resolve(pipelinesDir, "cibuild.yml");
1041
- if (existsSync(outputPath) && !ni) {
1042
- const { overwrite } = await prompts({
1043
- type: "confirm",
1044
- name: "overwrite",
1045
- message: ".ci/pipelines/cibuild.yml already exists. Overwrite?",
1046
- initial: false,
1047
- });
1048
- if (!overwrite) {
1049
- console.log("\nCancelled. Existing pipeline was not modified.");
1050
- process.exit(0);
1051
- }
1052
- }
1053
- // 7. Generate and write the pipeline
1054
- const yaml = generateIosPipeline(iosScanResult.projectPath, iosSetupOptions, iosVariants, options.metaNamespace, detectDefaultBranch());
1055
- writeFileSync(outputPath, yaml, "utf-8");
1056
- console.log("\n✅ Generated .ci/pipelines/cibuild.yml");
1057
- console.log(" Platform: iOS");
1058
- console.log(` Workflows: primary (${iosVariants.primary.scheme}/${iosVariants.primary.configuration}), pull-request (${iosVariants.pullRequest.scheme}/${iosVariants.pullRequest.configuration}), release (${iosVariants.release.scheme}/${iosVariants.release.configuration})`);
1059
- if (iosSetupOptions.cocoaPods || iosSetupOptions.codeSigning || iosSetupOptions.appStoreDeploy) {
1060
- console.log(" Setup steps included:");
1061
- if (iosSetupOptions.cocoaPods)
1062
- console.log(" • Install CocoaPods (pod install)");
1063
- if (iosSetupOptions.codeSigning)
1064
- console.log(" • Setup Code Signing (CERTIFICATE_P12, PROVISIONING_PROFILE) — release only");
1065
- if (iosSetupOptions.appStoreDeploy)
1066
- console.log(" • App Store Connect / TestFlight upload (app-store-deploy) — release only");
1067
- }
1068
- // Generate GitHub Actions workflow
1069
- let githubWorkflowGenerated = false;
1070
- if (options.createPipelinesDir) {
1071
- githubWorkflowGenerated = generateGitHubActionsWorkflow({ platform: "ios", cwd });
1072
- }
1073
- // Show env var secrets hint
1074
- const envVarWarnings = iosScanResult.warnings.filter((w) => w.category === "env-var" && w.severity === "warning");
1075
- if (envVarWarnings.length > 0) {
1076
- console.log("\n──────────────────────────────────────────────────");
1077
- console.log("⚠ Additional secrets detected in your xcconfig files");
1078
- console.log("──────────────────────────────────────────────────");
1079
- console.log(" These variables were found but not yet configured.\n");
1080
- console.log(" Environment variables:");
1081
- for (const w of envVarWarnings) {
1082
- const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
1083
- const loc = w.location ? ` (${w.location})` : "";
1084
- console.log(` ci secrets add ${varName}${loc}`);
1085
- }
1086
- console.log("");
1087
- }
1088
- const hasSecrets = existsSync(resolve(cwd, ".cibuild-secrets.json"));
1089
- const hasWorkflow = existsSync(resolve(cwd, ".github", "workflows", "ci.yml"));
1090
- console.log("\nNext steps:");
1091
- console.log(" ci run .ci/pipelines/cibuild.yml -w primary # Run locally");
1092
- if (hasWorkflow) {
1093
- if (hasSecrets) {
1094
- console.log(" ci secrets upload # Upload secrets to GitHub");
1095
- }
1096
- console.log(" git add . && git push # Push to GitHub - CI runs automatically");
1097
- }
1098
- console.log("");
1099
- }
1100
- export async function handleBuildCommand(detectMobileProjectRoot, options = {}) {
1101
- const cwd = process.cwd();
1102
- // 1. Detect project type
1103
- const projectType = detectMobileProjectRoot(cwd);
1104
- if (projectType === "ios") {
1105
- await handleIosBuildCommand(cwd, options);
1106
- return;
1107
- }
1108
- if (projectType === "kmm" || projectType === "android") {
1109
- await handleAndroidBuildCommand(cwd, options, projectType === "kmm" ? "kmm" : "gradle");
1110
- return;
1111
- }
1112
- console.error("Error: Not a mobile project root.");
1113
- console.error("'ci build' must be run from the root folder of an Android, iOS, or KMM project.");
1114
- console.error("\nAndroid/KMM projects must contain one of:");
1115
- console.error(" build.gradle, build.gradle.kts, settings.gradle, settings.gradle.kts, gradlew");
1116
- console.error("\nKMM projects additionally need: iosApp/, shared/, or composeApp/ directory");
1117
- console.error("\niOS projects must contain one of:");
1118
- console.error(" *.xcodeproj, *.xcworkspace, Podfile");
1119
- process.exit(1);
1120
- }
1121
- async function handleAndroidBuildCommand(cwd, options, cacheTechnology) {
1122
- const platformLabel = cacheTechnology === "kmm" ? "KMM (Kotlin Multiplatform)" : "Android";
1123
- // 2. Scan the project for potential unknowns
1124
- console.log(`\n🔍 Scanning ${platformLabel} project for potential unknowns...\n`);
1125
- const scanResult = await scanAndroidProject(cwd);
1126
- console.log(formatScanResult(scanResult));
1127
- // 3. Prompt for build variants per workflow
1128
- const ni = options.nonInteractive;
1129
- const { variants: detectedVariants, productFlavors } = scanResult.buildVariants;
1130
- if (productFlavors.length > 0) {
1131
- console.log(`ℹ Detected product flavors: ${productFlavors.join(", ")}`);
1132
- console.log(` Available variants: ${detectedVariants.join(", ")}\n`);
1133
- }
1134
- let variants;
1135
- if (ni) {
1136
- variants = DEFAULT_VARIANTS;
1137
- }
1138
- else {
1139
- console.log("⚙ Configure build variants\n");
1140
- const primaryVariant = await promptVariant("primary", "debug", detectedVariants);
1141
- const pullRequestVariant = await promptVariant("pull-request", "debug", detectedVariants);
1142
- const releaseVariant = await promptVariant("release", "release", detectedVariants);
1143
- variants = {
1144
- primary: primaryVariant,
1145
- pullRequest: pullRequestVariant,
1146
- release: releaseVariant,
1147
- };
1148
- }
1149
- // 4. Prompt to generate setup steps for detected file issues
1150
- const setupNeeds = detectSetupNeeds(scanResult.warnings);
1151
- // Which flavor each workflow builds decides where its google-services.json
1152
- // is written, and — with no path to give it away — whether the properties
1153
- // file can be one value or has to be one per workflow. Both maps are
1154
- // derived from the variants chosen just above.
1155
- const googleServicesPaths = googleServicesTargets(variants, productFlavors);
1156
- const workflowFlavors = variantFlavors(variants, productFlavors);
1157
- const setupOptions = { keystore: false, keystoreProperties: false, googleServices: false, googlePlayDeploy: false, googlePlayPackageName: '', artifactType: 'apk', keystorePaths: {}, googleServicesPaths, workflowFlavors, secretsProperties: false, secretsPropertiesPath: setupNeeds.secretsPropertiesPath };
1158
- const secretsManager = new SecretsManager();
1159
- const keystoreWorkflowGroups = [
1160
- { label: "debug builds (primary, pull-request)", workflows: ["primary", "pull-request"] },
1161
- { label: "release", workflows: ["release"] },
1162
- ];
1163
- if (setupNeeds.keystore) {
1164
- if (ni) {
1165
- setupOptions.keystore = true;
1166
- }
1167
- else {
1168
- const { create } = await prompts({
1169
- type: "confirm",
1170
- name: "create",
1171
- message: "Generate a setup script step for the keystore?",
1172
- initial: true,
1173
- });
1174
- setupOptions.keystore = !!create;
1175
- }
1176
- if (setupOptions.keystore) {
1177
- setupOptions.keystorePaths = await collectFileSecret({
1178
- displayName: "keystore",
1179
- secretName: "KEYSTORE_BASE64",
1180
- fileMatch: (name) => name.endsWith(".jks") || name.endsWith(".keystore"),
1181
- readAs: "base64",
1182
- workflowGroups: keystoreWorkflowGroups,
1183
- promptTargetPath: true,
1184
- }, cwd, secretsManager, ni);
1185
- }
1186
- }
1187
- if (setupNeeds.keystoreProperties) {
1188
- if (ni) {
1189
- setupOptions.keystoreProperties = true;
1190
- }
1191
- else {
1192
- const { create } = await prompts({
1193
- type: "confirm",
1194
- name: "create",
1195
- message: "Generate a setup script step for keystore.properties (release signing)?",
1196
- initial: true,
1197
- });
1198
- setupOptions.keystoreProperties = !!create;
1199
- }
1200
- if (setupOptions.keystoreProperties) {
1201
- await collectFileSecret({
1202
- displayName: "keystore.properties",
1203
- secretName: "KEYSTORE_PROPERTIES",
1204
- fileMatch: (name) => name === "keystore.properties",
1205
- readAs: "text",
1206
- workflowGroups: [
1207
- { label: "release", workflows: ["release"] },
1208
- ],
1209
- }, cwd, secretsManager, ni);
1210
- }
1211
- }
1212
- // Resolve missing keystore target paths for all configured workflows.
1213
- // For each workflow with no file-selected path: extract storeFile from any
1214
- // KEYSTORE_PROPERTIES secret, then fall back to Gradle-detected paths.
1215
- if (setupOptions.keystore) {
1216
- const gradleKeystorePaths = scanResult.warnings
1217
- .filter((w) => w.category === "missing-file" && w.message.startsWith("Keystore file not found:"))
1218
- .map((w) => w.message.replace("Keystore file not found: ", ""));
1219
- for (const wf of keystoreWorkflowGroups.flatMap((g) => g.workflows)) {
1220
- if (setupOptions.keystorePaths[wf])
1221
- continue;
1222
- const kpContent = secretsManager.getSecret("KEYSTORE_PROPERTIES", wf);
1223
- if (kpContent) {
1224
- const line = kpContent.split("\n").find((l) => l.trim().startsWith("storeFile="));
1225
- if (line) {
1226
- setupOptions.keystorePaths[wf] = line.split("=").slice(1).join("=").trim();
1227
- continue;
1228
- }
1229
- }
1230
- if (gradleKeystorePaths.length > 0) {
1231
- setupOptions.keystorePaths[wf] = gradleKeystorePaths[0];
1232
- }
1233
- }
1234
- }
1235
- if (setupNeeds.googleServices) {
1236
- if (ni) {
1237
- setupOptions.googleServices = true;
1238
- }
1239
- else {
1240
- const { create } = await prompts({
1241
- type: "confirm",
1242
- name: "create",
1243
- message: "Generate a setup script step for google-services.json?",
1244
- initial: true,
1245
- });
1246
- setupOptions.googleServices = !!create;
1247
- }
1248
- if (setupOptions.googleServices) {
1249
- // One collection per distinct target path. Workflows building the same
1250
- // flavor write to the same place and are collected together; workflows
1251
- // building different flavors need different files, so each is asked
1252
- // separately and pinned to its own source set — which is what lets a
1253
- // flavored repository auto-select rather than guess.
1254
- //
1255
- // The secret name is the same every time, and the *value* is stored per
1256
- // workflow. That is what `SecretsManager` has always done, what
1257
- // `ci secrets upload` encodes as `CIBUILD_SW__<WF>__<VAR>`, and what
1258
- // `EnvResolver` resolves for the workflow being run.
1259
- const byTarget = collectionGroups(discriminators(setupOptions.googleServicesPaths, "app/google-services.json"), (_path, workflows) => workflows.join(", "));
1260
- for (const group of byTarget) {
1261
- const flavorDir = /\/src\/([^/]+)\//.exec(group.discriminator)?.[1] ?? null;
1262
- await collectFileSecret({
1263
- displayName: flavorDir
1264
- ? `google-services.json for the ${flavorDir} flavor`
1265
- : "google-services.json",
1266
- secretName: "GOOGLE_SERVICES_JSON",
1267
- fileMatch: (name) => name === "google-services.json",
1268
- // A flavored target takes the copy from that flavor's source set. An
1269
- // unflavored one takes anything that is *not* a flavor's copy, so
1270
- // the module root wins rather than an arbitrary source set.
1271
- pathMatch: flavorDir
1272
- ? (rel) => rel.includes(`/src/${flavorDir}/`) || rel.startsWith(`src/${flavorDir}/`)
1273
- : (rel) => !/(?:^|\/)src\/[^/]+\//.test(rel),
1274
- readAs: "text",
1275
- // One group per call, not all of them: each group narrows the search
1276
- // to its own flavor's source set, which is what lets a flavored
1277
- // repository auto-select rather than ask.
1278
- workflowGroups: [group],
1279
- }, cwd, secretsManager, ni);
1280
- }
1281
- }
1282
- }
1283
- if (setupNeeds.secretsProperties) {
1284
- const propertiesFile = setupNeeds.secretsPropertiesPath;
1285
- if (ni) {
1286
- setupOptions.secretsProperties = true;
1287
- }
1288
- else {
1289
- const { create } = await prompts({
1290
- type: "confirm",
1291
- name: "create",
1292
- message: `Generate a setup step for '${propertiesFile}' (read by the Secrets Gradle Plugin)?`,
1293
- initial: true,
1294
- });
1295
- setupOptions.secretsProperties = !!create;
1296
- }
1297
- if (setupOptions.secretsProperties) {
1298
- // Every workflow, not just release: the plugin's evaluation listener
1299
- // runs for every variant, so a debug build cannot configure without it.
1300
- //
1301
- // And one group per flavor, because the plugin turns this file into
1302
- // BuildConfig and a flavor is an environment. Every group in one call,
1303
- // unlike google-services.json above: there is no per-flavor path
1304
- // convention for a properties file to narrow the search by, so the
1305
- // groups differ only in which answer they take.
1306
- await collectFileSecret({
1307
- displayName: propertiesFile,
1308
- secretName: "LOCAL_PROPERTIES",
1309
- fileMatch: (name) => name === propertiesFile.split("/").pop(),
1310
- readAs: "text",
1311
- workflowGroups: propertiesFileGroups(setupOptions.workflowFlavors),
1312
- }, cwd, secretsManager, ni);
1313
- }
1314
- }
1315
- // 4b. Prompt for Google Play deployment setup — skip in non-interactive mode (requires package name + credentials)
1316
- if (!ni) {
1317
- const { setupGooglePlay } = await prompts({
1318
- type: "confirm",
1319
- name: "setupGooglePlay",
1320
- message: "Add a Google Play deploy step to the release workflow?",
1321
- initial: false,
1322
- });
1323
- if (setupGooglePlay) {
1324
- const { packageName } = await prompts({
1325
- type: "text",
1326
- name: "packageName",
1327
- message: "Android package name (e.g. com.example.app):",
1328
- validate: (v) => (v.trim().length > 0 ? true : "Package name is required"),
1329
- });
1330
- setupOptions.googlePlayDeploy = true;
1331
- setupOptions.googlePlayPackageName = packageName?.trim() || '';
1332
- const { artifactType } = await prompts({
1333
- type: "select",
1334
- name: "artifactType",
1335
- message: "Artifact format for Google Play upload:",
1336
- choices: [
1337
- { title: "AAB — Android App Bundle (recommended by Google Play)", value: "aab" },
1338
- { title: "APK — traditional APK format", value: "apk" },
1339
- ],
1340
- initial: 0,
1341
- });
1342
- setupOptions.artifactType = artifactType || 'aab';
1343
- await collectFileSecret({
1344
- displayName: "Google Play service account JSON",
1345
- secretName: "GOOGLE_PLAY_SERVICE_ACCOUNT_JSON",
1346
- fileMatch: (name) => name.endsWith(".json"),
1347
- readAs: "text",
1348
- workflowGroups: [
1349
- { label: "release", workflows: ["release"] },
1350
- ],
1351
- }, cwd, secretsManager);
1352
- }
1353
- }
1354
- // 5. Ensure .ci/pipelines/ exists
1355
- const pipelinesDir = resolve(cwd, ".ci", "pipelines");
1356
- if (!existsSync(pipelinesDir)) {
1357
- if (options.createPipelinesDir) {
1358
- mkdirSync(pipelinesDir, { recursive: true });
1359
- console.log("✅ Created .ci/pipelines/");
1360
- }
1361
- else {
1362
- console.error("Project not initialised. Run 'ci init' first.");
1363
- process.exit(1);
1364
- }
1365
- }
1366
- // 6. Check if cibuild.yml already exists
1367
- const outputPath = resolve(pipelinesDir, "cibuild.yml");
1368
- if (existsSync(outputPath) && !ni) {
1369
- const { overwrite } = await prompts({
1370
- type: "confirm",
1371
- name: "overwrite",
1372
- message: ".ci/pipelines/cibuild.yml already exists. Overwrite?",
1373
- initial: false,
1374
- });
1375
- if (!overwrite) {
1376
- console.log("\nCancelled. Existing pipeline was not modified.");
1377
- process.exit(0);
1378
- }
1379
- }
1380
- // 7. Generate and write the pipeline
1381
- //
1382
- // The chooser abstains rather than falling back, so `undefined` here means
1383
- // "no installed JDK meets this project's constraints" — not "17 is fine".
1384
- // The default is what gets written in that case, and the warning below is
1385
- // what says so; it is never silent when the answer is not what was asked
1386
- // for, which is the whole reason the two are resolved together.
1387
- const javaVersion = runnableJavaVersion(scanResult.detectedJavaVersion, scanResult.detectedGradleVersion);
1388
- const javaProblem = javaVersionProblem(scanResult.detectedJavaVersion, scanResult.detectedGradleVersion, scanResult.detectedJavaVersionSource);
1389
- if (javaProblem)
1390
- console.log(`\n⚠️ ${javaProblem}`);
1391
- const yaml = generateAndroidPipeline(javaVersion ?? DEFAULT_PIPELINE_JAVA_VERSION, setupOptions, variants, cacheTechnology, options.metaNamespace, detectDefaultBranch());
1392
- writeFileSync(outputPath, yaml, "utf-8");
1393
- console.log("\n✅ Generated .ci/pipelines/cibuild.yml");
1394
- console.log(` Platform: ${platformLabel}`);
1395
- console.log(` Workflows: primary (${variants.primary.variant}), pull-request (${variants.pullRequest.variant}), release (${variants.release.variant})`);
1396
- if (setupOptions.keystore || setupOptions.keystoreProperties || setupOptions.googleServices || setupOptions.googlePlayDeploy) {
1397
- console.log(" Setup steps included:");
1398
- if (setupOptions.keystore)
1399
- console.log(" • Setup Keystore (KEYSTORE_BASE64)");
1400
- if (setupOptions.keystoreProperties)
1401
- console.log(" • Setup keystore.properties (KEYSTORE_PROPERTIES) — release only");
1402
- if (setupOptions.googleServices) {
1403
- const seen = new Set();
1404
- for (const wf of GENERATED_WORKFLOWS) {
1405
- const path = setupOptions.googleServicesPaths[wf];
1406
- if (!path || seen.has(path))
1407
- continue;
1408
- seen.add(path);
1409
- console.log(` • Setup ${path} (GOOGLE_SERVICES_JSON)`);
1410
- }
1411
- }
1412
- if (setupOptions.googlePlayDeploy)
1413
- console.log(` • Google Play deploy (${setupOptions.googlePlayPackageName}) — release only`);
1414
- }
1415
- showMissingSecretsHint(scanResult, setupOptions);
1416
- // Generate GitHub Actions workflow
1417
- let githubWorkflowGenerated = false;
1418
- if (options.createPipelinesDir) {
1419
- githubWorkflowGenerated = generateGitHubActionsWorkflow({ platform: "android", cwd });
1420
- }
1421
- const hasSecrets = existsSync(resolve(cwd, ".cibuild-secrets.json"));
1422
- const hasWorkflow = existsSync(resolve(cwd, ".github", "workflows", "ci.yml"));
1423
- console.log("\nNext steps:");
1424
- console.log(" ci run .ci/pipelines/cibuild.yml -w primary # Run locally");
1425
- if (hasWorkflow) {
1426
- if (hasSecrets) {
1427
- console.log(" ci secrets upload # Upload secrets to GitHub");
1428
- }
1429
- console.log(" git add . && git push # Push to GitHub - CI runs automatically");
1430
- }
1431
- console.log("");
1432
- }
1433
- //# sourceMappingURL=build.js.map