@invarn/cibuild 2.9.4 → 2.9.7

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 (547) 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/android-sign.d.ts +0 -35
  282. package/dist/src/yaml/steps/android-sign.d.ts.map +0 -1
  283. package/dist/src/yaml/steps/android-sign.js +0 -147
  284. package/dist/src/yaml/steps/android-variant-resolution.test.d.ts +0 -2
  285. package/dist/src/yaml/steps/android-variant-resolution.test.d.ts.map +0 -1
  286. package/dist/src/yaml/steps/android-variant-resolution.test.js +0 -260
  287. package/dist/src/yaml/steps/android-version.d.ts +0 -26
  288. package/dist/src/yaml/steps/android-version.d.ts.map +0 -1
  289. package/dist/src/yaml/steps/android-version.js +0 -128
  290. package/dist/src/yaml/steps/android-version.test.d.ts +0 -5
  291. package/dist/src/yaml/steps/android-version.test.d.ts.map +0 -1
  292. package/dist/src/yaml/steps/android-version.test.js +0 -196
  293. package/dist/src/yaml/steps/android.d.ts +0 -156
  294. package/dist/src/yaml/steps/android.d.ts.map +0 -1
  295. package/dist/src/yaml/steps/android.js +0 -1084
  296. package/dist/src/yaml/steps/app-store-deploy.d.ts +0 -48
  297. package/dist/src/yaml/steps/app-store-deploy.d.ts.map +0 -1
  298. package/dist/src/yaml/steps/app-store-deploy.js +0 -167
  299. package/dist/src/yaml/steps/base.d.ts +0 -263
  300. package/dist/src/yaml/steps/base.d.ts.map +0 -1
  301. package/dist/src/yaml/steps/base.js +0 -380
  302. package/dist/src/yaml/steps/bitrise-android-tools.d.ts +0 -26
  303. package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +0 -1
  304. package/dist/src/yaml/steps/bitrise-android-tools.js +0 -200
  305. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +0 -5
  306. package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +0 -1
  307. package/dist/src/yaml/steps/bitrise-android-tools.test.js +0 -280
  308. package/dist/src/yaml/steps/bitrise-apk-info.d.ts +0 -22
  309. package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +0 -1
  310. package/dist/src/yaml/steps/bitrise-apk-info.js +0 -144
  311. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +0 -5
  312. package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +0 -1
  313. package/dist/src/yaml/steps/bitrise-apk-info.test.js +0 -331
  314. package/dist/src/yaml/steps/bitrise-slack.d.ts +0 -49
  315. package/dist/src/yaml/steps/bitrise-slack.d.ts.map +0 -1
  316. package/dist/src/yaml/steps/bitrise-slack.js +0 -280
  317. package/dist/src/yaml/steps/bitrise-slack.test.d.ts +0 -5
  318. package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +0 -1
  319. package/dist/src/yaml/steps/bitrise-slack.test.js +0 -484
  320. package/dist/src/yaml/steps/bitrise-ssh.d.ts +0 -27
  321. package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +0 -1
  322. package/dist/src/yaml/steps/bitrise-ssh.js +0 -134
  323. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +0 -5
  324. package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +0 -1
  325. package/dist/src/yaml/steps/bitrise-ssh.test.js +0 -205
  326. package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts +0 -27
  327. package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts.map +0 -1
  328. package/dist/src/yaml/steps/cache-pull-daemon.test.js +0 -212
  329. package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts +0 -42
  330. package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts.map +0 -1
  331. package/dist/src/yaml/steps/cache-pull-restore-roots.test.js +0 -209
  332. package/dist/src/yaml/steps/cache-push-daemon.test.d.ts +0 -27
  333. package/dist/src/yaml/steps/cache-push-daemon.test.d.ts.map +0 -1
  334. package/dist/src/yaml/steps/cache-push-daemon.test.js +0 -171
  335. package/dist/src/yaml/steps/cache.d.ts +0 -59
  336. package/dist/src/yaml/steps/cache.d.ts.map +0 -1
  337. package/dist/src/yaml/steps/cache.js +0 -1027
  338. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts +0 -42
  339. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts.map +0 -1
  340. package/dist/src/yaml/steps/cocoapods-deployment-floor.js +0 -140
  341. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts +0 -11
  342. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts.map +0 -1
  343. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.js +0 -176
  344. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +0 -22
  345. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +0 -1
  346. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +0 -97
  347. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts +0 -17
  348. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts.map +0 -1
  349. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.js +0 -282
  350. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts +0 -17
  351. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts.map +0 -1
  352. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.js +0 -132
  353. package/dist/src/yaml/steps/envstore-loader.test.d.ts +0 -15
  354. package/dist/src/yaml/steps/envstore-loader.test.d.ts.map +0 -1
  355. package/dist/src/yaml/steps/envstore-loader.test.js +0 -143
  356. package/dist/src/yaml/steps/fastlane.d.ts +0 -27
  357. package/dist/src/yaml/steps/fastlane.d.ts.map +0 -1
  358. package/dist/src/yaml/steps/fastlane.js +0 -79
  359. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts +0 -53
  360. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts.map +0 -1
  361. package/dist/src/yaml/steps/fidelity-aspect-guard.js +0 -72
  362. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts +0 -11
  363. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts.map +0 -1
  364. package/dist/src/yaml/steps/fidelity-aspect-guard.test.js +0 -50
  365. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts +0 -87
  366. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts.map +0 -1
  367. package/dist/src/yaml/steps/fidelity-reference-provenance.js +0 -111
  368. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts +0 -22
  369. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts.map +0 -1
  370. package/dist/src/yaml/steps/fidelity-reference-provenance.test.js +0 -118
  371. package/dist/src/yaml/steps/file.d.ts +0 -52
  372. package/dist/src/yaml/steps/file.d.ts.map +0 -1
  373. package/dist/src/yaml/steps/file.js +0 -108
  374. package/dist/src/yaml/steps/flutter.d.ts +0 -63
  375. package/dist/src/yaml/steps/flutter.d.ts.map +0 -1
  376. package/dist/src/yaml/steps/flutter.js +0 -215
  377. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts +0 -16
  378. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts.map +0 -1
  379. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.js +0 -127
  380. package/dist/src/yaml/steps/git-clone.d.ts +0 -32
  381. package/dist/src/yaml/steps/git-clone.d.ts.map +0 -1
  382. package/dist/src/yaml/steps/git-clone.js +0 -171
  383. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts +0 -10
  384. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts.map +0 -1
  385. package/dist/src/yaml/steps/git-fetch-retry-exec.test.js +0 -162
  386. package/dist/src/yaml/steps/git-fetch-retry.d.ts +0 -62
  387. package/dist/src/yaml/steps/git-fetch-retry.d.ts.map +0 -1
  388. package/dist/src/yaml/steps/git-fetch-retry.js +0 -155
  389. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts +0 -5
  390. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts.map +0 -1
  391. package/dist/src/yaml/steps/git-fetch-retry.test.js +0 -86
  392. package/dist/src/yaml/steps/google-play-deploy.d.ts +0 -37
  393. package/dist/src/yaml/steps/google-play-deploy.d.ts.map +0 -1
  394. package/dist/src/yaml/steps/google-play-deploy.js +0 -193
  395. package/dist/src/yaml/steps/google-play-deploy.test.d.ts +0 -5
  396. package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +0 -1
  397. package/dist/src/yaml/steps/google-play-deploy.test.js +0 -310
  398. package/dist/src/yaml/steps/gradle-memory.d.ts +0 -61
  399. package/dist/src/yaml/steps/gradle-memory.d.ts.map +0 -1
  400. package/dist/src/yaml/steps/gradle-memory.js +0 -311
  401. package/dist/src/yaml/steps/gradle-memory.test.d.ts +0 -2
  402. package/dist/src/yaml/steps/gradle-memory.test.d.ts.map +0 -1
  403. package/dist/src/yaml/steps/gradle-memory.test.js +0 -297
  404. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts +0 -28
  405. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts.map +0 -1
  406. package/dist/src/yaml/steps/gradle-sdk-cmake.js +0 -107
  407. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts +0 -2
  408. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts.map +0 -1
  409. package/dist/src/yaml/steps/gradle-sdk-cmake.test.js +0 -183
  410. package/dist/src/yaml/steps/index.d.ts +0 -10
  411. package/dist/src/yaml/steps/index.d.ts.map +0 -1
  412. package/dist/src/yaml/steps/index.js +0 -1461
  413. package/dist/src/yaml/steps/ios-deps.d.ts +0 -78
  414. package/dist/src/yaml/steps/ios-deps.d.ts.map +0 -1
  415. package/dist/src/yaml/steps/ios-deps.js +0 -279
  416. package/dist/src/yaml/steps/ios-deps.test.d.ts +0 -5
  417. package/dist/src/yaml/steps/ios-deps.test.d.ts.map +0 -1
  418. package/dist/src/yaml/steps/ios-deps.test.js +0 -90
  419. package/dist/src/yaml/steps/ios-signing.d.ts +0 -31
  420. package/dist/src/yaml/steps/ios-signing.d.ts.map +0 -1
  421. package/dist/src/yaml/steps/ios-signing.js +0 -144
  422. package/dist/src/yaml/steps/ios-version.d.ts +0 -47
  423. package/dist/src/yaml/steps/ios-version.d.ts.map +0 -1
  424. package/dist/src/yaml/steps/ios-version.js +0 -151
  425. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts +0 -10
  426. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts.map +0 -1
  427. package/dist/src/yaml/steps/java-home-version-spec.test.js +0 -58
  428. package/dist/src/yaml/steps/linting.d.ts +0 -47
  429. package/dist/src/yaml/steps/linting.d.ts.map +0 -1
  430. package/dist/src/yaml/steps/linting.js +0 -148
  431. package/dist/src/yaml/steps/phase2.test.d.ts +0 -6
  432. package/dist/src/yaml/steps/phase2.test.d.ts.map +0 -1
  433. package/dist/src/yaml/steps/phase2.test.js +0 -197
  434. package/dist/src/yaml/steps/phase3.test.d.ts +0 -5
  435. package/dist/src/yaml/steps/phase3.test.d.ts.map +0 -1
  436. package/dist/src/yaml/steps/phase3.test.js +0 -144
  437. package/dist/src/yaml/steps/phase4.test.d.ts +0 -5
  438. package/dist/src/yaml/steps/phase4.test.d.ts.map +0 -1
  439. package/dist/src/yaml/steps/phase4.test.js +0 -166
  440. package/dist/src/yaml/steps/phase5.test.d.ts +0 -6
  441. package/dist/src/yaml/steps/phase5.test.d.ts.map +0 -1
  442. package/dist/src/yaml/steps/phase5.test.js +0 -263
  443. package/dist/src/yaml/steps/registry.d.ts +0 -88
  444. package/dist/src/yaml/steps/registry.d.ts.map +0 -1
  445. package/dist/src/yaml/steps/registry.js +0 -125
  446. package/dist/src/yaml/steps/registry.test.d.ts +0 -5
  447. package/dist/src/yaml/steps/registry.test.d.ts.map +0 -1
  448. package/dist/src/yaml/steps/registry.test.js +0 -235
  449. package/dist/src/yaml/steps/release.d.ts +0 -50
  450. package/dist/src/yaml/steps/release.d.ts.map +0 -1
  451. package/dist/src/yaml/steps/release.js +0 -154
  452. package/dist/src/yaml/steps/render-post-processor.d.ts +0 -100
  453. package/dist/src/yaml/steps/render-post-processor.d.ts.map +0 -1
  454. package/dist/src/yaml/steps/render-post-processor.js +0 -270
  455. package/dist/src/yaml/steps/render-post-processor.test.d.ts +0 -13
  456. package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +0 -1
  457. package/dist/src/yaml/steps/render-post-processor.test.js +0 -220
  458. package/dist/src/yaml/steps/script.d.ts +0 -23
  459. package/dist/src/yaml/steps/script.d.ts.map +0 -1
  460. package/dist/src/yaml/steps/script.js +0 -63
  461. package/dist/src/yaml/steps/spec-validation.test.d.ts +0 -6
  462. package/dist/src/yaml/steps/spec-validation.test.d.ts.map +0 -1
  463. package/dist/src/yaml/steps/spec-validation.test.js +0 -130
  464. package/dist/src/yaml/steps/steps.test.d.ts +0 -6
  465. package/dist/src/yaml/steps/steps.test.d.ts.map +0 -1
  466. package/dist/src/yaml/steps/steps.test.js +0 -881
  467. package/dist/src/yaml/steps/structural-facts.d.ts +0 -992
  468. package/dist/src/yaml/steps/structural-facts.d.ts.map +0 -1
  469. package/dist/src/yaml/steps/structural-facts.js +0 -3276
  470. package/dist/src/yaml/steps/structural-facts.test.d.ts +0 -12
  471. package/dist/src/yaml/steps/structural-facts.test.d.ts.map +0 -1
  472. package/dist/src/yaml/steps/structural-facts.test.js +0 -3039
  473. package/dist/src/yaml/steps/test-config.d.ts +0 -5
  474. package/dist/src/yaml/steps/test-config.d.ts.map +0 -1
  475. package/dist/src/yaml/steps/test-config.js +0 -25
  476. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +0 -28
  477. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +0 -1
  478. package/dist/src/yaml/steps/trim-align-corpus.test.js +0 -383
  479. package/dist/src/yaml/steps/trim-align.d.ts +0 -87
  480. package/dist/src/yaml/steps/trim-align.d.ts.map +0 -1
  481. package/dist/src/yaml/steps/trim-align.js +0 -367
  482. package/dist/src/yaml/steps/trim-align.test.d.ts +0 -11
  483. package/dist/src/yaml/steps/trim-align.test.d.ts.map +0 -1
  484. package/dist/src/yaml/steps/trim-align.test.js +0 -261
  485. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +0 -66
  486. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +0 -1
  487. package/dist/src/yaml/steps/ui-fidelity-preview-android.js +0 -251
  488. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts +0 -13
  489. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts.map +0 -1
  490. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +0 -302
  491. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts +0 -134
  492. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +0 -1
  493. package/dist/src/yaml/steps/ui-fidelity-preview.js +0 -173
  494. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts +0 -11
  495. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts.map +0 -1
  496. package/dist/src/yaml/steps/ui-fidelity-preview.test.js +0 -210
  497. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +0 -155
  498. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +0 -1
  499. package/dist/src/yaml/steps/ui-fidelity-render-android.js +0 -1389
  500. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts +0 -2
  501. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts.map +0 -1
  502. package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +0 -1106
  503. package/dist/src/yaml/steps/ui-fidelity-render.d.ts +0 -230
  504. package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +0 -1
  505. package/dist/src/yaml/steps/ui-fidelity-render.js +0 -1597
  506. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts +0 -18
  507. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts.map +0 -1
  508. package/dist/src/yaml/steps/ui-fidelity-render.test.js +0 -1871
  509. package/dist/src/yaml/steps/xcode-app-product.d.ts +0 -90
  510. package/dist/src/yaml/steps/xcode-app-product.d.ts.map +0 -1
  511. package/dist/src/yaml/steps/xcode-app-product.js +0 -247
  512. package/dist/src/yaml/steps/xcode-app-product.test.d.ts +0 -2
  513. package/dist/src/yaml/steps/xcode-app-product.test.d.ts.map +0 -1
  514. package/dist/src/yaml/steps/xcode-app-product.test.js +0 -202
  515. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts +0 -30
  516. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts.map +0 -1
  517. package/dist/src/yaml/steps/xcode-derived-data.test.js +0 -253
  518. package/dist/src/yaml/steps/xcode-destination.d.ts +0 -47
  519. package/dist/src/yaml/steps/xcode-destination.d.ts.map +0 -1
  520. package/dist/src/yaml/steps/xcode-destination.js +0 -202
  521. package/dist/src/yaml/steps/xcode-destination.test.d.ts +0 -9
  522. package/dist/src/yaml/steps/xcode-destination.test.d.ts.map +0 -1
  523. package/dist/src/yaml/steps/xcode-destination.test.js +0 -227
  524. package/dist/src/yaml/steps/xcode-new.test.d.ts +0 -5
  525. package/dist/src/yaml/steps/xcode-new.test.d.ts.map +0 -1
  526. package/dist/src/yaml/steps/xcode-new.test.js +0 -211
  527. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts +0 -9
  528. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts.map +0 -1
  529. package/dist/src/yaml/steps/xcode-resolve-retry.test.js +0 -146
  530. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts +0 -53
  531. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts.map +0 -1
  532. package/dist/src/yaml/steps/xcode-simulator-archs.js +0 -98
  533. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts +0 -21
  534. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts.map +0 -1
  535. package/dist/src/yaml/steps/xcode-simulator-archs.test.js +0 -155
  536. package/dist/src/yaml/steps/xcode.d.ts +0 -230
  537. package/dist/src/yaml/steps/xcode.d.ts.map +0 -1
  538. package/dist/src/yaml/steps/xcode.js +0 -1340
  539. package/dist/src/yaml/types.d.ts +0 -76
  540. package/dist/src/yaml/types.d.ts.map +0 -1
  541. package/dist/src/yaml/types.js +0 -5
  542. package/dist/src/yaml/validation-types.d.ts +0 -102
  543. package/dist/src/yaml/validation-types.d.ts.map +0 -1
  544. package/dist/src/yaml/validation-types.js +0 -8
  545. package/dist/src/yaml/yaml-updater.d.ts +0 -24
  546. package/dist/src/yaml/yaml-updater.d.ts.map +0 -1
  547. package/dist/src/yaml/yaml-updater.js +0 -128
