@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,1389 +0,0 @@
1
- /**
2
- * ui-fidelity-render-android step implementation.
3
- *
4
- * Renders parameterless Jetpack Compose wrappers from a self-contained Gradle
5
- * project to PNGs via Roborazzi + Robolectric on the JVM — no device, no
6
- * emulator — on a macOS runner. The step returns a self-contained node script
7
- * that, at runtime:
8
- *
9
- * 1. Reads `.ci/inputs/params.json`
10
- * ({ "screens": { "<ScreenName>": "<referenceFileBasename>" } });
11
- * reference images live at `.ci/inputs/<basename>`.
12
- * Optional reference-provenance sidecar (manifest-driven multi-state path):
13
- * `"render_locale": "<locale>"` plus
14
- * `"provenance": { "<ScreenName>": { figmaNode, locale, resolved, exportedAt } }`.
15
- * The gate arms whenever a screen's provenance is present (or, legacy, when
16
- * `render_locale` is declared) and checks provenance METADATA only (never
17
- * pixels/OCR). An absent reference — or an unprovenanced screen while
18
- * `render_locale` is declared — is FATAL: status `unverifiable`, failing
19
- * loud instead of yielding a confident-but-false PASS. `resolved == false`
20
- * (placeholder copy) or `locale != render_locale` downgrade ONLY the copy
21
- * channel: the screen renders and is judged on geometry, colors, typeface,
22
- * spans, and lines, while copy comparisons are skipped and reported as a
23
- * per-screen `copy_advisory` (`copy_unverifiable` + reason) in
24
- * protocol-result.json. Content differences (copy, geometry) are NOT gated
25
- * here — they stay soft, judged findings.
26
- * 2. Extracts and validates the shipped Gradle project shipped as
27
- * `.ci/inputs/package.tar.gz` (single project root carrying a settings
28
- * script, bounded extracted size, safe member paths).
29
- * 3. Runs the project's Roborazzi record task (default
30
- * `recordRoborazziDebug`) once. A build/compile failure means the project
31
- * does not render and every screen is marked RENDER_UNSUPPORTED — the
32
- * Android analog of the iOS probe gate (one Gradle module compiles as a
33
- * unit, so a wrapper compile error cannot be isolated to one screen).
34
- * 4. Locates each screen's Roborazzi golden inside the project (recorded as
35
- * `build/outputs/roborazzi/...render<ScreenName>.png` from a test method
36
- * named render<ScreenName>, or a flat `<ScreenName>.png`), runs it through
37
- * the shared render-output
38
- * post-processor (content trim, reference alignment, dimension
39
- * reporting), and writes, inside the artifacts dir
40
- * (${CIBUILD_ARTIFACTS_DIR:-.ci/artifacts}):
41
- * - ui-fidelity/rendered/<Screen>.png
42
- * - ui-fidelity/references/<Screen>.png
43
- * - ui-fidelity/aligned/<Screen>.png
44
- * - protocol-result.json (always written, even on partial/total failure)
45
- * 5. Exits non-zero iff the archive was invalid or any screen is not
46
- * "rendered".
47
- *
48
- * The result payload and artifact layout are identical in shape to the iOS
49
- * ui-fidelity-render step, so the dashboard compare view, the artifact
50
- * download tool, and the agent's compare flow are reused unchanged. Image
51
- * paths in protocol-result.json are relative to the artifacts dir, and error
52
- * messages are sanitized so absolute runner paths are rewritten before they
53
- * are stored.
54
- */
55
- import { BaseStepExecutor } from './base.js';
56
- import { parseScale, DEFAULT_SCALE } from './ui-fidelity-render.js';
57
- import { RENDER_SCRIPT_TRIM_STAGE } from './render-post-processor.js';
58
- import { RENDER_SCRIPT_FACTS_STAGE, RENDER_SCRIPT_GEOMETRY_STAGE } from './structural-facts.js';
59
- import { RENDER_SCRIPT_SOURCE_DIGEST_STAGE } from '../fidelity-input-digest.js';
60
- /**
61
- * Default Roborazzi record task. Run from the project root so Gradle resolves
62
- * it in whichever subproject declares it.
63
- */
64
- export const DEFAULT_GRADLE_TASK = 'recordRoborazziDebug';
65
- /** Valid values for the package_source input. */
66
- export const PACKAGE_SOURCES = ['repo', 'inputs', 'url'];
67
- /**
68
- * Default package source: the Gradle project ships as package.tar.gz in the
69
- * run-inputs directory (the agent-upload path). "repo" instead renders the
70
- * committed project at package_path, for a triggered (agent-less) run.
71
- * "url" (fidelity-linux 03) downloads the same package.tar.gz from a signed
72
- * package_url into the run-inputs directory and then proceeds byte-for-byte
73
- * like inputs mode — the clone-free delivery for triggered runs, on macOS
74
- * and Linux workers alike.
75
- */
76
- export const DEFAULT_PACKAGE_SOURCE = 'inputs';
77
- /** Valid values for the reference_source input. */
78
- export const REFERENCE_SOURCES = ['repo', 'inputs'];
79
- /**
80
- * Default reference source: references arrive as run inputs (a plain basename
81
- * under `.ci/inputs`), matching the agent-upload path.
82
- */
83
- export const DEFAULT_REFERENCE_SOURCE = 'inputs';
84
- /** Basename of the shipped Gradle-project archive inside the run-inputs dir. */
85
- export const PACKAGE_ARCHIVE_BASENAME = 'package.tar.gz';
86
- /** Renderer identifier recorded in protocol-result.json. */
87
- export const ANDROID_RENDERER = 'roborazzi-robolectric';
88
- /**
89
- * Validates a Gradle task name: a non-empty token of letters, digits, and the
90
- * separators Gradle allows in a task path (`:`, `-`, `_`). Rejects shell
91
- * metacharacters so the task is safe to pass to the runner.
92
- */
93
- export function isValidGradleTask(task) {
94
- return /^[A-Za-z0-9_:-]+$/.test(task);
95
- }
96
- /**
97
- * Runtime body of the generated node script. Plain dependency-free CJS (the
98
- * runner executes it via "node -e"). Must not contain backticks or "${" —
99
- * String.raw keeps escape sequences literal. The renderer id and archive name
100
- * are concatenated in as JS string literals by generateAndroidRenderScript.
101
- */
102
- const ANDROID_RENDER_SCRIPT_MAIN = String.raw `
103
- // ---- runtime ----
104
-
105
- var fs = require('fs');
106
- var path = require('path');
107
- var os = require('os');
108
- var cp = require('child_process');
109
-
110
- var SCREEN_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
111
-
112
- var artifactsDir = process.env.CIBUILD_ARTIFACTS_DIR || '.ci/artifacts';
113
- var inputsDir = path.join('.ci', 'inputs');
114
- var paramsPath = path.join(inputsDir, 'params.json');
115
- var resultPath = path.join(artifactsDir, 'protocol-result.json');
116
-
117
- var currentEntries = [];
118
- var buildError = null;
119
- // params.reference_geometry (design-px geometry per screen), set in main() and
120
- // read by the structural assembler. Null when the run declared none → Signal B
121
- // is skipped and the structural block carries facts only.
122
- var referenceGeometry = null;
123
- // params.reference_text (Signal C: Figma characters per element id per screen),
124
- // set in main() and read by the structural assembler. Null when none declared →
125
- // the appearance container carries presence facts only (no copy facts).
126
- var referenceText = null;
127
- // params.reference_color (Signal C: Figma fills per element id per screen, as
128
- // { fill, text } hex), set in main() and read by the structural assembler. Null
129
- // when none declared → no color facts.
130
- var referenceColor = null;
131
- // params.reference_spans (Signal C span facts, issue 16: the declared highlight
132
- // extent per element id per screen, as { text, background }), set in main() and
133
- // read by the structural assembler. Null when none declared → no span facts.
134
- var referenceSpans = null;
135
- // params.reference_lines (Signal C line facts, issue 18: the declared line
136
- // split per element id per screen, as an array of line texts), set in main()
137
- // and read by the structural assembler. Null when none declared → no line facts.
138
- var referenceLines = null;
139
- // params.reference_typeface (Signal C typeface facts, typeface issue 04: the
140
- // DESIGN font per element id per screen, as { family, weight }, built by the
141
- // caller from Figma node metadata), set in main() and read by the structural
142
- // assembler. Null when none declared → no typeface facts.
143
- var referenceTypeface = null;
144
- // params.reference_baseline (Signal C: 'figma' (default) or 'previous_render').
145
- // Marks whether the appearance reference is the design (divergences) or the
146
- // previous catalog render (regressions/drift, the no-Figma path).
147
- var referenceBaseline = null;
148
- // params.reference_frame (composition runs: the design frame's { w, h } per
149
- // screen, design px treated 1:1 with dp), set in main() and read by the
150
- // structural assembler, which asserts the captured root against it. Null when
151
- // none declared → no frame assertion, no placement gate.
152
- var referenceFrame = null;
153
- // params.proof_kind ('component' default | 'composition'), set in main(). The
154
- // frame assertion and the placement gate only apply to composition runs.
155
- var proofKind = 'component';
156
-
157
- function log(message) {
158
- console.log('[ui-fidelity-render-android] ' + message);
159
- }
160
-
161
- function logError(message) {
162
- console.error('[ui-fidelity-render-android] ' + message);
163
- }
164
-
165
- // ---- result-document path hygiene ----
166
- //
167
- // protocol-result.json must contain relative paths only. Error messages can
168
- // embed absolute runner paths (extracted project dir, gradle diagnostics), so
169
- // every message stored in the result document is passed through
170
- // sanitizeMessage(), which rewrites each known absolute directory -- and its
171
- // symlink-resolved variant -- to a relative path or a stable placeholder.
172
-
173
- var pathReplacements = [];
174
-
175
- function registerPathReplacement(absolutePath, replacement) {
176
- if (!absolutePath || !path.isAbsolute(absolutePath)) return;
177
- pathReplacements.push([absolutePath, replacement]);
178
- try {
179
- var real = fs.realpathSync(absolutePath);
180
- if (real !== absolutePath) pathReplacements.push([real, replacement]);
181
- } catch (realpathError) {
182
- // path does not exist (yet) -- keep the literal form only
183
- }
184
- }
185
-
186
- function sanitizeMessage(message) {
187
- var sanitized = String(message);
188
- pathReplacements
189
- .slice()
190
- .sort(function (a, b) { return b[0].length - a[0].length; })
191
- .forEach(function (pair) {
192
- sanitized = sanitized.split(pair[0]).join(pair[1]);
193
- });
194
- return sanitized;
195
- }
196
-
197
- registerPathReplacement(path.resolve(artifactsDir), path.isAbsolute(artifactsDir) ? '<artifacts>' : artifactsDir);
198
- registerPathReplacement(process.cwd(), '.');
199
- registerPathReplacement(os.tmpdir(), '<tmp>');
200
-
201
- function setBuildError(code, message) {
202
- buildError = { code: code, message: sanitizeMessage(message) };
203
- logError(code + ': ' + buildError.message);
204
- }
205
-
206
- function setError(entry, code, message) {
207
- entry.status = 'render_failed';
208
- entry.error = { code: code, message: sanitizeMessage(message) };
209
- }
210
-
211
- // A reference the gate cannot honestly judge: a hard, mechanical metadata
212
- // verdict (never pixels/OCR). Distinct status so a rotten reference fails loud
213
- // instead of producing a confident-but-false PASS.
214
- function setUnverifiable(entry, code, message) {
215
- entry.status = 'unverifiable';
216
- entry.error = { code: code, message: sanitizeMessage(message) };
217
- }
218
-
219
- // Inlined twins of src/yaml/steps/fidelity-reference-provenance.ts (this script
220
- // ships self-contained and cannot import it). The render-step integration tests
221
- // exercise BOTH against the same fixtures so they cannot silently drift.
222
-
223
- // Provenance is only "present" when the fields the gate reasons over are there.
224
- // A partial object (e.g. a hand-dropped { figmaNode }) is treated as no
225
- // provenance — verifiable is earned only through the full export path.
226
- function hasReferenceProvenance(provenance) {
227
- return (
228
- provenance != null &&
229
- typeof provenance === 'object' &&
230
- typeof provenance.locale === 'string' &&
231
- typeof provenance.resolved === 'boolean'
232
- );
233
- }
234
-
235
- // Assesses one screen's reference. Returns { fault, copyAdvisory }: a fatal
236
- // fault makes the screen unverifiable; a copy advisory downgrades only the
237
- // copy channel (the screen still renders and is judged on geometry, colors,
238
- // typeface, spans, and lines). An absent reference wins over a downgrade —
239
- // there is nothing to judge against.
240
- function assessReferenceProvenance(screen, provenance, renderLocale, referenceExists) {
241
- if (!hasReferenceProvenance(provenance)) {
242
- if (renderLocale !== null) {
243
- return {
244
- fault: {
245
- code: 'NO_PROVENANCE',
246
- message:
247
- 'reference for ' + screen + ' has no export provenance; a reference is ' +
248
- 'verifiable only when exported through the deterministic path',
249
- },
250
- copyAdvisory: null,
251
- };
252
- }
253
- // Unarmed: the caller keeps legacy behaviour (including its own
254
- // reference-existence check).
255
- return { fault: null, copyAdvisory: null };
256
- }
257
- if (!referenceExists) {
258
- return {
259
- fault: {
260
- code: 'REFERENCE_ABSENT',
261
- message: 'reference image for ' + screen + ' is absent',
262
- },
263
- copyAdvisory: null,
264
- };
265
- }
266
- if (provenance.resolved === false) {
267
- return {
268
- fault: null,
269
- copyAdvisory: {
270
- code: 'copy_unverifiable',
271
- reason: 'unresolved_placeholders',
272
- message:
273
- 'reference for ' + screen + ' carries unresolved copy placeholders ' +
274
- '(resolved=false); copy comparisons are skipped — geometry, color, ' +
275
- 'typeface, span, and line judging still hold',
276
- },
277
- };
278
- }
279
- if (renderLocale !== null && provenance.locale !== renderLocale) {
280
- return {
281
- fault: null,
282
- copyAdvisory: {
283
- code: 'copy_unverifiable',
284
- reason: 'locale_mismatch',
285
- message:
286
- 'reference for ' + screen + ' is locale "' + provenance.locale +
287
- '" but the render locale is "' + renderLocale + '"; copy comparisons ' +
288
- 'are skipped — geometry, color, typeface, span, and line judging still hold',
289
- },
290
- };
291
- }
292
- return { fault: null, copyAdvisory: null };
293
- }
294
-
295
- function outputTail(result) {
296
- var combined = ((result.stdout || '') + '\n' + (result.stderr || '')).trim();
297
- if (combined.length > 2000) combined = '...' + combined.slice(-2000);
298
- return combined;
299
- }
300
-
301
- // protocol-result.json is ALWAYS written, with one entry per screen in
302
- // params.json order, even on partial or total failure. All paths inside it
303
- // are relative to the artifacts dir.
304
- function writeResult() {
305
- fs.mkdirSync(artifactsDir, { recursive: true });
306
- var doc = {
307
- renderer: RENDERER,
308
- package_source: 'inputs',
309
- error: buildError,
310
- screens: currentEntries,
311
- };
312
- fs.writeFileSync(resultPath, JSON.stringify(doc, null, 2) + '\n');
313
- log('wrote ' + resultPath);
314
- }
315
-
316
- // ---- shipped Gradle-project stage ----
317
- //
318
- // The caller ships a self-contained Gradle project as
319
- // .ci/inputs/package.tar.gz. Before any render it is located, extracted into a
320
- // scratch directory, and validated. Failures here are PER-BUILD structured
321
- // errors recorded as the result document's top-level error object. The
322
- // extracted size is bounded as a decompression-bomb guard.
323
-
324
- var DEFAULT_MAX_EXTRACTED_BYTES = 64 * 1024 * 1024;
325
- var maxExtractedBytes =
326
- Number(process.env.UI_FIDELITY_MAX_PACKAGE_BYTES || '') || DEFAULT_MAX_EXTRACTED_BYTES;
327
- var shippedExtractDir = null;
328
-
329
- function runTar(args) {
330
- var result = cp.spawnSync('tar', args, { encoding: 'utf-8', maxBuffer: 64 * 1024 * 1024 });
331
- if (result.error) {
332
- result.status = result.status == null ? 127 : result.status;
333
- result.stderr = (result.stderr || '') + String(result.error.message || result.error);
334
- }
335
- return result;
336
- }
337
-
338
- function isUnsafeArchiveMember(name) {
339
- if (name.charAt(0) === '/') return true;
340
- var segments = name.split('/');
341
- for (var i = 0; i < segments.length; i++) {
342
- if (segments[i] === '..') return true;
343
- }
344
- return false;
345
- }
346
-
347
- function extractedSize(dir) {
348
- var total = 0;
349
- var stack = [dir];
350
- while (stack.length > 0) {
351
- var current = stack.pop();
352
- var names = fs.readdirSync(current);
353
- for (var i = 0; i < names.length; i++) {
354
- var entryPath = path.join(current, names[i]);
355
- var stat = fs.lstatSync(entryPath);
356
- if (stat.isDirectory()) stack.push(entryPath);
357
- else total += stat.size;
358
- }
359
- }
360
- return total;
361
- }
362
-
363
- // Entries macOS archiving tools add but that say nothing about the project
364
- // layout are ignored when locating the project root.
365
- function isJunkEntry(name) {
366
- return name === '.DS_Store' || name === '__MACOSX' || name.indexOf('._') === 0;
367
- }
368
-
369
- // A Gradle project root is identified by a settings script at its top.
370
- function hasSettings(dir) {
371
- try {
372
- if (fs.statSync(path.join(dir, 'settings.gradle.kts')).isFile()) return true;
373
- } catch (e1) {
374
- // not the .kts variant
375
- }
376
- try {
377
- return fs.statSync(path.join(dir, 'settings.gradle')).isFile();
378
- } catch (e2) {
379
- return false;
380
- }
381
- }
382
-
383
- // Project-root rule: a settings script at the extraction root wins; otherwise
384
- // exactly one top-level directory with a settings script at its top is the
385
- // root; anything else is a structured per-build error.
386
- function findProjectRoot(extractDir) {
387
- if (hasSettings(extractDir)) return extractDir;
388
- var entries = fs.readdirSync(extractDir).filter(function (name) {
389
- return !isJunkEntry(name);
390
- });
391
- if (entries.length === 0) {
392
- setBuildError('ANDROID_ARCHIVE_INVALID', PACKAGE_ARCHIVE_NAME + ' is empty');
393
- return null;
394
- }
395
- if (entries.length > 1) {
396
- setBuildError(
397
- 'ANDROID_ARCHIVE_INVALID',
398
- PACKAGE_ARCHIVE_NAME + ' must contain a single project root; found ' +
399
- entries.length + ' top-level entries (' + entries.sort().join(', ') +
400
- ') and no settings.gradle(.kts) at the archive root'
401
- );
402
- return null;
403
- }
404
- var rootPath = path.join(extractDir, entries[0]);
405
- if (!fs.statSync(rootPath).isDirectory()) {
406
- setBuildError(
407
- 'ANDROID_ARCHIVE_INVALID',
408
- PACKAGE_ARCHIVE_NAME + ' must contain a project directory; its only ' +
409
- 'top-level entry (' + entries[0] + ') is not a directory'
410
- );
411
- return null;
412
- }
413
- if (!hasSettings(rootPath)) {
414
- setBuildError(
415
- 'ANDROID_PROJECT_ROOT_MISSING',
416
- 'no settings.gradle(.kts) at the top of the project root (' + entries[0] + ') in ' +
417
- PACKAGE_ARCHIVE_NAME
418
- );
419
- return null;
420
- }
421
- return rootPath;
422
- }
423
-
424
- // Locates, extracts, and validates the shipped archive. Returns the project
425
- // root to build from, or null after recording a structured build error.
426
- function prepareShippedProject() {
427
- var archivePath = path.join(inputsDir, PACKAGE_ARCHIVE_NAME);
428
- var archiveStat = null;
429
- try {
430
- archiveStat = fs.statSync(archivePath);
431
- } catch (statError) {
432
- archiveStat = null;
433
- }
434
- if (!archiveStat || !archiveStat.isFile()) {
435
- setBuildError(
436
- 'ANDROID_ARCHIVE_MISSING',
437
- 'no ' + PACKAGE_ARCHIVE_NAME + ' in ' + inputsDir + '; ship the Gradle ' +
438
- 'render project as a run input named ' + PACKAGE_ARCHIVE_NAME
439
- );
440
- return null;
441
- }
442
-
443
- var listing = runTar(['-tzf', archivePath]);
444
- if (listing.status !== 0) {
445
- setBuildError(
446
- 'ANDROID_ARCHIVE_INVALID',
447
- PACKAGE_ARCHIVE_NAME + ' is not a readable gzipped tar archive:\n' + outputTail(listing)
448
- );
449
- return null;
450
- }
451
- var members = (listing.stdout || '').split('\n').filter(function (name) { return name !== ''; });
452
- var unsafe = members.filter(isUnsafeArchiveMember);
453
- if (unsafe.length > 0) {
454
- setBuildError(
455
- 'ANDROID_ARCHIVE_INVALID',
456
- PACKAGE_ARCHIVE_NAME + ' contains unsafe member paths (absolute or dot-dot): ' +
457
- unsafe.slice(0, 5).join(', ')
458
- );
459
- return null;
460
- }
461
-
462
- shippedExtractDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-fidelity-android-project-'));
463
- registerPathReplacement(shippedExtractDir, '<project>');
464
- log('extracting ' + PACKAGE_ARCHIVE_NAME + ' (' + archiveStat.size + ' bytes)');
465
- var extraction = runTar(['-xzf', archivePath, '-C', shippedExtractDir]);
466
- if (extraction.status !== 0) {
467
- setBuildError(
468
- 'ANDROID_ARCHIVE_INVALID',
469
- PACKAGE_ARCHIVE_NAME + ' could not be extracted:\n' + outputTail(extraction)
470
- );
471
- return null;
472
- }
473
-
474
- var totalBytes = extractedSize(shippedExtractDir);
475
- if (totalBytes > maxExtractedBytes) {
476
- setBuildError(
477
- 'ANDROID_ARCHIVE_TOO_LARGE',
478
- PACKAGE_ARCHIVE_NAME + ' extracts to ' + totalBytes + ' bytes, over the ' +
479
- maxExtractedBytes + '-byte limit; ship a leaner project'
480
- );
481
- return null;
482
- }
483
-
484
- var projectRoot = findProjectRoot(shippedExtractDir);
485
- if (projectRoot === null) return null;
486
- var rootLabel = path.relative(shippedExtractDir, projectRoot);
487
- log('project root: ' + (rootLabel === '' ? '.' : rootLabel));
488
- return projectRoot;
489
- }
490
-
491
- function cleanupShippedProject() {
492
- if (shippedExtractDir === null) return;
493
- if (process.env.UI_FIDELITY_KEEP_HARNESS) {
494
- log('keeping extracted project (UI_FIDELITY_KEEP_HARNESS set)');
495
- return;
496
- }
497
- try {
498
- fs.rmSync(shippedExtractDir, { recursive: true, force: true });
499
- } catch (cleanupError) {
500
- // best effort
501
- }
502
- shippedExtractDir = null;
503
- }
504
-
505
- // ---- render stage ----
506
- //
507
- // Trim/align runs through the shared trim stage's runTrimHelper (the pure-JS
508
- // helper inside the ci binary — the same bytes iOS uses), so this step has no
509
- // platform-toolchain dependency and runs on macOS and Linux workers alike.
510
-
511
- // Runs the project's Roborazzi record task once, from the project root, so
512
- // Gradle resolves the task in whichever module declares it. Prefers the shipped
513
- // wrapper; falls back to a gradle on PATH.
514
- function runGradle(projectRoot, task) {
515
- var wrapper = path.join(projectRoot, 'gradlew');
516
- var hasWrapper = false;
517
- try {
518
- hasWrapper = fs.statSync(wrapper).isFile();
519
- } catch (statError) {
520
- hasWrapper = false;
521
- }
522
- var cmd = hasWrapper ? wrapper : 'gradle';
523
- log((hasWrapper ? './gradlew ' : 'gradle ') + task);
524
- var result = cp.spawnSync(cmd, [task], {
525
- cwd: projectRoot,
526
- encoding: 'utf-8',
527
- maxBuffer: 64 * 1024 * 1024,
528
- });
529
- if (result.stdout) process.stdout.write(result.stdout);
530
- if (result.stderr) process.stderr.write(result.stderr);
531
- if (result.error) {
532
- result.status = result.status == null ? 127 : result.status;
533
- result.stderr = (result.stderr || '') + String(result.error.message || result.error);
534
- }
535
- return result;
536
- }
537
-
538
- // The golden's trailing method segment that renders a screen: Roborazzi's
539
- // record task names goldens <package>.<Class>.<method>.png and ignores an
540
- // explicit capture filename, so the contract is a test method named
541
- // render<ScreenName> (or a flat <ScreenName>.png). Matching the final
542
- // dot-delimited segment keeps it collision-safe (renderXProof never matches
543
- // screen "Proof").
544
- function roborazziPngMatches(basename, screen) {
545
- if (basename.slice(-4) !== '.png') return false;
546
- var stem = basename.slice(0, -4);
547
- var lastDot = stem.lastIndexOf('.');
548
- var method = lastDot === -1 ? stem : stem.slice(lastDot + 1);
549
- return method === screen || method === 'render' + screen;
550
- }
551
-
552
- // The structural-pass layout dump (Signal A) is emitted next to the PNG in a
553
- // roborazzi output dir, but Roborazzi controls the PNG's golden filename and
554
- // ignores explicit capture names, so the dump can't reliably share the PNG's
555
- // stem. Match it the same collision-safe way as the PNG instead — by the final
556
- // dot-delimited segment of "<...>.layout.json" — so the emitter can write a
557
- // simple "<Screen>.layout.json" regardless of the PNG's golden name.
558
- function roborazziLayoutMatches(basename, screen) {
559
- var suffix = '.layout.json';
560
- if (basename.slice(-suffix.length) !== suffix) return false;
561
- var stem = basename.slice(0, -suffix.length);
562
- var lastDot = stem.lastIndexOf('.');
563
- var seg = lastDot === -1 ? stem : stem.slice(lastDot + 1);
564
- return seg === screen || seg === 'render' + screen;
565
- }
566
-
567
- // Roborazzi writes PNGs to <module>/build/outputs/roborazzi/. Collect every
568
- // such directory in the project so a screen's PNG can be located regardless of
569
- // which module rendered it.
570
- function findRoborazziDirs(root) {
571
- var found = [];
572
- var stack = [root];
573
- while (stack.length > 0) {
574
- var dir = stack.pop();
575
- var entries;
576
- try {
577
- entries = fs.readdirSync(dir, { withFileTypes: true });
578
- } catch (readError) {
579
- continue;
580
- }
581
- for (var i = 0; i < entries.length; i++) {
582
- var entry = entries[i];
583
- if (!entry.isDirectory()) continue;
584
- var full = path.join(dir, entry.name);
585
- if (
586
- entry.name === 'roborazzi' &&
587
- path.basename(path.dirname(full)) === 'outputs' &&
588
- path.basename(path.dirname(path.dirname(full))) === 'build'
589
- ) {
590
- found.push(full);
591
- } else {
592
- stack.push(full);
593
- }
594
- }
595
- }
596
- return found;
597
- }
598
-
599
- // Builds the project once and pairs each screen's rendered PNG with its
600
- // reference via the shared post-processor. A build failure means the project
601
- // does not render and EVERY screen is RENDER_UNSUPPORTED (one Gradle module is
602
- // one compile unit, so a wrapper compile error cannot be isolated to a single
603
- // screen — the analog of the iOS probe gate). A screen whose PNG is absent
604
- // after a successful build is RENDER_FAILED on its own.
605
- function renderScreens(renderable, projectRoot) {
606
- // Snapshot the package's input files BEFORE the build runs, so the digest is
607
- // over the pristine committed source and never perturbed by build outputs.
608
- var packageFiles = __fidCollectPackageFiles(projectRoot);
609
- // A package is safely cacheable only when it is hermetic — no Gradle script
610
- // reaches outside its own root. Then the package IS its whole dependency
611
- // closure, so the content key cannot miss an external change. A non-hermetic
612
- // package emits no digest, so a later dispatch never skips it.
613
- var packageHermetic = __fidIsHermetic(packageFiles);
614
- var gradle = runGradle(projectRoot, CONFIG.gradleTask);
615
- if (gradle.status !== 0) {
616
- var detail = outputTail(gradle);
617
- currentEntries.forEach(function (entry) {
618
- setError(
619
- entry,
620
- 'RENDER_UNSUPPORTED',
621
- 'the Gradle render project did not build:\n' + detail
622
- );
623
- });
624
- return;
625
- }
626
-
627
- var workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-fidelity-android-trim-'));
628
- registerPathReplacement(workDir, '<work>');
629
- try {
630
- var roborazziDirs = findRoborazziDirs(projectRoot);
631
- renderable.forEach(function (entry) {
632
- var source = null;
633
- for (var i = 0; i < roborazziDirs.length && source === null; i++) {
634
- var names;
635
- try {
636
- names = fs.readdirSync(roborazziDirs[i]);
637
- } catch (readError) {
638
- names = [];
639
- }
640
- for (var j = 0; j < names.length; j++) {
641
- if (roborazziPngMatches(names[j], entry.screen)) {
642
- source = path.join(roborazziDirs[i], names[j]);
643
- break;
644
- }
645
- }
646
- }
647
- if (source === null) {
648
- setError(
649
- entry,
650
- 'RENDER_FAILED',
651
- 'no PNG was produced for ' + entry.screen + '; the render test must record a ' +
652
- 'golden named render' + entry.screen + ' (build/outputs/roborazzi/...render' +
653
- entry.screen + '.png)'
654
- );
655
- return;
656
- }
657
- var relative = 'ui-fidelity/rendered/' + entry.screen + '.png';
658
- var outputPath = path.resolve(artifactsDir, relative);
659
- try {
660
- fs.copyFileSync(source, outputPath);
661
- } catch (copyError) {
662
- setError(
663
- entry,
664
- 'RENDER_FAILED',
665
- 'could not copy the render for ' + entry.screen + ': ' +
666
- (copyError && copyError.message ? copyError.message : String(copyError))
667
- );
668
- return;
669
- }
670
- entry.rendered_image_path = relative;
671
- entry.status = 'rendered';
672
- entry.error = null;
673
- // Content key over the package source + this screen's reference: lets a
674
- // consumer skip a re-render whose inputs are unchanged. Emitted only for a
675
- // hermetic package; otherwise omitted so no stale verdict can be replayed.
676
- if (packageHermetic) {
677
- var referenceBytes = null;
678
- try {
679
- if (entry.reference_image_path) {
680
- referenceBytes = fs.readFileSync(path.resolve(artifactsDir, entry.reference_image_path));
681
- }
682
- } catch (digestReferenceError) {
683
- referenceBytes = null;
684
- }
685
- entry.source_digest = __fidSourceDigest(packageFiles, referenceBytes);
686
- }
687
- log('rendered ' + entry.screen + ' -> ' + relative);
688
- trimRenderedImage(entry, outputPath);
689
- // Signal A: the render emits a per-node layout dump into a roborazzi
690
- // output dir (named "<Screen>.layout.json" or "...render<Screen>.layout.json").
691
- // Locate it the same way as the PNG and derive structural facts; absent or
692
- // malformed is a clean no-op.
693
- var layoutSource = null;
694
- for (var li = 0; li < roborazziDirs.length && layoutSource === null; li++) {
695
- var lnames;
696
- try {
697
- lnames = fs.readdirSync(roborazziDirs[li]);
698
- } catch (lReadError) {
699
- lnames = [];
700
- }
701
- for (var lj = 0; lj < lnames.length; lj++) {
702
- if (roborazziLayoutMatches(lnames[lj], entry.screen)) {
703
- layoutSource = path.join(roborazziDirs[li], lnames[lj]);
704
- break;
705
- }
706
- }
707
- }
708
- if (layoutSource !== null) {
709
- // Signal A: write the facts + layout artifacts (deep-dive path).
710
- deriveAndWriteFacts(entry, layoutSource);
711
- // ADR-0004: assemble the inline structural block (facts + Signal-B
712
- // geometry deltas vs reference_geometry) onto the screen entry, so the
713
- // judge reads the signals from protocol-result.json without fetching
714
- // artifacts. CONFIG.scale is the render density (dp = px / scale).
715
- assembleStructural(entry, layoutSource, referenceGeometry, CONFIG.scale, referenceText, referenceColor, referenceBaseline, referenceFrame, proofKind, referenceSpans, referenceLines, referenceTypeface);
716
- }
717
- });
718
- } finally {
719
- if (process.env.UI_FIDELITY_KEEP_HARNESS) {
720
- log('keeping trim work dir (UI_FIDELITY_KEEP_HARNESS set)');
721
- } else {
722
- try {
723
- fs.rmSync(workDir, { recursive: true, force: true });
724
- } catch (cleanupError) {
725
- // best effort
726
- }
727
- }
728
- }
729
- }
730
-
731
- function main() {
732
- // 1. Read and validate .ci/inputs/params.json.
733
- var paramsProblem = null;
734
- var params = null;
735
- if (!fs.existsSync(paramsPath)) {
736
- paramsProblem = 'not found at ' + paramsPath;
737
- } else {
738
- try {
739
- params = JSON.parse(fs.readFileSync(paramsPath, 'utf-8'));
740
- } catch (parseError) {
741
- paramsProblem = 'is not valid JSON: ' +
742
- (parseError && parseError.message ? parseError.message : String(parseError));
743
- }
744
- }
745
- if (!paramsProblem) {
746
- if (
747
- !params || typeof params !== 'object' || Array.isArray(params) ||
748
- !params.screens || typeof params.screens !== 'object' || Array.isArray(params.screens)
749
- ) {
750
- paramsProblem =
751
- 'must have the shape { "screens": { "<ScreenName>": "<referenceFileBasename>" } }';
752
- }
753
- }
754
- if (paramsProblem) {
755
- // Screens cannot be enumerated, so the result document is written with an
756
- // empty screens array and the step fails.
757
- logError('params.json ' + paramsProblem);
758
- writeResult();
759
- return 1;
760
- }
761
-
762
- // Reference-provenance sidecar. Enforcement is ON only when the run declares
763
- // its render locale (the manifest-driven multi-state path always does);
764
- // legacy single-reference runs without it keep their existing behaviour.
765
- var renderLocale =
766
- typeof params.render_locale === 'string' && params.render_locale !== ''
767
- ? params.render_locale
768
- : null;
769
- var provenanceMap =
770
- params.provenance && typeof params.provenance === 'object' && !Array.isArray(params.provenance)
771
- ? params.provenance
772
- : {};
773
- // Signal-B design numbers (advisory): { "<Screen>": { "<tagId>": {w,h,x,y} } }
774
- // in design px. Absent → the structural block carries Signal-A facts only.
775
- referenceGeometry =
776
- params.reference_geometry &&
777
- typeof params.reference_geometry === 'object' &&
778
- !Array.isArray(params.reference_geometry)
779
- ? params.reference_geometry
780
- : null;
781
- // Signal-C copy reference: { "<Screen>": { "<elementId>": "<text>" } } — the
782
- // Figma characters mapped to element ids. Absent → presence facts only.
783
- referenceText =
784
- params.reference_text &&
785
- typeof params.reference_text === 'object' &&
786
- !Array.isArray(params.reference_text)
787
- ? params.reference_text
788
- : null;
789
- // Signal-C color reference: { "<Screen>": { "<elementId>": { fill, text } } } —
790
- // the Figma fills mapped to element ids. Absent → no color facts.
791
- referenceColor =
792
- params.reference_color &&
793
- typeof params.reference_color === 'object' &&
794
- !Array.isArray(params.reference_color)
795
- ? params.reference_color
796
- : null;
797
- // Signal-C span reference (issue 16): { "<Screen>": { "<elementId>":
798
- // { text, background } } } — the substring the design highlights on that
799
- // text element. Absent → no span facts.
800
- referenceSpans =
801
- params.reference_spans &&
802
- typeof params.reference_spans === 'object' &&
803
- !Array.isArray(params.reference_spans)
804
- ? params.reference_spans
805
- : null;
806
- // Signal-C line reference (issue 18): { "<Screen>": { "<elementId>":
807
- // ["line 1", "line 2"] } } — the exact line texts the design breaks that
808
- // text element into. Absent → no line facts.
809
- referenceLines =
810
- params.reference_lines &&
811
- typeof params.reference_lines === 'object' &&
812
- !Array.isArray(params.reference_lines)
813
- ? params.reference_lines
814
- : null;
815
- // Signal-C typeface reference (typeface issue 04): { "<Screen>":
816
- // { "<elementId>": { family, weight } } } — the DESIGN font per element, from
817
- // Figma node metadata. Diffed against the RESOLVED (painted) font the dump
818
- // captured. Absent → no typeface facts.
819
- referenceTypeface =
820
- params.reference_typeface &&
821
- typeof params.reference_typeface === 'object' &&
822
- !Array.isArray(params.reference_typeface)
823
- ? params.reference_typeface
824
- : null;
825
- // Signal-C baseline marker: 'previous_render' when the appearance reference was
826
- // sourced from the previous catalog render (no-Figma drift path), else 'figma'.
827
- referenceBaseline =
828
- params.reference_baseline === 'previous_render' ? 'previous_render' : 'figma';
829
- // Composition-run inputs: the declared kind gates the frame assertion; the
830
- // per-screen design frame is what the captured root is asserted against.
831
- proofKind = params.proof_kind === 'composition' ? 'composition' : 'component';
832
- referenceFrame =
833
- params.reference_frame &&
834
- typeof params.reference_frame === 'object' &&
835
- !Array.isArray(params.reference_frame)
836
- ? params.reference_frame
837
- : null;
838
-
839
- var screens = Object.keys(params.screens);
840
- currentEntries = screens.map(function (screen) {
841
- return {
842
- screen: screen,
843
- status: 'render_failed',
844
- error: null,
845
- copy_advisory: null,
846
- reference_image_path: null,
847
- rendered_image_path: null,
848
- aligned_image_path: null,
849
- dimensions: null,
850
- layout_path: null,
851
- facts_path: null,
852
- };
853
- });
854
- log('screens: ' + (screens.join(', ') || '(none)'));
855
-
856
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'rendered'), { recursive: true });
857
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'references'), { recursive: true });
858
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'aligned'), { recursive: true });
859
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'layout'), { recursive: true });
860
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'facts'), { recursive: true });
861
-
862
- // 2. Per-screen validation + reference copies. Each screen gets its OWN copy
863
- // named by screen, even when two screens share a reference basename.
864
- currentEntries.forEach(function (entry) {
865
- if (!SCREEN_NAME.test(entry.screen)) {
866
- setError(
867
- entry,
868
- 'INVALID_SCREEN_NAME',
869
- 'screen name ' + JSON.stringify(entry.screen) +
870
- ' is not a valid identifier (letters, digits, underscores)'
871
- );
872
- return;
873
- }
874
- var basename = params.screens[entry.screen];
875
- if (
876
- typeof basename !== 'string' || basename === '' ||
877
- basename === '.' || basename === '..' ||
878
- basename !== path.basename(basename)
879
- ) {
880
- setError(
881
- entry,
882
- 'REFERENCE_MISSING',
883
- 'reference for ' + entry.screen + ' must be a plain file basename inside ' +
884
- inputsDir + ', got: ' + JSON.stringify(basename)
885
- );
886
- return;
887
- }
888
- var source = path.join(inputsDir, basename);
889
- var sourceStat = null;
890
- try {
891
- sourceStat = fs.statSync(source);
892
- } catch (statError) {
893
- sourceStat = null;
894
- }
895
- var referenceExists = !!(sourceStat && sourceStat.isFile());
896
-
897
- // Provenance assessment, armed whenever this screen's provenance rides in
898
- // params (the references/ snapshot lifts it automatically) or the run
899
- // declares a render locale (legacy arming, which keeps NO_PROVENANCE
900
- // fatal). Fatal faults (reference absent, no provenance while armed) make
901
- // the screen UNVERIFIABLE — hard, loud, never a silent PASS. Unresolved
902
- // placeholders and locale mismatches downgrade ONLY the copy channel: the
903
- // screen renders and is judged on geometry, colors, typeface, spans, and
904
- // lines, while copy comparisons are skipped and reported as a structured
905
- // copy_unverifiable advisory.
906
- var screenProvenance = provenanceMap[entry.screen];
907
- if (renderLocale !== null || hasReferenceProvenance(screenProvenance)) {
908
- var assessment = assessReferenceProvenance(
909
- entry.screen,
910
- screenProvenance,
911
- renderLocale,
912
- referenceExists
913
- );
914
- if (assessment.fault) {
915
- setUnverifiable(entry, assessment.fault.code, assessment.fault.message);
916
- return;
917
- }
918
- if (assessment.copyAdvisory) {
919
- entry.copy_advisory = {
920
- code: assessment.copyAdvisory.code,
921
- reason: assessment.copyAdvisory.reason,
922
- message: sanitizeMessage(assessment.copyAdvisory.message),
923
- };
924
- // Copy comparisons are unverifiable for this screen: drop its
925
- // reference text so no copy facts (text_mismatch / text_missing) are
926
- // derived downstream.
927
- if (referenceText && referenceText[entry.screen]) {
928
- delete referenceText[entry.screen];
929
- }
930
- log(
931
- entry.screen + ': copy channel downgraded (' +
932
- assessment.copyAdvisory.reason + '), non-copy judging proceeds'
933
- );
934
- }
935
- } else if (!referenceExists) {
936
- // Legacy (no provenance, no render locale): an absent reference stays a
937
- // render_failed REFERENCE_MISSING, exactly as before.
938
- setError(
939
- entry,
940
- 'REFERENCE_MISSING',
941
- sourceStat
942
- ? 'reference for ' + entry.screen + ' is not a regular file: ' + source
943
- : 'reference image not found: ' + source
944
- );
945
- return;
946
- }
947
- var relative = 'ui-fidelity/references/' + entry.screen + '.png';
948
- try {
949
- fs.copyFileSync(source, path.join(artifactsDir, relative));
950
- } catch (copyError) {
951
- setError(
952
- entry,
953
- 'REFERENCE_MISSING',
954
- 'could not copy reference for ' + entry.screen + ': ' +
955
- (copyError && copyError.message ? copyError.message : String(copyError))
956
- );
957
- return;
958
- }
959
- entry.reference_image_path = relative;
960
- });
961
-
962
- // 3. Extract + validate the shipped Gradle project, then render the
963
- // renderable screens. The project stage runs even when no screen is
964
- // renderable, so packaging mistakes are always reported.
965
- var renderable = currentEntries.filter(function (entry) { return entry.error === null; });
966
- try {
967
- var projectRoot = prepareShippedProject();
968
- if (projectRoot !== null) {
969
- if (renderable.length > 0) {
970
- renderScreens(renderable, projectRoot);
971
- } else if (currentEntries.length > 0) {
972
- log('no renderable screens, skipping render');
973
- }
974
- }
975
- } finally {
976
- cleanupShippedProject();
977
- }
978
-
979
- // 4. Exit code: zero only when the shipped project (if any) was valid and
980
- // every screen rendered.
981
- writeResult();
982
- if (buildError !== null) {
983
- logError('archive validation failed: ' + buildError.code);
984
- return 1;
985
- }
986
- var failed = currentEntries.filter(function (entry) { return entry.status !== 'rendered'; });
987
- if (failed.length > 0) {
988
- logError(failed.length + ' of ' + currentEntries.length + ' screen(s) not rendered');
989
- return 1;
990
- }
991
- log('all ' + currentEntries.length + ' screen(s) rendered');
992
- return 0;
993
- }
994
-
995
- var exitCode = 1;
996
- try {
997
- exitCode = main();
998
- } catch (unexpectedError) {
999
- logError(
1000
- 'unexpected failure: ' +
1001
- (unexpectedError && unexpectedError.stack ? unexpectedError.stack : String(unexpectedError))
1002
- );
1003
- try {
1004
- currentEntries.forEach(function (entry) {
1005
- if (entry.status !== 'rendered' && entry.error === null) {
1006
- setError(
1007
- entry,
1008
- 'INTERNAL_ERROR',
1009
- String(unexpectedError && unexpectedError.message ? unexpectedError.message : unexpectedError)
1010
- );
1011
- }
1012
- });
1013
- writeResult();
1014
- } catch (writeError) {
1015
- logError('could not write protocol-result.json: ' + writeError);
1016
- }
1017
- exitCode = 1;
1018
- }
1019
- process.exit(exitCode);
1020
- `;
1021
- /**
1022
- * Replaces exactly one occurrence of `anchor` in `source`. Throws when the
1023
- * anchor is missing or ambiguous, so any drift between the default runtime text
1024
- * and a *_source variant fails loudly at module load.
1025
- */
1026
- function replaceOnce(source, anchor, replacement) {
1027
- const first = source.indexOf(anchor);
1028
- if (first === -1 || source.indexOf(anchor, first + anchor.length) !== -1) {
1029
- throw new Error('ui-fidelity-render-android: expected exactly one occurrence of anchor: ' + anchor);
1030
- }
1031
- return source.slice(0, first) + replacement + source.slice(first + anchor.length);
1032
- }
1033
- /**
1034
- * Builds the runtime main for package_source "repo" by patching the default
1035
- * (inputs) runtime text: the result document records package_source "repo", and
1036
- * the project is resolved from the committed checkout at package_path instead of
1037
- * being extracted from a shipped tarball. ANDROID_RENDER_SCRIPT_MAIN itself is
1038
- * never modified, so omitting package_source keeps the generated script
1039
- * byte-identical to the default. Mirrors the iOS package_source patch.
1040
- */
1041
- function buildAndroidPackageRepoMain(base) {
1042
- let main = base;
1043
- // The result document advertises the repo package source.
1044
- main = replaceOnce(main, "package_source: 'inputs',", "package_source: 'repo',");
1045
- // Render directly from the committed project at package_path; no archive
1046
- // extract/validate. A missing or non-Gradle directory is a clean per-build
1047
- // error (the same exit-on-buildError path the inputs archive validation uses).
1048
- main = replaceOnce(main, ' // 3. Extract + validate the shipped Gradle project, then render the\n' +
1049
- ' // renderable screens. The project stage runs even when no screen is\n' +
1050
- ' // renderable, so packaging mistakes are always reported.\n' +
1051
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1052
- ' try {\n' +
1053
- ' var projectRoot = prepareShippedProject();\n' +
1054
- ' if (projectRoot !== null) {\n' +
1055
- ' if (renderable.length > 0) {\n' +
1056
- ' renderScreens(renderable, projectRoot);\n' +
1057
- ' } else if (currentEntries.length > 0) {\n' +
1058
- " log('no renderable screens, skipping render');\n" +
1059
- ' }\n' +
1060
- ' }\n' +
1061
- ' } finally {\n' +
1062
- ' cleanupShippedProject();\n' +
1063
- ' }', ' // 3. Resolve the committed Gradle project at package_path and render the\n' +
1064
- ' // renderable screens directly from the checkout (no archive extract).\n' +
1065
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1066
- ' var projectRoot = path.resolve(CONFIG.packagePath);\n' +
1067
- ' registerPathReplacement(\n' +
1068
- ' projectRoot,\n' +
1069
- " path.isAbsolute(CONFIG.packagePath) ? '<package_path>' : CONFIG.packagePath\n" +
1070
- ' );\n' +
1071
- ' if (!fs.existsSync(projectRoot)) {\n' +
1072
- " setBuildError('ANDROID_PROJECT_MISSING', 'package_path does not exist: ' + projectRoot);\n" +
1073
- ' } else if (!hasSettings(projectRoot)) {\n' +
1074
- ' setBuildError(\n' +
1075
- " 'ANDROID_PROJECT_ROOT_MISSING',\n" +
1076
- " 'no settings.gradle(.kts) at the top of package_path: ' + projectRoot\n" +
1077
- ' );\n' +
1078
- ' } else if (renderable.length > 0) {\n' +
1079
- ' renderScreens(renderable, projectRoot);\n' +
1080
- ' } else if (currentEntries.length > 0) {\n' +
1081
- " log('no renderable screens, skipping render');\n" +
1082
- ' }');
1083
- return main;
1084
- }
1085
- const ANDROID_RENDER_SCRIPT_MAIN_REPO = buildAndroidPackageRepoMain(ANDROID_RENDER_SCRIPT_MAIN);
1086
- /**
1087
- * Patches the reference-sourcing block to read each screen's reference from a
1088
- * repo-relative path (resolved against the checkout) instead of a plain basename
1089
- * under `.ci/inputs/`. Mirrors the iOS reference_source: repo patch. The
1090
- * downstream stat/copy/provenance logic is unchanged; only how `source` is
1091
- * computed differs, so a triggered (agent-less) run can point at a committed
1092
- * reference that lives in the cloned repo.
1093
- */
1094
- function buildAndroidReferenceRepoMain(base) {
1095
- return replaceOnce(base, 'var basename = params.screens[entry.screen];\n' +
1096
- ' if (\n' +
1097
- " typeof basename !== 'string' || basename === '' ||\n" +
1098
- " basename === '.' || basename === '..' ||\n" +
1099
- ' basename !== path.basename(basename)\n' +
1100
- ' ) {\n' +
1101
- ' setError(\n' +
1102
- ' entry,\n' +
1103
- " 'REFERENCE_MISSING',\n" +
1104
- " 'reference for ' + entry.screen + ' must be a plain file basename inside ' +\n" +
1105
- " inputsDir + ', got: ' + JSON.stringify(basename)\n" +
1106
- ' );\n' +
1107
- ' return;\n' +
1108
- ' }\n' +
1109
- ' var source = path.join(inputsDir, basename);', 'var refPath = params.screens[entry.screen];\n' +
1110
- " if (typeof refPath !== 'string' || refPath === '') {\n" +
1111
- ' setError(\n' +
1112
- ' entry,\n' +
1113
- " 'REFERENCE_MISSING',\n" +
1114
- " 'reference for ' + entry.screen + ' must be a non-empty repo-relative path, got: ' +\n" +
1115
- ' JSON.stringify(refPath)\n' +
1116
- ' );\n' +
1117
- ' return;\n' +
1118
- ' }\n' +
1119
- ' var source = path.resolve(refPath);');
1120
- }
1121
- /**
1122
- * Builds the runtime main for package_source "url" (fidelity-linux 03) by
1123
- * patching the default (inputs) runtime text. Three departures from the
1124
- * default, all forced by the same fact — a clone-free worker has no checkout:
1125
- *
1126
- * 1. The package is DOWNLOADED (curl, already a runner requirement in this
1127
- * mode) to `.ci/inputs/package.tar.gz` and extracted BEFORE the per-screen
1128
- * reference stage, because committed references travel inside it. A failed
1129
- * download is one structured per-build error, never per-screen noise.
1130
- * 2. Each screen's reference is the committed repo-relative path resolved
1131
- * INSIDE the extracted package: the path must sit under packagePath, and
1132
- * the remainder resolves against the extracted project root.
1133
- * 3. The render stage reuses the already-extracted root (no second extract);
1134
- * cleanup mirrors the inputs path. The result document records
1135
- * package_source "url".
1136
- *
1137
- * ANDROID_RENDER_SCRIPT_MAIN itself is never modified, so omitting
1138
- * package_source keeps the generated script byte-identical to the default.
1139
- */
1140
- function buildAndroidPackageUrlMain(base) {
1141
- let main = base;
1142
- // The result document advertises the url package source.
1143
- main = replaceOnce(main, "package_source: 'inputs',", "package_source: 'url',");
1144
- // Download + extract FIRST — the reference stage below reads from inside
1145
- // the extracted package.
1146
- main = replaceOnce(main, ' // 2. Per-screen validation + reference copies. Each screen gets its OWN copy\n' +
1147
- ' // named by screen, even when two screens share a reference basename.\n' +
1148
- ' currentEntries.forEach(function (entry) {', ' // 2a. Download + extract the shipped package FIRST (URL mode): committed\n' +
1149
- ' // references live inside it, so the per-screen reference copies below\n' +
1150
- ' // need the extracted project root. A failed download or an invalid\n' +
1151
- ' // archive is ONE structured per-build error; per-screen reference\n' +
1152
- ' // work then stops cleanly (no bogus REFERENCE_MISSING noise).\n' +
1153
- ' var urlProjectRoot = null;\n' +
1154
- ' (function () {\n' +
1155
- ' try {\n' +
1156
- ' fs.mkdirSync(inputsDir, { recursive: true });\n' +
1157
- ' } catch (mkdirError) {\n' +
1158
- ' // statSync below reports the real failure\n' +
1159
- ' }\n' +
1160
- ' var dest = path.join(inputsDir, PACKAGE_ARCHIVE_NAME);\n' +
1161
- " log('downloading render package');\n" +
1162
- ' var download = cp.spawnSync(\n' +
1163
- " 'curl',\n" +
1164
- " ['-fsSL', '--retry', '2', '--max-time', '300', '-o', dest, CONFIG.packageUrl],\n" +
1165
- " { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 }\n" +
1166
- ' );\n' +
1167
- ' if (download.error) {\n' +
1168
- ' download.status = download.status == null ? 127 : download.status;\n' +
1169
- " download.stderr = (download.stderr || '') + String(download.error.message || download.error);\n" +
1170
- ' }\n' +
1171
- ' if (download.status !== 0) {\n' +
1172
- ' setBuildError(\n' +
1173
- " 'ANDROID_PACKAGE_DOWNLOAD_FAILED',\n" +
1174
- " 'render package download failed (curl exit ' + download.status + '):\\n' + outputTail(download)\n" +
1175
- ' );\n' +
1176
- ' return;\n' +
1177
- ' }\n' +
1178
- ' urlProjectRoot = prepareShippedProject();\n' +
1179
- ' })();\n' +
1180
- '\n' +
1181
- ' // 2. Per-screen validation + reference copies. Each screen gets its OWN copy\n' +
1182
- ' // named by screen, even when two screens share a reference basename.\n' +
1183
- ' currentEntries.forEach(function (entry) {');
1184
- // References resolve INSIDE the extracted package: repo-relative path minus
1185
- // the packagePath prefix, against the extracted project root.
1186
- main = replaceOnce(main, 'var basename = params.screens[entry.screen];\n' +
1187
- ' if (\n' +
1188
- " typeof basename !== 'string' || basename === '' ||\n" +
1189
- " basename === '.' || basename === '..' ||\n" +
1190
- ' basename !== path.basename(basename)\n' +
1191
- ' ) {\n' +
1192
- ' setError(\n' +
1193
- ' entry,\n' +
1194
- " 'REFERENCE_MISSING',\n" +
1195
- " 'reference for ' + entry.screen + ' must be a plain file basename inside ' +\n" +
1196
- " inputsDir + ', got: ' + JSON.stringify(basename)\n" +
1197
- ' );\n' +
1198
- ' return;\n' +
1199
- ' }\n' +
1200
- ' var source = path.join(inputsDir, basename);', 'var refPath = params.screens[entry.screen];\n' +
1201
- " if (typeof refPath !== 'string' || refPath === '') {\n" +
1202
- ' setError(\n' +
1203
- ' entry,\n' +
1204
- " 'REFERENCE_MISSING',\n" +
1205
- " 'reference for ' + entry.screen + ' must be a non-empty repo-relative path, got: ' +\n" +
1206
- ' JSON.stringify(refPath)\n' +
1207
- ' );\n' +
1208
- ' return;\n' +
1209
- ' }\n' +
1210
- ' if (urlProjectRoot === null) {\n' +
1211
- ' // Package unavailable: the per-build error above carries the cause.\n' +
1212
- ' return;\n' +
1213
- ' }\n' +
1214
- " var packagePrefix = CONFIG.packagePath.replace(/^\\.\\//, '').replace(/\\/+$/, '');\n" +
1215
- " var normalizedRef = refPath.replace(/^\\.\\//, '');\n" +
1216
- " if (normalizedRef.indexOf(packagePrefix + '/') !== 0) {\n" +
1217
- ' setError(\n' +
1218
- ' entry,\n' +
1219
- " 'REFERENCE_MISSING',\n" +
1220
- " 'reference for ' + entry.screen + ' must live inside the shipped package (' +\n" +
1221
- " packagePrefix + '), got: ' + JSON.stringify(refPath)\n" +
1222
- ' );\n' +
1223
- ' return;\n' +
1224
- ' }\n' +
1225
- ' var source = path.join(urlProjectRoot, normalizedRef.slice(packagePrefix.length + 1));');
1226
- // Render from the package extracted in stage 2a — no second extract.
1227
- main = replaceOnce(main, ' // 3. Extract + validate the shipped Gradle project, then render the\n' +
1228
- ' // renderable screens. The project stage runs even when no screen is\n' +
1229
- ' // renderable, so packaging mistakes are always reported.\n' +
1230
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1231
- ' try {\n' +
1232
- ' var projectRoot = prepareShippedProject();\n' +
1233
- ' if (projectRoot !== null) {\n' +
1234
- ' if (renderable.length > 0) {\n' +
1235
- ' renderScreens(renderable, projectRoot);\n' +
1236
- ' } else if (currentEntries.length > 0) {\n' +
1237
- " log('no renderable screens, skipping render');\n" +
1238
- ' }\n' +
1239
- ' }\n' +
1240
- ' } finally {\n' +
1241
- ' cleanupShippedProject();\n' +
1242
- ' }', ' // 3. Render from the package downloaded + extracted in stage 2a. The\n' +
1243
- ' // extraction already reported packaging mistakes as the per-build\n' +
1244
- ' // error, so a null root here just skips the render.\n' +
1245
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1246
- ' try {\n' +
1247
- ' if (urlProjectRoot !== null) {\n' +
1248
- ' if (renderable.length > 0) {\n' +
1249
- ' renderScreens(renderable, urlProjectRoot);\n' +
1250
- ' } else if (currentEntries.length > 0) {\n' +
1251
- " log('no renderable screens, skipping render');\n" +
1252
- ' }\n' +
1253
- ' }\n' +
1254
- ' } finally {\n' +
1255
- ' cleanupShippedProject();\n' +
1256
- ' }');
1257
- return main;
1258
- }
1259
- const ANDROID_RENDER_SCRIPT_MAIN_URL = buildAndroidPackageUrlMain(ANDROID_RENDER_SCRIPT_MAIN);
1260
- /**
1261
- * Generates the self-contained runtime node script for the step. Pure function
1262
- * of its config — exported so tests can execute the script directly.
1263
- *
1264
- * When config.referenceSource is absent the reference block is byte-identical
1265
- * to the default (JSON.stringify drops the undefined key, and the unpatched
1266
- * runtime is used).
1267
- */
1268
- export function generateAndroidRenderScript(config) {
1269
- let main = config.packageSource === 'repo'
1270
- ? ANDROID_RENDER_SCRIPT_MAIN_REPO
1271
- : config.packageSource === 'url'
1272
- ? ANDROID_RENDER_SCRIPT_MAIN_URL
1273
- : ANDROID_RENDER_SCRIPT_MAIN;
1274
- // Reference-from-repo is an independent dimension layered on top of whichever
1275
- // package base applies. Absent → byte-identical reference block (default
1276
- // basename behaviour). URL mode embeds its own reference sourcing and the
1277
- // executor rejects the combination before this point.
1278
- if (config.referenceSource === 'repo') {
1279
- main = buildAndroidReferenceRepoMain(main);
1280
- }
1281
- return [
1282
- "'use strict';",
1283
- '// ui-fidelity-render-android runtime script (generated by cibuild at YAML conversion time)',
1284
- 'var CONFIG = ' + JSON.stringify(config) + ';',
1285
- 'var RENDERER = ' + JSON.stringify(ANDROID_RENDERER) + ';',
1286
- 'var PACKAGE_ARCHIVE_NAME = ' + JSON.stringify(PACKAGE_ARCHIVE_BASENAME) + ';',
1287
- RENDER_SCRIPT_TRIM_STAGE,
1288
- RENDER_SCRIPT_FACTS_STAGE,
1289
- RENDER_SCRIPT_GEOMETRY_STAGE,
1290
- RENDER_SCRIPT_SOURCE_DIGEST_STAGE,
1291
- main,
1292
- ].join('\n');
1293
- }
1294
- /**
1295
- * ui-fidelity-render-android step executor. Returns a node-kind StepDef whose
1296
- * script performs the render at runtime on the macOS runner.
1297
- */
1298
- export class UiFidelityRenderAndroidStepExecutor extends BaseStepExecutor {
1299
- getValidationRequirements(inputs, _env, _config) {
1300
- // No platform-toolchain requirement: trim/align is pure JS inside the ci
1301
- // binary (fidelity-linux 06), so the step validates on macOS and Linux
1302
- // runners alike.
1303
- const requirements = [
1304
- this.requireCommand('tar', 'Archive tool used to extract the shipped Gradle project', 'Install tar on the runner'),
1305
- ];
1306
- // URL mode (fidelity-linux 03) downloads the package at runtime; existing
1307
- // inputs/repo runs gain no new requirement.
1308
- if (this.getInput(inputs, 'package_source', undefined) === 'url') {
1309
- requirements.push(this.requireCommand('curl', 'Downloader used to fetch the render package by signed URL', 'Install curl on the runner'));
1310
- }
1311
- return requirements;
1312
- }
1313
- async execute(inputs, _env, _config) {
1314
- const stepName = 'ui-fidelity-render-android';
1315
- const scale = parseScale(this.getInput(inputs, 'scale', DEFAULT_SCALE));
1316
- const gradleTask = String(this.getInput(inputs, 'gradle_task', DEFAULT_GRADLE_TASK));
1317
- if (!isValidGradleTask(gradleTask)) {
1318
- throw new Error(`Invalid gradle_task '${gradleTask}' for step '${stepName}': ` +
1319
- 'expected a Gradle task path (letters, digits, and : - _)');
1320
- }
1321
- // package_source: validate when provided, but only record (and patch the
1322
- // script for) the non-default modes so omitting it — or setting the default
1323
- // "inputs" — keeps the default archive-extract script byte-identical. In
1324
- // repo mode package_path is required (the committed project to render); in
1325
- // url mode (fidelity-linux 03) package_url and package_path are required
1326
- // (the signed download + the prefix committed reference paths sit under).
1327
- const rawPackageSource = this.getInput(inputs, 'package_source', undefined);
1328
- let packageSource;
1329
- if (rawPackageSource !== undefined) {
1330
- const value = String(rawPackageSource);
1331
- if (!PACKAGE_SOURCES.includes(value)) {
1332
- throw new Error(`Invalid package_source '${value}' for step '${stepName}': ` +
1333
- `expected one of: ${PACKAGE_SOURCES.join(', ')}`);
1334
- }
1335
- if (value === 'repo' || value === 'url') {
1336
- packageSource = value;
1337
- }
1338
- }
1339
- let packagePath;
1340
- if (packageSource === 'repo' || packageSource === 'url') {
1341
- packagePath = String(this.getRequiredInput(inputs, 'package_path', stepName));
1342
- }
1343
- let packageUrl;
1344
- if (packageSource === 'url') {
1345
- packageUrl = String(this.getRequiredInput(inputs, 'package_url', stepName));
1346
- if (!/^https?:\/\//.test(packageUrl)) {
1347
- throw new Error(`Invalid package_url for step '${stepName}': expected an http(s) URL`);
1348
- }
1349
- }
1350
- // reference_source: validate when provided, but only record (and patch the
1351
- // script for) the non-default "repo" so omitting it — or setting the default
1352
- // "inputs" — keeps the default basename reference block intact.
1353
- const rawReferenceSource = this.getInput(inputs, 'reference_source', undefined);
1354
- let referenceSource;
1355
- if (rawReferenceSource !== undefined) {
1356
- const value = String(rawReferenceSource);
1357
- if (!REFERENCE_SOURCES.includes(value)) {
1358
- throw new Error(`Invalid reference_source '${value}' for step '${stepName}': ` +
1359
- `expected one of: ${REFERENCE_SOURCES.join(', ')}`);
1360
- }
1361
- // URL mode owns reference sourcing (inside the extracted package) —
1362
- // an explicit reference_source alongside it is a contradiction.
1363
- if (packageSource === 'url') {
1364
- throw new Error(`Invalid reference_source for step '${stepName}': ` +
1365
- "package_source 'url' resolves references inside the downloaded " +
1366
- 'package; omit reference_source');
1367
- }
1368
- if (value === 'repo') {
1369
- referenceSource = 'repo';
1370
- }
1371
- }
1372
- const config = { scale, gradleTask };
1373
- if (packageSource !== undefined) {
1374
- config.packageSource = packageSource;
1375
- }
1376
- if (packagePath !== undefined) {
1377
- config.packagePath = packagePath;
1378
- }
1379
- if (packageUrl !== undefined) {
1380
- config.packageUrl = packageUrl;
1381
- }
1382
- if (referenceSource !== undefined) {
1383
- config.referenceSource = referenceSource;
1384
- }
1385
- const script = generateAndroidRenderScript(config);
1386
- return this.createNodeStep(script, stepName);
1387
- }
1388
- }
1389
- //# sourceMappingURL=ui-fidelity-render-android.js.map