@@ -1,1027 +0,0 @@
1
- /**
2
- * Cache step implementations (cache-pull and cache-push)
3
- */
4
- import { BaseStepExecutor } from './base.js';
5
- /**
6
- * Files that together define a Gradle/KMM build graph. Hashing these catches
7
- * module additions/removals, plugin changes, and version-catalog bumps — things
8
- * that invalidate reusability of the `~/.gradle/caches` contents across branches.
9
- * Source file edits do NOT affect this hash; Gradle's task cache inside the
10
- * tarball handles source-level reuse.
11
- */
12
- const GRADLE_FINGERPRINT = [
13
- 'settings.gradle',
14
- 'settings.gradle.kts',
15
- 'build.gradle',
16
- 'build.gradle.kts',
17
- 'libs.versions.toml',
18
- 'gradle-wrapper.properties',
19
- ];
20
- export const CACHE_PRESETS = {
21
- ios: { lockfile: 'Podfile.lock', keyPrefix: 'pods', paths: ['Pods'], fallback: 'spm' },
22
- cocoapods: { lockfile: 'Podfile.lock', keyPrefix: 'pods', paths: ['Pods'] },
23
- carthage: { lockfile: 'Cartfile.resolved', keyPrefix: 'carthage', paths: ['Carthage'] },
24
- spm: { lockfile: 'Package.resolved', keyPrefix: 'spm', paths: ['~/Library/Developer/Xcode/DerivedData/*/SourcePackages'] },
25
- gradle: { fingerprint: GRADLE_FINGERPRINT, keyPrefix: 'gradle', paths: ['~/.gradle/caches', '~/.gradle/wrapper/dists'] },
26
- kmm: { fingerprint: GRADLE_FINGERPRINT, keyPrefix: 'kmm', paths: ['~/.gradle/caches', '~/.gradle/wrapper/dists', '~/.konan'] },
27
- npm: { lockfile: 'package-lock.json', keyPrefix: 'npm', paths: ['node_modules'] },
28
- yarn: { lockfile: 'yarn.lock', keyPrefix: 'yarn', paths: ['node_modules'] },
29
- dart: { lockfile: 'pubspec.lock', keyPrefix: 'dart', paths: ['.dart_tool', '.pub-cache'] },
30
- };
31
- /**
32
- * Emits bash that derives a stable short project id from the git remote URL
33
- * (with embedded credentials stripped, so token rotations don't churn the key).
34
- * Sets $__ci_proj to an 8-char hex. Falls back to "no-remote" if no git remote.
35
- */
36
- function emitProjectScopeCommands() {
37
- return [
38
- '__ci_git_remote=$(git config --get remote.origin.url 2>/dev/null | sed -E "s#(://)[^@/]+@#\\1#" || echo "no-remote")',
39
- // `shasum` is Perl-provided and is not guaranteed on a slim Linux image;
40
- // `sha256sum` is coreutils and is absent from macOS. Both print
41
- // `<hex> -`, so either yields the same 8-char project id — which is what
42
- // keeps this byte-identical to `deriveProjectId` in the runner.
43
- '__ci_sha256() { if command -v shasum >/dev/null 2>&1; then shasum -a 256; else sha256sum; fi; }',
44
- '__ci_proj=$(printf "%s" "${__ci_git_remote:-no-remote}" | __ci_sha256 | cut -c1-8)',
45
- ];
46
- }
47
- /**
48
- * Emits bash that discovers fingerprint filenames across the repo, sorts them,
49
- * and hashes their concatenated names+contents into CACHE_KEY.
50
- * Key format: <keyPrefix>-<projectId>-<fingerprint>, so tarballs are scoped
51
- * per-repo and fallback globbing can't cross-contaminate between projects.
52
- * Used by presets (Gradle, KMM) where no single lockfile captures the build graph.
53
- */
54
- function emitFingerprintKeyCommands(preset) {
55
- const fp = preset.fingerprint ?? [];
56
- const escape = (s) => s.replace(/'/g, "'\\''");
57
- const nameArgs = fp
58
- .map((n, i) => `${i === 0 ? '' : '-o '}-name '${escape(n)}'`)
59
- .join(' ');
60
- return [
61
- `__ci_fp_files=$(find . -type f \\( ${nameArgs} \\) \\`,
62
- ` -not -path '*/build/*' \\`,
63
- ` -not -path '*/.gradle/*' \\`,
64
- ` -not -path '*/node_modules/*' \\`,
65
- ` -not -path '*/DerivedData/*' \\`,
66
- ` 2>/dev/null | LC_ALL=C sort)`,
67
- `if [ -n "$__ci_fp_files" ]; then`,
68
- ` CHECKSUM=$(echo "$__ci_fp_files" | xargs shasum -a 256 2>/dev/null | shasum -a 256 | cut -c1-16)`,
69
- ` CACHE_KEY="${escape(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`,
70
- `else`,
71
- ` echo "Warning: no fingerprint files found, using fallback cache key"`,
72
- ` CACHE_KEY="${escape(preset.keyPrefix)}-\${__ci_proj}-no-fingerprint"`,
73
- `fi`,
74
- ];
75
- }
76
- /**
77
- * Resolves a preset with its fallback chain into a flat list of
78
- * {lockfile, keyPrefix, paths} entries to try in order at runtime.
79
- */
80
- function resolvePresetChain(technology) {
81
- const chain = [];
82
- let current = technology;
83
- const visited = new Set();
84
- while (current && !visited.has(current)) {
85
- visited.add(current);
86
- const preset = CACHE_PRESETS[current];
87
- if (!preset)
88
- break;
89
- chain.push(preset);
90
- current = preset.fallback;
91
- }
92
- return chain;
93
- }
94
- /**
95
- * Resolves the `identifier:pattern` shorthand a `cache_paths` entry may use
96
- * (`derived_data:MyApp-*`, `builds:nightly`) into a real path. Anything else is
97
- * returned unchanged.
98
- *
99
- * Shared because the two sides used to disagree: `cache-push` resolved these
100
- * and `cache-pull` did not, so the classification below would have put a
101
- * `derived_data:` entry in the project-relative class on the way out and the
102
- * absolute class on the way in.
103
- */
104
- function resolveCachePathIdentifier(path, config) {
105
- if (!path.includes(':'))
106
- return path;
107
- const [identifier, pattern] = path.split(':', 2);
108
- switch (identifier) {
109
- case 'derived_data':
110
- return `${config.paths.derivedDataDir}/${pattern}`;
111
- case 'builds':
112
- return `${config.paths.buildsDir}/${pattern}`;
113
- default:
114
- return path;
115
- }
116
- }
117
- /**
118
- * Splits `cache_paths` into the two classes that decide where a member has to
119
- * be extracted to.
120
- *
121
- * `cache-push` archives whatever `PATHS_TO_CACHE` holds. A `~`- or `/`-rooted
122
- * entry is expanded to an absolute path and tar stores it with the leading `/`
123
- * stripped (`/home/builder/.gradle/caches` -> `home/builder/.gradle/caches`),
124
- * which round-trips through `-C /`. A project-relative entry (`Pods`,
125
- * `.gradle`, `node_modules`) is stored as-is, relative to the step's working
126
- * directory — and `-C /` then tries to create it at the root of the
127
- * filesystem, which the build user cannot do.
128
- *
129
- * So the class is decided by the declared form, not by anything in the tarball,
130
- * and both steps can compute it from the same input.
131
- */
132
- function classifyCachePaths(paths, config) {
133
- const absolute = [];
134
- const relative = [];
135
- for (const raw of paths) {
136
- const path = resolveCachePathIdentifier(raw, config);
137
- if (path.startsWith('~') || path.startsWith('/')) {
138
- absolute.push(path);
139
- }
140
- else {
141
- relative.push(path);
142
- }
143
- }
144
- return { absolute, relative };
145
- }
146
- /**
147
- * Emits `__ci_cache_extract`, which every restore path pipes its tar stream
148
- * into, plus the two member lists it reads.
149
- *
150
- * **The defect it closes.** Every restore used one `tar -xf - -C /`. For a
151
- * `cache_paths` that mixes the two classes — which every Gradle/Android
152
- * pipeline does, including the one the README ships — the absolute half landed
153
- * (that *was* the warmth) and the relative half could not, so tar exited
154
- * non-zero, the compound condition went false, and a build that had just been
155
- * restored reported `CACHE_SOURCE=cold`. Worse, a pipeline whose paths are
156
- * *entirely* project-relative (`Pods`, `Podfile.lock`, `node_modules`) had
157
- * every member fail: those caches were written on every build and had never
158
- * restored anything. Measured on GNU tar 1.35 and bsdtar 3.5.3 alike.
159
- *
160
- * On the filesystem transport the same failure was not a mis-report at all —
161
- * the extraction is a bare command there, so under `set -e -o pipefail` it
162
- * ended the step.
163
- *
164
- * **Cost.** A restore with no project-relative paths keeps today's single
165
- * streaming pass and stages nothing, so the shape that already worked pays
166
- * nothing. The mixed and all-relative shapes stage the tar and read it twice,
167
- * which is the price of extracting two roots from one stream.
168
- *
169
- * Written for bash 3.2 — macOS ships it, and the generated script says
170
- * `#!/bin/bash`.
171
- */
172
- function emitCacheExtractHelper(paths, config, indent = '') {
173
- const { absolute, relative } = classifyCachePaths(paths, config);
174
- const lit = (s) => `'${s.replace(/'/g, "'\\''")}'`;
175
- const out = [
176
- // Captured before anything can cd: project-relative members are recorded
177
- // against the step's working directory, so that is where they go back.
178
- `${indent}__ci_cache_root="$PWD"`,
179
- `${indent}__ci_cache_abs=()`,
180
- `${indent}__ci_cache_rel=()`,
181
- ];
182
- for (const p of absolute) {
183
- // Expanded at runtime — `~` means ${CIBUILD_USER_HOME:-$HOME}, which the
184
- // generator cannot know. The leading `/` is stripped to match how tar
185
- // recorded the member.
186
- out.push(`${indent}__ci_p=${lit(p)}`);
187
- out.push(`${indent}__ci_p="\${__ci_p/#~/\${CIBUILD_USER_HOME:-$HOME}}"`);
188
- out.push(`${indent}__ci_cache_abs+=("\${__ci_p#/}")`);
189
- }
190
- for (const p of relative) {
191
- out.push(`${indent}__ci_cache_rel+=(${lit(p)})`);
192
- }
193
- out.push(`${indent}__ci_cache_extract() {`,
194
- // Nothing project-relative: the archive is entirely absolute-derived and
195
- // one streaming pass is both correct and cheapest.
196
- `${indent} if [ \${#__ci_cache_rel[@]} -eq 0 ]; then`, `${indent} tar -xf - -C /`, `${indent} return`, `${indent} fi`, `${indent} local __t="\${TMPDIR:-/tmp}/cibuild-cache-extract.$$.tar"`, `${indent} local __rc=0 __top __c __is_rel __p`, `${indent} local __abs_ops __rel_ops`, `${indent} cat > "$__t" || { rm -f "$__t"; return 1; }`,
197
- // The split is decided on the archive's own top-level names, not by
198
- // `--exclude`. tar's exclude patterns are unanchored: `--exclude=.gradle`
199
- // also matches `home/builder/.gradle`, so excluding the project-relative
200
- // half from the pass that targets `/` silently dropped the absolute half
201
- // too whenever the two shared a component name — which the canonical
202
- // Gradle shape (`~/.gradle/caches` plus `.gradle`) always does.
203
- `${indent} __top=$(tar -tf "$__t" 2>/dev/null | sed -e 's|^\\./||' -e 's|/.*||' | grep -v '^$' | sort -u) || __rc=$?`, `${indent} if [ "$__rc" -ne 0 ]; then rm -f "$__t"; return "$__rc"; fi`, `${indent} __abs_ops=()`, `${indent} __rel_ops=()`,
204
- // Read rather than word-split: a cached path may contain spaces
205
- // (`derived_data:My App-*`). Process substitution keeps the loop in this
206
- // shell, so the arrays it fills survive it.
207
- `${indent} while IFS= read -r __c; do`, `${indent} __is_rel=0`, `${indent} for __p in "\${__ci_cache_rel[@]}"; do`, `${indent} case "$__p" in "$__c"|"$__c"/*) __is_rel=1 ;; esac`, `${indent} done`,
208
- // A name claimed by both classes resolves to `/`, which is what it did
209
- // before this existed. It needs a project directory named after a
210
- // filesystem root while also caching an absolute path under it.
211
- `${indent} if [ "$__is_rel" -eq 1 ]; then __rel_ops+=("$__c"); else __abs_ops+=("$__c"); fi`, `${indent} done < <(printf '%s\\n' "$__top")`, `${indent} if [ \${#__abs_ops[@]} -gt 0 ]; then`, `${indent} tar -xf "$__t" -C / "\${__abs_ops[@]}" || __rc=$?`, `${indent} fi`, `${indent} if [ "$__rc" -eq 0 ] && [ \${#__rel_ops[@]} -gt 0 ]; then`, `${indent} tar -xf "$__t" -C "$__ci_cache_root" "\${__rel_ops[@]}" || __rc=$?`, `${indent} fi`, `${indent} rm -f "$__t"`, `${indent} return "$__rc"`, `${indent}}`);
212
- return out;
213
- }
214
- /** The token `%{http_code}` is reported under; see `daemonCurl`. */
215
- const DAEMON_STATUS_TOKEN = 'ci_http_status';
216
- /**
217
- * Emits the `curl` invocation the cache steps talk to the daemon with. The
218
- * bearer token is per-build and only present on the Invarn runner, so it is
219
- * expanded conditionally and the same script works without one.
220
- *
221
- * Every request also reports the HTTP status it got, on **stderr** — the
222
- * response body is the tarball and goes to stdout, so a status written there
223
- * would corrupt it. `%{stderr}` is curl 7.63+ (2018); an older curl prints the
224
- * token literally instead of a number, which the reader below tolerates.
225
- *
226
- * `-f`, not `--fail-with-body`: on the pull side the error body would be piped
227
- * straight into `zstd`. The status is what distinguishes the answers anyway,
228
- * and it now arrives without the body.
229
- */
230
- function daemonCurl(args) {
231
- return `curl -fsS -w '%{stderr}${DAEMON_STATUS_TOKEN}=%{http_code}\\n' \${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"} ${args}`;
232
- }
233
- /**
234
- * Emitted once inside a daemon pull branch, before the first fetch: a file for
235
- * curl's stderr, and the reader that turns it into one line — or into nothing
236
- * at all when the daemon simply had no such entry.
237
- *
238
- * `{ curl | zstd | tar; } 2>/dev/null` used to swallow curl's message
239
- * entirely, so a 401, a 400, a 503, a 500 and a daemon that was not listening
240
- * all printed the same `No cache found for key: …` as an empty cache. That is
241
- * the same class as the push-side defect: the instrument reported the normal
242
- * case for every abnormal one.
243
- *
244
- * **A miss stays quiet and a failure gets loud.** A cold cache is the normal
245
- * case and must not start printing errors; the point is only that a broken
246
- * daemon stops looking like one.
247
- */
248
- function emitDaemonPullDiagnostics(indent = '') {
249
- return [
250
- `${indent}__ci_cache_diag="\${TMPDIR:-/tmp}/cibuild-cache-pull.$$.diag"`,
251
- `${indent}__ci_cache_daemon_said() {`,
252
- `${indent} local __said=''`,
253
- `${indent} if [ -s "$__ci_cache_diag" ]; then`,
254
- `${indent} __said=$(tr '\\n' ' ' < "$__ci_cache_diag")`,
255
- `${indent} fi`,
256
- `${indent} case "$__said" in`,
257
- // 404 is the daemon's answer for `cache_miss` and `no_cache_for_scope` —
258
- // an empty cache, which is normal. The second pattern reads curl's own
259
- // English for a curl too old to honour `%{stderr}`, so neither reader
260
- // failing alone can turn a cold cache noisy.
261
- `${indent} *${DAEMON_STATUS_TOKEN}=404*|*'error: 404'*) __said='' ;;`,
262
- `${indent} esac`,
263
- // Always exits 0. A non-zero return would be the assignment's status at
264
- // every call site, and under errexit that ends the step on a cache miss.
265
- `${indent} printf '%s' "\${__said:0:300}"`,
266
- `${indent}}`,
267
- ];
268
- }
269
- /**
270
- * The `else` arm of a daemon fetch: says why it did not restore, unless the
271
- * daemon simply had no such entry. Never fatal — a build that cannot read a
272
- * cache still builds, it just builds cold.
273
- */
274
- function emitDaemonPullFailureDiagnosis(indent, what) {
275
- return [
276
- `${indent}__ci_cache_why=$(__ci_cache_daemon_said)`,
277
- `${indent}if [ -n "$__ci_cache_why" ]; then`,
278
- `${indent} echo "Warning: cache-pull could not restore ${what} from the daemon — $__ci_cache_why"`,
279
- `${indent}fi`,
280
- ];
281
- }
282
- /**
283
- * The same request, keeping the response body when the daemon refuses.
284
- *
285
- * `-f` throws the body away, so a rejection reached the log as an exit code
286
- * and nothing else. The daemon distinguishes `invalid_cache_key` (400), a
287
- * retryable `cache_busy` (503) and an auth failure (401); all three arrived
288
- * as the same `curl: (22)` line under the same warning, which is how a
289
- * one-line bug stayed open behind it. Push only — on the pull side the body
290
- * would be piped into `zstd`.
291
- *
292
- * `--fail-with-body` is curl 7.76+ (2021); every runner image is well past
293
- * it. An older curl rejects the option and the step still degrades to
294
- * today's behaviour, with the reason in the message.
295
- */
296
- function daemonCurlKeepingErrorBody(args) {
297
- return `curl --fail-with-body -sS \${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"} ${args}`;
298
- }
299
- /**
300
- * The daemon half of `cache-push`, shared by both step forms.
301
- *
302
- * Shared because it did not used to be. The two forms carried byte-identical
303
- * copies of this block, and the explicit-key one reached it without ever
304
- * assigning the `CACHE_KEY` it interpolates — the preset form assigned it,
305
- * the other only assigned `CACHE_FILE`. There is no `set -u`, so the PUT went
306
- * to `<daemon>/cache/.tar.zst`, the daemon answered 400, and the step
307
- * reported success. Every curated Invarn template uses the explicit-key form,
308
- * so nothing was ever stored by any of them. One definition cannot diverge
309
- * from itself.
310
- *
311
- * The failure message names the stage that failed, which took a second fix.
312
- * `$( … 2>&1 )` captured curl's stdout, and `--fail-with-body` writes the
313
- * daemon's response body there on success as well as on refusal — so when the
314
- * *middle* stage died (no `zstd` on the image), pipefail took the else branch
315
- * and printed the daemon's **success** body as the reason the upload failed:
316
- *
317
- * Warning: failed to upload cache to the daemon — {"ok":true}
318
- *
319
- * No stream separates those two cases, because the body is on stdout either
320
- * way; only the stage's exit status does. Hence `PIPESTATUS` rather than the
321
- * pull side's diag-file split — the file is still here, but it is the daemon's
322
- * answer, and it is quoted only when curl is what refused. A rejection's
323
- * reason still reaches the log, which `cache-wire-test.sh` asserts.
324
- */
325
- function emitDaemonPushCommands() {
326
- return [
327
- // curl's own stdout and stderr, kept out of the pipeline's status. Mirrors
328
- // the pull side's `$__ci_cache_diag`.
329
- ' __ci_push_out="${TMPDIR:-/tmp}/cibuild-cache-push.$$.out"',
330
- ` if tar -cf - "\${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 | ${daemonCurlKeepingErrorBody('-T - "$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst"')} >"$__ci_push_out" 2>&1; then`,
331
- ' echo "Cache created successfully"',
332
- ' else',
333
- // Read first, before anything else in this branch: PIPESTATUS holds the
334
- // last pipeline's statuses and the next command replaces it.
335
- ' __ci_push_rc=("${PIPESTATUS[@]}")',
336
- // Never fatal: a build that produced good output should not be failed by
337
- // a cache upload, and the next build simply misses. Saying *why* costs
338
- // nothing and is the difference between a warning and a diagnosis.
339
- ' __ci_push_msg=""',
340
- ' if [ "${__ci_push_rc[2]}" != 0 ]; then',
341
- // The daemon refused, so the daemon's own answer is the reason —
342
- // `invalid_cache_key`, `cache_busy`, `empty_body`, an auth failure.
343
- // `[ -s ]` guards the substitution: an assignment whose command
344
- // substitution fails is a non-zero statement, and under errexit that ends
345
- // the step.
346
- ' if [ -s "$__ci_push_out" ]; then',
347
- " __ci_push_msg=$(tr '\\n' ' ' < \"$__ci_push_out\")",
348
- ' fi',
349
- ' elif [ "${__ci_push_rc[1]}" = 127 ]; then',
350
- // curl was happy — it uploaded whatever reached it, which was nothing.
351
- // 127 is the shell failing to find the command, and zstd itself never
352
- // exits 127, so this is unambiguous and worth saying outright.
353
- ' __ci_push_msg="zstd is not installed on this image, so nothing was uploaded"',
354
- ' else',
355
- ' __ci_push_msg="nothing was uploaded — tar exited ${__ci_push_rc[0]}, zstd exited ${__ci_push_rc[1]}"',
356
- ' fi',
357
- ' __ci_push_msg=${__ci_push_msg:0:300}',
358
- ' if [ -n "$__ci_push_msg" ]; then',
359
- ' echo "Warning: failed to upload cache to the daemon — $__ci_push_msg"',
360
- ' else',
361
- ' echo "Warning: failed to upload cache to the daemon"',
362
- ' fi',
363
- ' fi',
364
- ' rm -f "$__ci_push_out"',
365
- ];
366
- }
367
- /**
368
- * Restores a cache through the daemon rather than a shared directory.
369
- *
370
- * Two requests, mirroring the directory-based flow it replaces: the exact key
371
- * first, then — on a miss — the scope's newest tarball. The daemon resolves
372
- * each from its own disk or a tailnet peer, so a hit here covers what used to
373
- * be separate "local" and "peer" branches; which one it was is recorded in the
374
- * daemon's own host-side log, where the fleet-wide view already lives.
375
- *
376
- * The 30-day age cap is preserved: the daemon dates the fallback with
377
- * `mtimeMs`, standing in for the `stat` the guest can no longer perform.
378
- */
379
- function emitDaemonPullCommands() {
380
- return [
381
- 'echo "Cache daemon: $CIBUILD_CACHE_DAEMON"',
382
- ...emitDaemonPullDiagnostics(),
383
- // Exact key. zstd's and tar's stderr is silenced — a miss is the normal
384
- // case and they both editorialise about it; pipefail still carries the
385
- // exit status. curl's own stderr is kept, in the file the diagnostics
386
- // above read: it is the only thing that knows *why*.
387
- `if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`,
388
- ' echo "Cache found (daemon), extracting..."',
389
- ' echo "CACHE_SOURCE=daemon CACHE_KEY=$CACHE_KEY"',
390
- 'else',
391
- // Said before the fallback is attempted, so the reason names the request
392
- // it belongs to rather than whichever request happened to fail last.
393
- ...emitDaemonPullFailureDiagnosis(' ', '$CACHE_KEY'),
394
- // Scope fallback: the newest tarball for this <keyPrefix>-<projectId>.
395
- // Gradle/Xcode re-hash their inputs on top of the warm directory, hitting
396
- // the unchanged work and rebuilding only what actually moved.
397
- ' __ci_fb_scope="${CACHE_KEY%-*}"',
398
- ` __ci_fb_json=$(${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/scope/$__ci_fb_scope/latest"')} 2>/dev/null || true)`,
399
- // Parse without a JSON tool: the guest image is not guaranteed to ship jq.
400
- ` __ci_fb_key=$(printf '%s' "$__ci_fb_json" | sed -n 's/.*"key"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
401
- ` __ci_fb_mtime=$(printf '%s' "$__ci_fb_json" | sed -n 's/.*"mtimeMs"[[:space:]]*:[[:space:]]*\\([0-9]*\\).*/\\1/p')`,
402
- ' if [ -n "$__ci_fb_key" ] && [ -n "$__ci_fb_mtime" ]; then',
403
- ' __ci_fb_age_days=$(( ($(date +%s) - __ci_fb_mtime / 1000) / 86400 ))',
404
- // Age cap: a fallback older than 30d likely carries obsolete toolchain
405
- // installs or cache entries from a pre-wrapper-bump world. Go cold rather
406
- // than seed from ancient state. Exact-key hits are unaffected.
407
- ' if [ "$__ci_fb_age_days" -le 30 ]; then',
408
- ' echo "Cache fallback (daemon): using prior tarball $__ci_fb_key (${__ci_fb_age_days}d old)"',
409
- ` if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$__ci_fb_key.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`,
410
- ' echo "CACHE_SOURCE=fallback_daemon CACHE_KEY=$CACHE_KEY FALLBACK_KEY=$__ci_fb_key"',
411
- ' else',
412
- ' echo "Cache fallback $__ci_fb_key could not be fetched — going cold"',
413
- ...emitDaemonPullFailureDiagnosis(' ', '$__ci_fb_key'),
414
- ' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"',
415
- ' fi',
416
- ' else',
417
- ' echo "Cache fallback candidate $__ci_fb_key is ${__ci_fb_age_days}d old, exceeds 30d cap — going cold"',
418
- ' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY FALLBACK_REJECTED=$__ci_fb_key"',
419
- ' fi',
420
- ' else',
421
- ' echo "No cache found for key: $CACHE_KEY"',
422
- ' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"',
423
- ' fi',
424
- 'fi',
425
- 'rm -f "$__ci_cache_diag"',
426
- ];
427
- }
428
- /**
429
- * Cache pull step executor
430
- * Restores cached files from cache directory
431
- */
432
- export class CachePullStepExecutor extends BaseStepExecutor {
433
- async execute(inputs, env, config) {
434
- const stepName = 'cache-pull';
435
- if (config.local) {
436
- const script = this.createBashScriptFromCommands(['echo "⏭️ Cache pull skipped (local mode)"'], stepName);
437
- return this.createScriptStep(script, stepName);
438
- }
439
- // Check for technology preset
440
- const technology = this.getInput(inputs, 'technology', '').trim();
441
- if (technology) {
442
- return this.executeWithPreset(technology, inputs, config);
443
- }
444
- // Get cache key - optional (skip if not provided)
445
- const cacheKey = this.getInput(inputs, 'cache_key', '');
446
- // Skip if no cache key provided
447
- if (!cacheKey || cacheKey.trim() === '') {
448
- const commands = [];
449
- commands.push('# Cache Pull - Skipped');
450
- commands.push('echo "⏭️ Cache pull skipped: no cache_key configured"');
451
- const script = this.createBashScriptFromCommands(commands, stepName);
452
- return this.createScriptStep(script, stepName);
453
- }
454
- // Get cache paths - defaults to empty array (can be string or array)
455
- const cachePathsInput = this.getInput(inputs, 'cache_paths', []);
456
- // Normalize to array (handle single string input)
457
- const cachePaths = Array.isArray(cachePathsInput)
458
- ? cachePathsInput
459
- : (typeof cachePathsInput === 'string' && cachePathsInput.trim() !== '')
460
- ? [cachePathsInput]
461
- : [];
462
- // Get debug mode flag
463
- const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
464
- const commands = [];
465
- commands.push('# Cache Pull');
466
- // Create cache directory if it doesn't exist
467
- commands.push('');
468
- commands.push('# Ensure cache directory exists');
469
- commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
470
- // Only the filesystem transport needs the directory to exist. Over the
471
- // daemon the cache lives on the host, and on a runner this path is no
472
- // longer mounted into the VM — an unconditional mkdir would abort the
473
- // whole step under errexit.
474
- commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
475
- // Project scope: an explicit `cache_key` is author-written and carries no
476
- // repo identity, so two projects that happen to write the same key (e.g.
477
- // `gradle-{{checksum "gradle/wrapper/gradle-wrapper.properties"}}`, which
478
- // depends only on the Gradle version) would share one tarball on a shared
479
- // runner. Prefixing the project id keeps each repo's cache its own.
480
- //
481
- // The id goes at the FRONT, deliberately. The preset path spells its key
482
- // `<keyPrefix>-<projectId>-<checksum>`, and that middle-segment shape is
483
- // load-bearing elsewhere: it is how a scope is recovered from a tarball
484
- // filename. An explicit key is author-written and must stay outside the
485
- // scope model, so it must not be able to imitate that shape by accident.
486
- commands.push('');
487
- commands.push(...emitProjectScopeCommands());
488
- // Generate cache file name based on cache key
489
- commands.push('');
490
- commands.push('# Generate cache file path');
491
- commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
492
- commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
493
- commands.push('echo "Cache key: $CACHE_KEY"');
494
- if (isDebugMode) {
495
- commands.push('echo "Debug mode enabled"');
496
- commands.push('echo "Cache file: $CACHE_FILE"');
497
- }
498
- // Peer cache directory
499
- const peerCacheDir = config.paths.peerCacheDir || '';
500
- if (peerCacheDir) {
501
- commands.push('');
502
- commands.push(`PEER_CACHE_DIR="${this.escapeBash(peerCacheDir)}"`);
503
- commands.push('PEER_CACHE_FILE="$PEER_CACHE_DIR/$CACHE_KEY.tar.zst"');
504
- }
505
- // Where each class of member is restored to. Emitted before every
506
- // transport, because all of them extract.
507
- commands.push('');
508
- commands.push('# Restore roots');
509
- commands.push(...emitCacheExtractHelper(cachePaths, config));
510
- // Check if cache file exists
511
- commands.push('');
512
- commands.push('# Check if cache exists');
513
- // Transport split — see the preset path. An explicit-key step has no scope
514
- // to fall back to, so the daemon side is the exact key or nothing.
515
- commands.push('if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
516
- commands.push(...emitDaemonPullDiagnostics(' '));
517
- commands.push(` if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`);
518
- commands.push(' echo "Cache found (daemon), extracting..."');
519
- commands.push(' echo "CACHE_SOURCE=daemon"');
520
- commands.push(' else');
521
- commands.push(...emitDaemonPullFailureDiagnosis(' ', '$CACHE_KEY'));
522
- commands.push(' echo "No cache found for key: $CACHE_KEY"');
523
- commands.push(' echo "CACHE_SOURCE=cold"');
524
- commands.push(' fi');
525
- commands.push(' rm -f "$__ci_cache_diag"');
526
- commands.push('else');
527
- commands.push('if [ -f "$CACHE_FILE" ]; then');
528
- commands.push(' echo "Cache found (local), extracting..."');
529
- if (isDebugMode) {
530
- commands.push(' echo "Cache file size: $(du -h "$CACHE_FILE" | cut -f1)"');
531
- }
532
- commands.push(' zstd -dc "$CACHE_FILE" | __ci_cache_extract');
533
- commands.push(' echo "CACHE_SOURCE=local"');
534
- // Peer fallback
535
- if (peerCacheDir) {
536
- commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
537
- commands.push(' echo "Cache found (peer), extracting..."');
538
- commands.push(' tee "$CACHE_FILE.tmp" < "$PEER_CACHE_FILE" | zstd -dc | __ci_cache_extract');
539
- commands.push(' mv "$CACHE_FILE.tmp" "$CACHE_FILE"');
540
- commands.push(' echo "CACHE_SOURCE=peer"');
541
- }
542
- commands.push('else');
543
- commands.push(' echo "No cache found for key: $CACHE_KEY"');
544
- commands.push(' echo "CACHE_SOURCE=cold"');
545
- commands.push('fi');
546
- // Closes the daemon/filesystem transport split.
547
- commands.push('fi');
548
- const script = this.createBashScriptFromCommands(commands, stepName);
549
- return this.createScriptStep(script, stepName);
550
- }
551
- async executeWithPreset(technology, inputs, config) {
552
- const stepName = 'cache-pull';
553
- const chain = resolvePresetChain(technology);
554
- if (chain.length === 0) {
555
- const supported = Object.keys(CACHE_PRESETS).join(', ');
556
- throw new Error(`Unknown cache technology '${technology}'. Supported: ${supported}`);
557
- }
558
- const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
559
- const commands = [];
560
- commands.push(`# Cache Pull (${technology})`);
561
- commands.push(`echo "Restoring ${technology} cache..."`);
562
- commands.push('');
563
- // Compute cache key from lockfile checksum at runtime
564
- // If technology has a fallback chain, try each lockfile in order
565
- commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
566
- // Only the filesystem transport needs the directory to exist. Over the
567
- // daemon the cache lives on the host, and on a runner this path is no
568
- // longer mounted into the VM — an unconditional mkdir would abort the
569
- // whole step under errexit.
570
- commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
571
- commands.push('');
572
- // Helper: resolve lockfile path — checks exact path first, then searches
573
- // recursively (handles SPM Package.resolved inside .xcodeproj/.xcworkspace)
574
- commands.push('__ci_find_lockfile() {');
575
- commands.push(' if [ -f "$1" ]; then echo "$1"; return; fi');
576
- commands.push(' find . -name "$(basename "$1")" -not -path "*/DerivedData/*" -not -path "*/.build/*" 2>/dev/null | head -1');
577
- commands.push('}');
578
- commands.push('');
579
- // Project scope: include a stable short hash of the git remote in every
580
- // cache key so different repos on the same runner don't share tarballs
581
- // (prevents fallback cross-contamination across projects).
582
- commands.push(...emitProjectScopeCommands());
583
- commands.push('');
584
- if (chain.length === 1 && chain[0].fingerprint && chain[0].fingerprint.length > 0) {
585
- commands.push(...emitFingerprintKeyCommands(chain[0]));
586
- }
587
- else if (chain.length === 1) {
588
- const preset = chain[0];
589
- commands.push(`LOCKFILE=$(__ci_find_lockfile "${this.escapeBash(preset.lockfile)}")`);
590
- commands.push('if [ -n "$LOCKFILE" ] && [ -f "$LOCKFILE" ]; then');
591
- commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
592
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
593
- commands.push('else');
594
- commands.push(` echo "Warning: ${this.escapeBash(preset.lockfile)} not found, using fallback cache key"`);
595
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-no-lockfile"`);
596
- commands.push('fi');
597
- }
598
- else {
599
- // Fallback chain: probe every lockfile up front, then branch on the
600
- // results. The probes must precede the if/elif — emitting a probe
601
- // inside a prior preset's `then` body (the old shape) meant the
602
- // second lockfile was only checked when the FIRST one was found, so
603
- // an SPM project with no Podfile.lock never reached the Package.resolved
604
- // branch and fell through to `<first>-no-lockfile`.
605
- for (let i = 0; i < chain.length; i++) {
606
- commands.push(`__ci_lockfile_${i}=$(__ci_find_lockfile "${this.escapeBash(chain[i].lockfile)}")`);
607
- }
608
- for (let i = 0; i < chain.length; i++) {
609
- const preset = chain[i];
610
- const cond = i === 0 ? 'if' : 'elif';
611
- commands.push(`${cond} [ -n "$__ci_lockfile_${i}" ] && [ -f "$__ci_lockfile_${i}" ]; then`);
612
- commands.push(` LOCKFILE="$__ci_lockfile_${i}"`);
613
- commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
614
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
615
- if (isDebugMode) {
616
- commands.push(` echo "Detected: ${this.escapeBash(preset.keyPrefix)} (lockfile: $LOCKFILE)"`);
617
- }
618
- }
619
- commands.push('else');
620
- commands.push(` echo "Warning: no lockfile found, using fallback cache key"`);
621
- commands.push(` CACHE_KEY="${this.escapeBash(chain[0].keyPrefix)}-\${__ci_proj}-no-lockfile"`);
622
- commands.push('fi');
623
- }
624
- commands.push('');
625
- if (isDebugMode) {
626
- commands.push('echo "Cache key: $CACHE_KEY"');
627
- }
628
- commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
629
- commands.push('');
630
- // Where each class of member is restored to. Every path below extracts, so
631
- // this is emitted once, above the transport split. `allPaths` is the same
632
- // union cache-push archives from, which is what keeps the two sides
633
- // agreeing about which class a path is in.
634
- commands.push('# Restore roots');
635
- commands.push(...emitCacheExtractHelper([...new Set(chain.flatMap(p => p.paths))], config));
636
- commands.push('');
637
- // Transport split. On a runner the cache lives on the host and is reached
638
- // over HTTP; standalone, it is a directory on this machine. The daemon URL
639
- // is the switch, so the same generated script serves both.
640
- commands.push('if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
641
- commands.push(...emitDaemonPullCommands());
642
- commands.push('else');
643
- // Peer cache directory — set from config, empty string disables peer lookup
644
- const peerCacheDir = config.paths.peerCacheDir || '';
645
- if (peerCacheDir) {
646
- commands.push(`PEER_CACHE_DIR="${this.escapeBash(peerCacheDir)}"`);
647
- commands.push('PEER_CACHE_FILE="$PEER_CACHE_DIR/$CACHE_KEY.tar.zst"');
648
- commands.push('');
649
- }
650
- // 1. Local hit
651
- commands.push('if [ -f "$CACHE_FILE" ]; then');
652
- commands.push(' echo "Cache found (local), extracting..."');
653
- if (isDebugMode) {
654
- commands.push(' echo "Cache file size: $(du -h "$CACHE_FILE" | cut -f1)"');
655
- }
656
- commands.push(' zstd -dc "$CACHE_FILE" | __ci_cache_extract');
657
- // LRU bump: lift this tarball to the top of `ls -t` so retention's age
658
- // cap counts last-used time, not last-written. Without this, a tarball
659
- // hit daily would still be reaped on its 31st day from creation.
660
- commands.push(' touch "$CACHE_FILE"');
661
- commands.push(' echo "CACHE_SOURCE=local CACHE_KEY=$CACHE_KEY"');
662
- // 2. Peer hit — tee to warm local cache while extracting in one pass
663
- if (peerCacheDir) {
664
- commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
665
- commands.push(' echo "Cache found (peer), extracting..."');
666
- if (isDebugMode) {
667
- commands.push(' echo "Peer cache file size: $(du -h "$PEER_CACHE_FILE" | cut -f1)"');
668
- }
669
- // Single NFS read: tee forks the stream to local cache file AND decompression
670
- commands.push(' tee "$CACHE_FILE.tmp" < "$PEER_CACHE_FILE" | zstd -dc | __ci_cache_extract');
671
- commands.push(' mv "$CACHE_FILE.tmp" "$CACHE_FILE"');
672
- commands.push(' echo "CACHE_SOURCE=peer CACHE_KEY=$CACHE_KEY"');
673
- }
674
- // 3. Fallback — no exact-key tarball, try the newest prior tarball scoped
675
- // to the same <keyPrefix>-<projectId> (so fallback stays within this repo
676
- // and doesn't pick up a different KMM project's cache). Gradle will
677
- // re-hash task inputs on top of the warm ~/.gradle/caches directory,
678
- // hitting the unchanged tasks and recompiling only what actually changed.
679
- // Deliberately does NOT warm $CACHE_FILE — that way cache-push still runs
680
- // and creates a fresh tarball under the new key for future exact hits.
681
- commands.push('else');
682
- commands.push(' __ci_fb_scope="${CACHE_KEY%-*}"');
683
- // `|| true` defuses pipefail+errexit when ls finds no matches: bare
684
- // assignment would abort the whole cache-pull step silently on an empty
685
- // cache dir, flipping any cold miss into a non-zero exit.
686
- commands.push(' __ci_fb=$(ls -t "$CACHE_DIR"/"$__ci_fb_scope"-*.tar.zst 2>/dev/null | head -1 || true)');
687
- if (peerCacheDir) {
688
- commands.push(' if [ -z "$__ci_fb" ] || [ ! -f "$__ci_fb" ]; then');
689
- commands.push(' __ci_fb=$(ls -t "$PEER_CACHE_DIR"/"$__ci_fb_scope"-*.tar.zst 2>/dev/null | head -1 || true)');
690
- commands.push(' __ci_fb_src=peer');
691
- commands.push(' else');
692
- commands.push(' __ci_fb_src=local');
693
- commands.push(' fi');
694
- }
695
- else {
696
- commands.push(' __ci_fb_src=local');
697
- }
698
- commands.push(' if [ -n "$__ci_fb" ] && [ -f "$__ci_fb" ]; then');
699
- commands.push(' __ci_fb_key=$(basename "$__ci_fb" .tar.zst)');
700
- // Age cap: a fallback older than 30d likely carries obsolete .konan
701
- // installs or Gradle entries from a pre-wrapper-bump world. Go cold
702
- // rather than seed from ancient state. Exact-key hits are unaffected.
703
- commands.push(' __ci_fb_age_days=$(( ($(date +%s) - $(stat -f %m "$__ci_fb")) / 86400 ))');
704
- commands.push(' if [ "$__ci_fb_age_days" -le 30 ]; then');
705
- commands.push(' echo "Cache fallback ($__ci_fb_src): using prior tarball $__ci_fb_key (${__ci_fb_age_days}d old)"');
706
- if (isDebugMode) {
707
- commands.push(' echo "Fallback size: $(du -h "$__ci_fb" | cut -f1)"');
708
- }
709
- commands.push(' zstd -dc "$__ci_fb" | __ci_cache_extract');
710
- // LRU bump for the fallback tarball — we restored from it, so it earns
711
- // its place at the top of the retention sort even if the exact-key
712
- // tarball is the one that gets created in this run's cache-push.
713
- commands.push(' touch "$__ci_fb"');
714
- commands.push(' echo "CACHE_SOURCE=fallback_$__ci_fb_src CACHE_KEY=$CACHE_KEY FALLBACK_KEY=$__ci_fb_key"');
715
- commands.push(' else');
716
- commands.push(' echo "Cache fallback candidate $__ci_fb_key is ${__ci_fb_age_days}d old, exceeds 30d cap — going cold"');
717
- commands.push(' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY FALLBACK_REJECTED=$__ci_fb_key"');
718
- commands.push(' fi');
719
- commands.push(' else');
720
- // 4. Cold miss — no exact key, no fallback candidate
721
- commands.push(' echo "No cache found for key: $CACHE_KEY"');
722
- commands.push(' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"');
723
- commands.push(' fi');
724
- commands.push('fi');
725
- // Closes the daemon/filesystem transport split.
726
- commands.push('fi');
727
- if (isDebugMode) {
728
- commands.push('echo "Restored paths:"');
729
- const allPaths = [...new Set(chain.flatMap(p => p.paths))];
730
- for (const p of allPaths) {
731
- commands.push(`EXPANDED="${this.escapeBash(p)}"`);
732
- commands.push('EXPANDED="${EXPANDED/#~/${CIBUILD_USER_HOME:-$HOME}}"');
733
- commands.push('if [ -e "$EXPANDED" ]; then');
734
- commands.push(' echo " ✓ $EXPANDED"');
735
- commands.push('else');
736
- commands.push(' echo " ✗ $EXPANDED (not found)"');
737
- commands.push('fi');
738
- }
739
- }
740
- const script = this.createBashScriptFromCommands(commands, stepName);
741
- return this.createScriptStep(script, stepName);
742
- }
743
- }
744
- /**
745
- * Cache push step executor
746
- * Saves files to cache directory
747
- */
748
- export class CachePushStepExecutor extends BaseStepExecutor {
749
- async execute(inputs, env, config) {
750
- const stepName = 'cache-push';
751
- if (config.local) {
752
- const script = this.createBashScriptFromCommands(['echo "⏭️ Cache push skipped (local mode)"'], stepName);
753
- return this.createScriptStep(script, stepName);
754
- }
755
- // Check for technology preset
756
- const technology = this.getInput(inputs, 'technology', '').trim();
757
- if (technology) {
758
- return this.executeWithPreset(technology, inputs, config);
759
- }
760
- // Get cache key - optional (skip if not provided)
761
- const cacheKey = this.getInput(inputs, 'cache_key', '');
762
- // Skip if no cache key provided
763
- if (!cacheKey || cacheKey.trim() === '') {
764
- const commands = [];
765
- commands.push('# Cache Push - Skipped');
766
- commands.push('echo "⏭️ Cache push skipped: no cache_key configured"');
767
- const script = this.createBashScriptFromCommands(commands, stepName);
768
- return this.createScriptStep(script, stepName);
769
- }
770
- // Get cache paths - required (can be string or array)
771
- const cachePathsInput = this.getInput(inputs, 'cache_paths', []);
772
- // Normalize to array (handle single string input)
773
- const cachePaths = Array.isArray(cachePathsInput)
774
- ? cachePathsInput
775
- : (typeof cachePathsInput === 'string' && cachePathsInput.trim() !== '')
776
- ? [cachePathsInput]
777
- : [];
778
- if (cachePaths.length === 0) {
779
- throw new Error(`Missing or empty 'cache_paths' for step '${stepName}'`);
780
- }
781
- // Get debug mode flag
782
- const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
783
- // Get ignore check flag
784
- const ignoreCheckOnPaths = this.getInput(inputs, 'ignore_check_on_paths', false);
785
- const commands = [];
786
- commands.push('# Cache Push');
787
- // Create cache directory if it doesn't exist
788
- commands.push('');
789
- commands.push('# Ensure cache directory exists');
790
- commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
791
- // Only the filesystem transport needs the directory to exist. Over the
792
- // daemon the cache lives on the host, and on a runner this path is no
793
- // longer mounted into the VM — an unconditional mkdir would abort the
794
- // whole step under errexit.
795
- commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
796
- // Project scope — must match the pull side exactly, or a push writes a
797
- // tarball no pull can ever find. See the note there for why an explicit
798
- // key needs the repo identity prefixed.
799
- commands.push('');
800
- commands.push(...emitProjectScopeCommands());
801
- // Generate cache file name based on cache key
802
- commands.push('');
803
- commands.push('# Generate cache file path');
804
- // CACHE_KEY first, CACHE_FILE derived from it — exactly as the pull side
805
- // does. This step used to set only CACHE_FILE while the daemon URL below
806
- // interpolated `$CACHE_KEY`; see emitDaemonPushCommands.
807
- commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
808
- commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
809
- commands.push('echo "Cache key: $CACHE_KEY"');
810
- if (isDebugMode) {
811
- commands.push('echo "Debug mode enabled"');
812
- commands.push('echo "Cache file: $CACHE_FILE"');
813
- }
814
- // Check which paths exist
815
- commands.push('');
816
- commands.push('# Check which paths exist');
817
- commands.push('PATHS_TO_CACHE=()');
818
- for (const path of cachePaths) {
819
- // Resolve path identifiers (e.g., "derived_data:pattern" -> "~/Library/.../DerivedData/pattern").
820
- // Shared with cache-pull's member classification: if only one side
821
- // resolved these, the two would disagree about which restore root the
822
- // path belongs to.
823
- const resolvedPath = resolveCachePathIdentifier(path, config);
824
- // Expand tilde by using parameter expansion
825
- commands.push(`EXPANDED_PATH="${this.escapeBash(resolvedPath)}"`);
826
- commands.push('EXPANDED_PATH="${EXPANDED_PATH/#~/${CIBUILD_USER_HOME:-$HOME}}"');
827
- commands.push('');
828
- commands.push('# Expand glob patterns and check each match');
829
- commands.push('FOUND_MATCH=false');
830
- commands.push('shopt -s nullglob');
831
- commands.push('for path_match in $EXPANDED_PATH; do');
832
- commands.push(' if [ -e "$path_match" ]; then');
833
- commands.push(' PATHS_TO_CACHE+=("$path_match")');
834
- commands.push(' FOUND_MATCH=true');
835
- if (isDebugMode) {
836
- commands.push(' echo "Found: $path_match"');
837
- }
838
- commands.push(' fi');
839
- commands.push('done');
840
- commands.push('shopt -u nullglob');
841
- commands.push('');
842
- commands.push('if [ "$FOUND_MATCH" = false ]; then');
843
- if (isDebugMode) {
844
- commands.push(` echo "Not found: $EXPANDED_PATH"`);
845
- }
846
- if (!ignoreCheckOnPaths) {
847
- commands.push(` echo "Warning: Path does not exist: $EXPANDED_PATH"`);
848
- }
849
- commands.push('fi');
850
- }
851
- // Create cache if we have paths
852
- commands.push('');
853
- commands.push('# Skip if cache already exists for this key');
854
- // Over the daemon there is no local file to test, so the "already up to
855
- // date" shortcut can't apply — the daemon's PUT is an idempotent overwrite
856
- // under the same key, which gets the same outcome.
857
- commands.push('if [ -z "$CIBUILD_CACHE_DAEMON" ] && [ -f "$CACHE_FILE" ]; then');
858
- commands.push(' echo "Cache already up to date for key: $CACHE_KEY"');
859
- commands.push('elif [ ${#PATHS_TO_CACHE[@]} -gt 0 ]; then');
860
- commands.push(' echo "Caching ${#PATHS_TO_CACHE[@]} path(s)..."');
861
- if (isDebugMode) {
862
- commands.push(' echo "Paths to cache:"');
863
- commands.push(' printf " %s\\n" "${PATHS_TO_CACHE[@]}"');
864
- }
865
- // Transport split — see the preset path.
866
- commands.push(' if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
867
- commands.push(...emitDaemonPushCommands());
868
- commands.push(' else');
869
- // Atomic write into a per-process-unique temp (CBR-12). The "runs serialized
870
- // per runner" assumption behind a fixed "$CACHE_FILE.tmp" is false with two VM
871
- // slots and collides with the host cache daemon mutating this VirtioFS-shared
872
- // dir; a unique name (PID + $RANDOM) removes the clash. The atomic mv is kept,
873
- // and a failed compress/mv removes its own temp (no orphan) and stays non-fatal.
874
- commands.push(' CACHE_TMP="$CACHE_FILE.$$.$RANDOM.tmp"');
875
- commands.push(' tar -cf - "${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 > "$CACHE_TMP" && mv "$CACHE_TMP" "$CACHE_FILE" || rm -f "$CACHE_TMP"');
876
- commands.push(' if [ -f "$CACHE_FILE" ]; then');
877
- commands.push(' echo "Cache created successfully"');
878
- if (isDebugMode) {
879
- commands.push(' echo "Cache size: $(du -h "$CACHE_FILE" | cut -f1)"');
880
- }
881
- commands.push(' else');
882
- commands.push(' echo "Warning: Failed to create cache file"');
883
- commands.push(' fi');
884
- // Closes the daemon/filesystem transport split.
885
- commands.push(' fi');
886
- commands.push('else');
887
- commands.push(' echo "No paths to cache"');
888
- commands.push('fi');
889
- const script = this.createBashScriptFromCommands(commands, stepName);
890
- return this.createScriptStep(script, stepName);
891
- }
892
- async executeWithPreset(technology, inputs, config) {
893
- const stepName = 'cache-push';
894
- const chain = resolvePresetChain(technology);
895
- if (chain.length === 0) {
896
- const supported = Object.keys(CACHE_PRESETS).join(', ');
897
- throw new Error(`Unknown cache technology '${technology}'. Supported: ${supported}`);
898
- }
899
- const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
900
- const commands = [];
901
- commands.push(`# Cache Push (${technology})`);
902
- commands.push(`echo "Saving ${technology} cache..."`);
903
- commands.push('');
904
- // Compute cache key from lockfile checksum at runtime
905
- commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
906
- // Only the filesystem transport needs the directory to exist. Over the
907
- // daemon the cache lives on the host, and on a runner this path is no
908
- // longer mounted into the VM — an unconditional mkdir would abort the
909
- // whole step under errexit.
910
- commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
911
- commands.push('');
912
- commands.push('__ci_find_lockfile() {');
913
- commands.push(' if [ -f "$1" ]; then echo "$1"; return; fi');
914
- commands.push(' find . -name "$(basename "$1")" -not -path "*/DerivedData/*" -not -path "*/.build/*" 2>/dev/null | head -1');
915
- commands.push('}');
916
- commands.push('');
917
- // Project scope: include a stable short hash of the git remote in every
918
- // cache key so different repos on the same runner don't share tarballs.
919
- commands.push(...emitProjectScopeCommands());
920
- commands.push('');
921
- if (chain.length === 1 && chain[0].fingerprint && chain[0].fingerprint.length > 0) {
922
- commands.push(...emitFingerprintKeyCommands(chain[0]));
923
- }
924
- else if (chain.length === 1) {
925
- const preset = chain[0];
926
- commands.push(`LOCKFILE=$(__ci_find_lockfile "${this.escapeBash(preset.lockfile)}")`);
927
- commands.push('if [ -n "$LOCKFILE" ] && [ -f "$LOCKFILE" ]; then');
928
- commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
929
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
930
- commands.push('else');
931
- commands.push(` echo "Warning: ${this.escapeBash(preset.lockfile)} not found, using fallback cache key"`);
932
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-no-lockfile"`);
933
- commands.push('fi');
934
- }
935
- else {
936
- // Fallback chain: probe every lockfile up front, then branch — same
937
- // fix as cache-pull. A probe nested in a prior preset's `then` body
938
- // only ran when the first lockfile was found, so push wrote the
939
- // `<first>-no-lockfile` key for SPM projects, mismatching what a
940
- // correct pull would look for.
941
- for (let i = 0; i < chain.length; i++) {
942
- commands.push(`__ci_lockfile_${i}=$(__ci_find_lockfile "${this.escapeBash(chain[i].lockfile)}")`);
943
- }
944
- for (let i = 0; i < chain.length; i++) {
945
- const preset = chain[i];
946
- const cond = i === 0 ? 'if' : 'elif';
947
- commands.push(`${cond} [ -n "$__ci_lockfile_${i}" ] && [ -f "$__ci_lockfile_${i}" ]; then`);
948
- commands.push(` LOCKFILE="$__ci_lockfile_${i}"`);
949
- commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
950
- commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
951
- }
952
- commands.push('else');
953
- commands.push(` echo "Warning: no lockfile found, using fallback cache key"`);
954
- commands.push(` CACHE_KEY="${this.escapeBash(chain[0].keyPrefix)}-\${__ci_proj}-no-lockfile"`);
955
- commands.push('fi');
956
- }
957
- commands.push('');
958
- if (isDebugMode) {
959
- commands.push('echo "Cache key: $CACHE_KEY"');
960
- }
961
- commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
962
- commands.push('');
963
- // Collect paths to cache — use all paths from all presets in the chain,
964
- // only archiving the ones that actually exist on disk
965
- commands.push('# Collect paths to cache');
966
- commands.push('PATHS_TO_CACHE=()');
967
- const allPaths = [...new Set(chain.flatMap(p => p.paths))];
968
- for (const p of allPaths) {
969
- commands.push(`EXPANDED="${this.escapeBash(p)}"`);
970
- commands.push('EXPANDED="${EXPANDED/#~/${CIBUILD_USER_HOME:-$HOME}}"');
971
- commands.push('if [ -e "$EXPANDED" ]; then');
972
- commands.push(' PATHS_TO_CACHE+=("$EXPANDED")');
973
- if (isDebugMode) {
974
- commands.push(' echo "Found: $EXPANDED"');
975
- }
976
- commands.push('else');
977
- commands.push(` echo "Warning: Path does not exist: $EXPANDED"`);
978
- commands.push('fi');
979
- }
980
- commands.push('');
981
- // Always re-archive on success: ~/.gradle/caches and friends are
982
- // write-through accumulators — same fingerprint key does NOT imply
983
- // unchanged contents (Gradle's build-cache-1 keeps appending task
984
- // outputs as sources change). Skipping here would freeze the tarball
985
- // at first-mint and let it drift further from current state on every
986
- // build. Atomic .tmp + mv overwrites any prior tarball under this key.
987
- commands.push('if [ ${#PATHS_TO_CACHE[@]} -gt 0 ]; then');
988
- commands.push(' echo "Caching ${#PATHS_TO_CACHE[@]} path(s)..."');
989
- // Transport split, mirroring cache-pull. Over the daemon the tarball is
990
- // streamed straight out as the PUT body — nothing is staged on the guest,
991
- // so there is no temp file to collide or orphan, and the daemon does the
992
- // atomic temp+rename on the host side where the cache actually lives.
993
- commands.push(' if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
994
- commands.push(...emitDaemonPushCommands());
995
- commands.push(' else');
996
- // Per-process-unique temp (CBR-12). The old fixed "$CACHE_FILE.tmp" assumed
997
- // a single serialized writer — false with two VM slots, and it collides with
998
- // the host cache daemon mutating this VirtioFS-shared dir, which (a) made the
999
- // guest's own .tmp transiently vanish at `mv` (silent clobber, masked by
1000
- // `|| true`) and (b) dropped VirtioFS off its async fast path. A unique name
1001
- // removes the collision; the atomic mv into place is preserved, and a failed
1002
- // compress/mv removes its own temp (no orphan) while staying non-fatal.
1003
- commands.push(' CACHE_TMP="$CACHE_FILE.$$.$RANDOM.tmp"');
1004
- commands.push(' tar -cf - "${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 > "$CACHE_TMP" && mv "$CACHE_TMP" "$CACHE_FILE" || rm -f "$CACHE_TMP"');
1005
- commands.push(' if [ -f "$CACHE_FILE" ]; then');
1006
- commands.push(' echo "Cache created successfully"');
1007
- if (isDebugMode) {
1008
- commands.push(' echo "Cache size: $(du -h "$CACHE_FILE" | cut -f1)"');
1009
- }
1010
- // Retention is no longer applied here (ADR 0002 / CBR-6). It moved into the
1011
- // long-lived peer cache daemon, which owns ~/cache and — unlike the guest —
1012
- // knows (via the manager) which scopes this runner OWNS and must never evict.
1013
- // A guest-side prune would have to run blind to ownership and could reap the
1014
- // fleet's single durable copy, so the daemon owns the whole policy now.
1015
- commands.push(' else');
1016
- commands.push(' echo "Warning: Failed to create cache file"');
1017
- commands.push(' fi');
1018
- // Closes the daemon/filesystem transport split.
1019
- commands.push(' fi');
1020
- commands.push('else');
1021
- commands.push(' echo "No paths to cache"');
1022
- commands.push('fi');
1023
- const script = this.createBashScriptFromCommands(commands, stepName);
1024
- return this.createScriptStep(script, stepName);
1025
- }
1026
- }
1027
- //# sourceMappingURL=cache.js.map