@invarn/cibuild 2.9.5 → 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 (553) 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 -1092
  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/capture-injection.d.ts +0 -30
  339. package/dist/src/yaml/steps/capture-injection.d.ts.map +0 -1
  340. package/dist/src/yaml/steps/capture-injection.js +0 -88
  341. package/dist/src/yaml/steps/capture-injection.test.d.ts +0 -2
  342. package/dist/src/yaml/steps/capture-injection.test.d.ts.map +0 -1
  343. package/dist/src/yaml/steps/capture-injection.test.js +0 -163
  344. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts +0 -42
  345. package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts.map +0 -1
  346. package/dist/src/yaml/steps/cocoapods-deployment-floor.js +0 -140
  347. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts +0 -11
  348. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts.map +0 -1
  349. package/dist/src/yaml/steps/cocoapods-deployment-floor.test.js +0 -176
  350. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +0 -22
  351. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +0 -1
  352. package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +0 -97
  353. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts +0 -17
  354. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts.map +0 -1
  355. package/dist/src/yaml/steps/cocoapods-locked-bundler.test.js +0 -282
  356. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts +0 -17
  357. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts.map +0 -1
  358. package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.js +0 -132
  359. package/dist/src/yaml/steps/envstore-loader.test.d.ts +0 -15
  360. package/dist/src/yaml/steps/envstore-loader.test.d.ts.map +0 -1
  361. package/dist/src/yaml/steps/envstore-loader.test.js +0 -143
  362. package/dist/src/yaml/steps/fastlane.d.ts +0 -27
  363. package/dist/src/yaml/steps/fastlane.d.ts.map +0 -1
  364. package/dist/src/yaml/steps/fastlane.js +0 -79
  365. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts +0 -53
  366. package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts.map +0 -1
  367. package/dist/src/yaml/steps/fidelity-aspect-guard.js +0 -72
  368. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts +0 -11
  369. package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts.map +0 -1
  370. package/dist/src/yaml/steps/fidelity-aspect-guard.test.js +0 -50
  371. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts +0 -87
  372. package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts.map +0 -1
  373. package/dist/src/yaml/steps/fidelity-reference-provenance.js +0 -111
  374. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts +0 -22
  375. package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts.map +0 -1
  376. package/dist/src/yaml/steps/fidelity-reference-provenance.test.js +0 -118
  377. package/dist/src/yaml/steps/file.d.ts +0 -52
  378. package/dist/src/yaml/steps/file.d.ts.map +0 -1
  379. package/dist/src/yaml/steps/file.js +0 -108
  380. package/dist/src/yaml/steps/flutter.d.ts +0 -63
  381. package/dist/src/yaml/steps/flutter.d.ts.map +0 -1
  382. package/dist/src/yaml/steps/flutter.js +0 -215
  383. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts +0 -16
  384. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts.map +0 -1
  385. package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.js +0 -127
  386. package/dist/src/yaml/steps/git-clone.d.ts +0 -32
  387. package/dist/src/yaml/steps/git-clone.d.ts.map +0 -1
  388. package/dist/src/yaml/steps/git-clone.js +0 -171
  389. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts +0 -10
  390. package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts.map +0 -1
  391. package/dist/src/yaml/steps/git-fetch-retry-exec.test.js +0 -162
  392. package/dist/src/yaml/steps/git-fetch-retry.d.ts +0 -62
  393. package/dist/src/yaml/steps/git-fetch-retry.d.ts.map +0 -1
  394. package/dist/src/yaml/steps/git-fetch-retry.js +0 -155
  395. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts +0 -5
  396. package/dist/src/yaml/steps/git-fetch-retry.test.d.ts.map +0 -1
  397. package/dist/src/yaml/steps/git-fetch-retry.test.js +0 -86
  398. package/dist/src/yaml/steps/google-play-deploy.d.ts +0 -37
  399. package/dist/src/yaml/steps/google-play-deploy.d.ts.map +0 -1
  400. package/dist/src/yaml/steps/google-play-deploy.js +0 -193
  401. package/dist/src/yaml/steps/google-play-deploy.test.d.ts +0 -5
  402. package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +0 -1
  403. package/dist/src/yaml/steps/google-play-deploy.test.js +0 -310
  404. package/dist/src/yaml/steps/gradle-memory.d.ts +0 -61
  405. package/dist/src/yaml/steps/gradle-memory.d.ts.map +0 -1
  406. package/dist/src/yaml/steps/gradle-memory.js +0 -386
  407. package/dist/src/yaml/steps/gradle-memory.test.d.ts +0 -2
  408. package/dist/src/yaml/steps/gradle-memory.test.d.ts.map +0 -1
  409. package/dist/src/yaml/steps/gradle-memory.test.js +0 -344
  410. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts +0 -28
  411. package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts.map +0 -1
  412. package/dist/src/yaml/steps/gradle-sdk-cmake.js +0 -107
  413. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts +0 -2
  414. package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts.map +0 -1
  415. package/dist/src/yaml/steps/gradle-sdk-cmake.test.js +0 -183
  416. package/dist/src/yaml/steps/index.d.ts +0 -10
  417. package/dist/src/yaml/steps/index.d.ts.map +0 -1
  418. package/dist/src/yaml/steps/index.js +0 -1461
  419. package/dist/src/yaml/steps/ios-deps.d.ts +0 -78
  420. package/dist/src/yaml/steps/ios-deps.d.ts.map +0 -1
  421. package/dist/src/yaml/steps/ios-deps.js +0 -279
  422. package/dist/src/yaml/steps/ios-deps.test.d.ts +0 -5
  423. package/dist/src/yaml/steps/ios-deps.test.d.ts.map +0 -1
  424. package/dist/src/yaml/steps/ios-deps.test.js +0 -90
  425. package/dist/src/yaml/steps/ios-signing.d.ts +0 -31
  426. package/dist/src/yaml/steps/ios-signing.d.ts.map +0 -1
  427. package/dist/src/yaml/steps/ios-signing.js +0 -144
  428. package/dist/src/yaml/steps/ios-version.d.ts +0 -47
  429. package/dist/src/yaml/steps/ios-version.d.ts.map +0 -1
  430. package/dist/src/yaml/steps/ios-version.js +0 -151
  431. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts +0 -10
  432. package/dist/src/yaml/steps/java-home-version-spec.test.d.ts.map +0 -1
  433. package/dist/src/yaml/steps/java-home-version-spec.test.js +0 -58
  434. package/dist/src/yaml/steps/linting.d.ts +0 -47
  435. package/dist/src/yaml/steps/linting.d.ts.map +0 -1
  436. package/dist/src/yaml/steps/linting.js +0 -148
  437. package/dist/src/yaml/steps/phase2.test.d.ts +0 -6
  438. package/dist/src/yaml/steps/phase2.test.d.ts.map +0 -1
  439. package/dist/src/yaml/steps/phase2.test.js +0 -197
  440. package/dist/src/yaml/steps/phase3.test.d.ts +0 -5
  441. package/dist/src/yaml/steps/phase3.test.d.ts.map +0 -1
  442. package/dist/src/yaml/steps/phase3.test.js +0 -144
  443. package/dist/src/yaml/steps/phase4.test.d.ts +0 -5
  444. package/dist/src/yaml/steps/phase4.test.d.ts.map +0 -1
  445. package/dist/src/yaml/steps/phase4.test.js +0 -166
  446. package/dist/src/yaml/steps/phase5.test.d.ts +0 -6
  447. package/dist/src/yaml/steps/phase5.test.d.ts.map +0 -1
  448. package/dist/src/yaml/steps/phase5.test.js +0 -263
  449. package/dist/src/yaml/steps/registry.d.ts +0 -88
  450. package/dist/src/yaml/steps/registry.d.ts.map +0 -1
  451. package/dist/src/yaml/steps/registry.js +0 -125
  452. package/dist/src/yaml/steps/registry.test.d.ts +0 -5
  453. package/dist/src/yaml/steps/registry.test.d.ts.map +0 -1
  454. package/dist/src/yaml/steps/registry.test.js +0 -235
  455. package/dist/src/yaml/steps/release.d.ts +0 -50
  456. package/dist/src/yaml/steps/release.d.ts.map +0 -1
  457. package/dist/src/yaml/steps/release.js +0 -154
  458. package/dist/src/yaml/steps/render-post-processor.d.ts +0 -100
  459. package/dist/src/yaml/steps/render-post-processor.d.ts.map +0 -1
  460. package/dist/src/yaml/steps/render-post-processor.js +0 -270
  461. package/dist/src/yaml/steps/render-post-processor.test.d.ts +0 -13
  462. package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +0 -1
  463. package/dist/src/yaml/steps/render-post-processor.test.js +0 -220
  464. package/dist/src/yaml/steps/script.d.ts +0 -23
  465. package/dist/src/yaml/steps/script.d.ts.map +0 -1
  466. package/dist/src/yaml/steps/script.js +0 -63
  467. package/dist/src/yaml/steps/spec-validation.test.d.ts +0 -6
  468. package/dist/src/yaml/steps/spec-validation.test.d.ts.map +0 -1
  469. package/dist/src/yaml/steps/spec-validation.test.js +0 -130
  470. package/dist/src/yaml/steps/steps.test.d.ts +0 -6
  471. package/dist/src/yaml/steps/steps.test.d.ts.map +0 -1
  472. package/dist/src/yaml/steps/steps.test.js +0 -881
  473. package/dist/src/yaml/steps/structural-facts.d.ts +0 -992
  474. package/dist/src/yaml/steps/structural-facts.d.ts.map +0 -1
  475. package/dist/src/yaml/steps/structural-facts.js +0 -3276
  476. package/dist/src/yaml/steps/structural-facts.test.d.ts +0 -12
  477. package/dist/src/yaml/steps/structural-facts.test.d.ts.map +0 -1
  478. package/dist/src/yaml/steps/structural-facts.test.js +0 -3039
  479. package/dist/src/yaml/steps/test-config.d.ts +0 -5
  480. package/dist/src/yaml/steps/test-config.d.ts.map +0 -1
  481. package/dist/src/yaml/steps/test-config.js +0 -25
  482. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +0 -28
  483. package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +0 -1
  484. package/dist/src/yaml/steps/trim-align-corpus.test.js +0 -383
  485. package/dist/src/yaml/steps/trim-align.d.ts +0 -87
  486. package/dist/src/yaml/steps/trim-align.d.ts.map +0 -1
  487. package/dist/src/yaml/steps/trim-align.js +0 -367
  488. package/dist/src/yaml/steps/trim-align.test.d.ts +0 -11
  489. package/dist/src/yaml/steps/trim-align.test.d.ts.map +0 -1
  490. package/dist/src/yaml/steps/trim-align.test.js +0 -261
  491. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +0 -66
  492. package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +0 -1
  493. package/dist/src/yaml/steps/ui-fidelity-preview-android.js +0 -251
  494. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts +0 -13
  495. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts.map +0 -1
  496. package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +0 -302
  497. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts +0 -134
  498. package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +0 -1
  499. package/dist/src/yaml/steps/ui-fidelity-preview.js +0 -173
  500. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts +0 -11
  501. package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts.map +0 -1
  502. package/dist/src/yaml/steps/ui-fidelity-preview.test.js +0 -210
  503. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +0 -155
  504. package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +0 -1
  505. package/dist/src/yaml/steps/ui-fidelity-render-android.js +0 -1389
  506. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts +0 -2
  507. package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts.map +0 -1
  508. package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +0 -1106
  509. package/dist/src/yaml/steps/ui-fidelity-render.d.ts +0 -230
  510. package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +0 -1
  511. package/dist/src/yaml/steps/ui-fidelity-render.js +0 -1597
  512. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts +0 -18
  513. package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts.map +0 -1
  514. package/dist/src/yaml/steps/ui-fidelity-render.test.js +0 -1871
  515. package/dist/src/yaml/steps/xcode-app-product.d.ts +0 -90
  516. package/dist/src/yaml/steps/xcode-app-product.d.ts.map +0 -1
  517. package/dist/src/yaml/steps/xcode-app-product.js +0 -247
  518. package/dist/src/yaml/steps/xcode-app-product.test.d.ts +0 -2
  519. package/dist/src/yaml/steps/xcode-app-product.test.d.ts.map +0 -1
  520. package/dist/src/yaml/steps/xcode-app-product.test.js +0 -202
  521. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts +0 -30
  522. package/dist/src/yaml/steps/xcode-derived-data.test.d.ts.map +0 -1
  523. package/dist/src/yaml/steps/xcode-derived-data.test.js +0 -279
  524. package/dist/src/yaml/steps/xcode-destination.d.ts +0 -47
  525. package/dist/src/yaml/steps/xcode-destination.d.ts.map +0 -1
  526. package/dist/src/yaml/steps/xcode-destination.js +0 -202
  527. package/dist/src/yaml/steps/xcode-destination.test.d.ts +0 -9
  528. package/dist/src/yaml/steps/xcode-destination.test.d.ts.map +0 -1
  529. package/dist/src/yaml/steps/xcode-destination.test.js +0 -227
  530. package/dist/src/yaml/steps/xcode-new.test.d.ts +0 -5
  531. package/dist/src/yaml/steps/xcode-new.test.d.ts.map +0 -1
  532. package/dist/src/yaml/steps/xcode-new.test.js +0 -211
  533. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts +0 -9
  534. package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts.map +0 -1
  535. package/dist/src/yaml/steps/xcode-resolve-retry.test.js +0 -146
  536. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts +0 -53
  537. package/dist/src/yaml/steps/xcode-simulator-archs.d.ts.map +0 -1
  538. package/dist/src/yaml/steps/xcode-simulator-archs.js +0 -98
  539. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts +0 -21
  540. package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts.map +0 -1
  541. package/dist/src/yaml/steps/xcode-simulator-archs.test.js +0 -155
  542. package/dist/src/yaml/steps/xcode.d.ts +0 -230
  543. package/dist/src/yaml/steps/xcode.d.ts.map +0 -1
  544. package/dist/src/yaml/steps/xcode.js +0 -1350
  545. package/dist/src/yaml/types.d.ts +0 -76
  546. package/dist/src/yaml/types.d.ts.map +0 -1
  547. package/dist/src/yaml/types.js +0 -5
  548. package/dist/src/yaml/validation-types.d.ts +0 -102
  549. package/dist/src/yaml/validation-types.d.ts.map +0 -1
  550. package/dist/src/yaml/validation-types.js +0 -8
  551. package/dist/src/yaml/yaml-updater.d.ts +0 -24
  552. package/dist/src/yaml/yaml-updater.d.ts.map +0 -1
  553. package/dist/src/yaml/yaml-updater.js +0 -128
@@ -1,1597 +0,0 @@
1
- /**
2
- * ui-fidelity-render step implementation.
3
- *
4
- * Renders parameterless SwiftUI views from a user's SwiftPM package to PNGs
5
- * with SwiftUI's ImageRenderer on a macOS runner — no app build, no
6
- * simulator. The step returns a self-contained node script that, at runtime:
7
- *
8
- * 1. Reads `.ci/inputs/params.json`
9
- * ({ "screens": { "<ViewTypeName>": "<referenceFileBasename>" } });
10
- * reference images live at `.ci/inputs/<basename>`.
11
- * Optional reference-provenance sidecar: `"render_locale": "<locale>"`
12
- * plus `"provenance": { "<ViewTypeName>": { figmaNode, locale, resolved,
13
- * exportedAt } }`. The gate arms per screen from provenance presence and
14
- * checks METADATA only (never pixels/OCR): an absent reference is FATAL
15
- * (status `unverifiable`, REFERENCE_ABSENT); `resolved == false` or
16
- * `locale != render_locale` downgrade ONLY the copy channel — the screen
17
- * renders and is judged on every non-copy channel, and the downgrade
18
- * rides as a per-screen `copy_advisory` (`copy_unverifiable` + reason).
19
- * Unprovenanced screens keep legacy behaviour (no NO_PROVENANCE arming
20
- * on iOS).
21
- * 2. Synthesizes a throwaway SwiftPM harness package that depends on the
22
- * user's package and contains ONE executable target per screen, so a
23
- * compile failure for one screen (unknown view type, unavailable init)
24
- * never breaks the others. A screen-free probe target distinguishes
25
- * "this screen is broken" from "the whole package does not build for
26
- * macOS" (RENDER_UNSUPPORTED).
27
- * 3. Builds and runs each per-screen executable, which constructs the view
28
- * strictly as `<ViewTypeName>()`, renders at render_size x scale on the
29
- * MainActor, and writes a PNG via CGImage + ImageIO.
30
- * 4. Writes, inside the artifacts dir (${CIBUILD_ARTIFACTS_DIR:-.ci/artifacts}):
31
- * - ui-fidelity/rendered/<Screen>.png
32
- * - ui-fidelity/references/<Screen>.png (one copy per screen, even when
33
- * two screens share a reference basename)
34
- * - protocol-result.json (always written, even on partial/total failure;
35
- * when params.json is absent/malformed the screens array is empty
36
- * because screens cannot be enumerated)
37
- * 5. Exits non-zero iff any screen is not "rendered".
38
- *
39
- * protocol-result.json contains relative paths only: image paths are relative
40
- * to the artifacts dir, and error messages are sanitized so absolute runner
41
- * paths (resolved package_path, harness temp dir, extracted shipped package,
42
- * compiler diagnostics) are rewritten to relative paths or placeholders
43
- * before they are stored.
44
- *
45
- * The package_source input picks where the SwiftPM package comes from:
46
- * - "repo" (default): the package lives in the checkout at package_path.
47
- * Omitting the input generates a byte-identical script to the original
48
- * step (locked by a snapshot test), and the result document keeps its
49
- * original { renderer, screens } shape.
50
- * - "inputs": the caller ships the package as .ci/inputs/package.tar.gz.
51
- * The archive is extracted into a scratch directory and validated
52
- * (single package root, Package.swift at its top, bounded extracted
53
- * size); rendering then proceeds exactly as in repo mode. Validation
54
- * failures are per-build structured errors (PACKAGE_ARCHIVE_MISSING,
55
- * PACKAGE_ARCHIVE_INVALID, PACKAGE_MANIFEST_MISSING,
56
- * PACKAGE_ARCHIVE_TOO_LARGE, PACKAGE_TARGET_UNRESOLVED) recorded as a
57
- * top-level error object in the result document, distinct from
58
- * per-screen render errors. With an explicit package_source the result
59
- * document is { renderer, package_source, error, screens }.
60
- */
61
- import { BaseStepExecutor } from './base.js';
62
- import { RENDER_SCRIPT_TRIM_STAGE } from './render-post-processor.js';
63
- import { RENDER_SCRIPT_SOURCE_DIGEST_STAGE } from '../fidelity-input-digest.js';
64
- /**
65
- * Default render size in device points. 393x852 is the iPhone-class portrait
66
- * canvas shared by the iPhone 14 Pro / 15 / 16 — the most common modern
67
- * iPhone logical resolution, so renders line up with design references by
68
- * default.
69
- */
70
- export const DEFAULT_RENDER_SIZE = '393x852';
71
- /** Default display scale (@2x), matching how references are typically exported. */
72
- export const DEFAULT_SCALE = 2;
73
- /** Valid values for the package_source input. */
74
- export const PACKAGE_SOURCES = ['repo', 'inputs', 'url'];
75
- /** Default package source: the package lives in the repo checkout. */
76
- export const DEFAULT_PACKAGE_SOURCE = 'repo';
77
- /** Valid values for the reference_source input. */
78
- export const REFERENCE_SOURCES = ['repo', 'inputs'];
79
- /** Default reference source: references arrive as run inputs (v1 behaviour). */
80
- export const DEFAULT_REFERENCE_SOURCE = 'inputs';
81
- /** Basename of the shipped package archive inside the run-inputs directory. */
82
- export const PACKAGE_ARCHIVE_BASENAME = 'package.tar.gz';
83
- /**
84
- * Default cap on the extracted size of a shipped package archive (32 MiB),
85
- * overridable at runtime via UI_FIDELITY_MAX_PACKAGE_BYTES. The archive
86
- * arrives through a size-limited dispatch channel (256 KiB), and gzip tops
87
- * out around a 1000:1 ratio, so this bound is a decompression-bomb guard
88
- * with enormous headroom for legitimate source slices, which are typically
89
- * well under 1 MiB extracted.
90
- */
91
- export const DEFAULT_MAX_EXTRACTED_BYTES = 32 * 1024 * 1024;
92
- /**
93
- * Parses a "<width>x<height>" render size string (device points).
94
- * @throws Error when the value is malformed or non-positive
95
- */
96
- export function parseRenderSize(value) {
97
- const match = /^(\d+)x(\d+)$/.exec(String(value).trim());
98
- const width = match ? Number(match[1]) : 0;
99
- const height = match ? Number(match[2]) : 0;
100
- if (!match || width <= 0 || height <= 0) {
101
- throw new Error(`Invalid render_size '${value}' for step 'ui-fidelity-render': ` +
102
- `expected "<width>x<height>" in device points, e.g. "${DEFAULT_RENDER_SIZE}"`);
103
- }
104
- return { width, height };
105
- }
106
- /**
107
- * Parses the scale input into a positive finite number.
108
- * @throws Error when the value is not a positive number
109
- */
110
- export function parseScale(value) {
111
- const scale = typeof value === 'number' ? value : Number(String(value).trim());
112
- if (!Number.isFinite(scale) || scale <= 0) {
113
- throw new Error(`Invalid scale '${value}' for step 'ui-fidelity-render': expected a positive number`);
114
- }
115
- return scale;
116
- }
117
- /**
118
- * Swift source generators, written as plain dependency-free JS so the exact
119
- * same code is embedded into the runtime node script AND directly evaluable
120
- * in unit tests (see getRenderScriptInternals). String.raw keeps escape
121
- * sequences literal; the body must not contain backticks or "${".
122
- */
123
- const RENDER_SCRIPT_GENERATORS = String.raw `
124
- // ---- Swift source generation (pure string builders) ----
125
-
126
- function swiftStringLiteral(value) {
127
- var out = '"';
128
- var s = String(value);
129
- for (var i = 0; i < s.length; i++) {
130
- var ch = s[i];
131
- var code = s.charCodeAt(i);
132
- if (ch === '\\') out += '\\\\';
133
- else if (ch === '"') out += '\\"';
134
- else if (ch === '\n') out += '\\n';
135
- else if (ch === '\r') out += '\\r';
136
- else if (ch === '\t') out += '\\t';
137
- else if (code < 0x20) out += '\\u{' + code.toString(16) + '}';
138
- else out += ch;
139
- }
140
- return out + '"';
141
- }
142
-
143
- // One executable target (and product) per screen: SwiftPM compiles the user
144
- // package once, while a per-screen harness compile failure stays isolated to
145
- // that screen. RenderProbe imports the user package without referencing any
146
- // screen, so its failure means the package itself does not build for macOS.
147
- function generateHarnessPackageSwift(screens, options) {
148
- var dependency =
149
- '.product(name: ' + swiftStringLiteral(options.target) +
150
- ', package: ' + swiftStringLiteral(options.packageRef) + ')';
151
- var lines = [];
152
- lines.push('// swift-tools-version:5.9');
153
- lines.push('import PackageDescription');
154
- lines.push('');
155
- lines.push('let package = Package(');
156
- lines.push(' name: "UIFidelityRenderHarness",');
157
- lines.push(' platforms: [.macOS(.v13)],');
158
- lines.push(' products: [');
159
- screens.forEach(function (screen) {
160
- lines.push(
161
- ' .executable(name: "Render_' + screen +
162
- '", targets: ["Render_' + screen + '"]),'
163
- );
164
- });
165
- lines.push(' ],');
166
- lines.push(' dependencies: [');
167
- lines.push(' .package(path: ' + swiftStringLiteral(options.packagePath) + ')');
168
- lines.push(' ],');
169
- lines.push(' targets: [');
170
- lines.push(' .executableTarget(name: "RenderProbe", dependencies: [' + dependency + ']),');
171
- screens.forEach(function (screen) {
172
- lines.push(
173
- ' .executableTarget(name: "Render_' + screen +
174
- '", dependencies: [' + dependency + ']),'
175
- );
176
- });
177
- lines.push(' ]');
178
- lines.push(')');
179
- return lines.join('\n') + '\n';
180
- }
181
-
182
- function generateProbeSwift(options) {
183
- var lines = [];
184
- lines.push('import ' + options.target);
185
- lines.push('');
186
- lines.push('@main');
187
- lines.push('struct Probe {');
188
- lines.push(' static func main() {}');
189
- lines.push('}');
190
- return lines.join('\n') + '\n';
191
- }
192
-
193
- // ImageRenderer is MainActor-isolated, so the @main entry point runs on the
194
- // MainActor. The view is constructed strictly via the parameterless init.
195
- function generateScreenSwift(screen, options) {
196
- var lines = [];
197
- lines.push('import Foundation');
198
- lines.push('import ImageIO');
199
- lines.push('import SwiftUI');
200
- lines.push('import UniformTypeIdentifiers');
201
- lines.push('import ' + options.target);
202
- lines.push('');
203
- lines.push('@main');
204
- lines.push('struct RenderEntry {');
205
- lines.push(' @MainActor');
206
- lines.push(' static func main() {');
207
- lines.push(' let arguments = CommandLine.arguments');
208
- lines.push(' guard arguments.count >= 2 else {');
209
- lines.push(' FileHandle.standardError.write(Data("usage: Render_' + screen + ' <output.png>".utf8))');
210
- lines.push(' exit(64)');
211
- lines.push(' }');
212
- lines.push(' let outputPath = arguments[1]');
213
- lines.push(' let width: CGFloat = ' + options.width);
214
- lines.push(' let height: CGFloat = ' + options.height);
215
- lines.push(' let view = ' + screen + '().frame(width: width, height: height)');
216
- lines.push(' let renderer = ImageRenderer(content: view)');
217
- lines.push(' renderer.scale = ' + options.scale);
218
- lines.push(' renderer.proposedSize = ProposedViewSize(width: width, height: height)');
219
- lines.push(' guard let image = renderer.cgImage else {');
220
- lines.push(' FileHandle.standardError.write(Data("ImageRenderer produced no image for ' + screen + '".utf8))');
221
- lines.push(' exit(65)');
222
- lines.push(' }');
223
- lines.push(' let url = URL(fileURLWithPath: outputPath)');
224
- lines.push(' guard let destination = CGImageDestinationCreateWithURL(url as CFURL, UTType.png.identifier as CFString, 1, nil) else {');
225
- lines.push(' FileHandle.standardError.write(Data(("could not create PNG destination at " + outputPath).utf8))');
226
- lines.push(' exit(66)');
227
- lines.push(' }');
228
- lines.push(' CGImageDestinationAddImage(destination, image, nil)');
229
- lines.push(' guard CGImageDestinationFinalize(destination) else {');
230
- lines.push(' FileHandle.standardError.write(Data(("could not write PNG to " + outputPath).utf8))');
231
- lines.push(' exit(66)');
232
- lines.push(' }');
233
- lines.push(' }');
234
- lines.push('}');
235
- return lines.join('\n') + '\n';
236
- }
237
- `;
238
- /**
239
- * Runtime body of the generated node script. Plain dependency-free CJS
240
- * (the runner executes it via "node -e"). Must not contain backticks or
241
- * "${" — String.raw keeps escape sequences literal.
242
- */
243
- const RENDER_SCRIPT_MAIN = String.raw `
244
- // ---- runtime ----
245
-
246
- var fs = require('fs');
247
- var path = require('path');
248
- var os = require('os');
249
- var cp = require('child_process');
250
-
251
- var RENDERER = 'imagerenderer-spm';
252
- var SWIFT_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
253
-
254
- var artifactsDir = process.env.CIBUILD_ARTIFACTS_DIR || '.ci/artifacts';
255
- var inputsDir = path.join('.ci', 'inputs');
256
- var paramsPath = path.join(inputsDir, 'params.json');
257
- var resultPath = path.join(artifactsDir, 'protocol-result.json');
258
-
259
- var currentEntries = [];
260
-
261
- function log(message) {
262
- console.log('[ui-fidelity-render] ' + message);
263
- }
264
-
265
- function logError(message) {
266
- console.error('[ui-fidelity-render] ' + message);
267
- }
268
-
269
- // ---- result-document path hygiene ----
270
- //
271
- // protocol-result.json must contain relative paths only. Error messages can
272
- // embed absolute runner paths (resolved package_path, harness temp dir,
273
- // swift compiler diagnostics, copy failures), so every message stored in the
274
- // result document is passed through sanitizeMessage(), which rewrites each
275
- // known absolute directory -- and its symlink-resolved variant, since macOS
276
- // tools print /var/folders/... as /private/var/folders/... -- to a relative
277
- // path or a stable placeholder.
278
-
279
- var pathReplacements = [];
280
-
281
- function registerPathReplacement(absolutePath, replacement) {
282
- if (!absolutePath || !path.isAbsolute(absolutePath)) return;
283
- pathReplacements.push([absolutePath, replacement]);
284
- try {
285
- var real = fs.realpathSync(absolutePath);
286
- if (real !== absolutePath) pathReplacements.push([real, replacement]);
287
- } catch (realpathError) {
288
- // path does not exist (yet) -- keep the literal form only
289
- }
290
- }
291
-
292
- function sanitizeMessage(message) {
293
- var sanitized = String(message);
294
- pathReplacements
295
- .slice()
296
- .sort(function (a, b) { return b[0].length - a[0].length; })
297
- .forEach(function (pair) {
298
- sanitized = sanitized.split(pair[0]).join(pair[1]);
299
- });
300
- return sanitized;
301
- }
302
-
303
- var resolvedPackagePath = path.resolve(CONFIG.packagePath);
304
- registerPathReplacement(
305
- resolvedPackagePath,
306
- path.isAbsolute(CONFIG.packagePath) ? '<package_path>' : CONFIG.packagePath
307
- );
308
- registerPathReplacement(
309
- path.resolve(artifactsDir),
310
- path.isAbsolute(artifactsDir) ? '<artifacts>' : artifactsDir
311
- );
312
- registerPathReplacement(process.cwd(), '.');
313
- registerPathReplacement(os.tmpdir(), '<tmp>');
314
-
315
- // protocol-result.json is ALWAYS written, with one entry per screen in
316
- // params.json order, even on partial or total failure. All paths inside it
317
- // are relative to the artifacts dir.
318
- function writeResult() {
319
- fs.mkdirSync(artifactsDir, { recursive: true });
320
- var doc = { renderer: RENDERER, screens: currentEntries };
321
- fs.writeFileSync(resultPath, JSON.stringify(doc, null, 2) + '\n');
322
- log('wrote ' + resultPath);
323
- }
324
-
325
- function setError(entry, code, message) {
326
- entry.status = 'render_failed';
327
- entry.error = { code: code, message: sanitizeMessage(message) };
328
- }
329
-
330
- // A reference the gate cannot honestly judge at all: a hard, mechanical
331
- // metadata verdict (never pixels/OCR), distinct from render_failed so a rotten
332
- // reference fails loud instead of producing a confident-but-false PASS.
333
- function setUnverifiable(entry, code, message) {
334
- entry.status = 'unverifiable';
335
- entry.error = { code: code, message: sanitizeMessage(message) };
336
- }
337
-
338
- // Inlined twins of src/yaml/steps/fidelity-reference-provenance.ts (this script
339
- // ships self-contained and cannot import it). The render-step integration tests
340
- // exercise BOTH against the same fixtures so they cannot silently drift. The
341
- // iOS gate arms ONLY from provenance presence — the step never refused
342
- // unprovenanced screens, so NO_PROVENANCE (an Android-legacy arming via
343
- // render_locale) is deliberately not wired here.
344
-
345
- // Provenance is only "present" when the fields the gate reasons over are there.
346
- // A partial object (e.g. a hand-dropped { figmaNode }) is treated as no
347
- // provenance — verifiable is earned only through the full export path.
348
- function hasReferenceProvenance(provenance) {
349
- return (
350
- provenance != null &&
351
- typeof provenance === 'object' &&
352
- typeof provenance.locale === 'string' &&
353
- typeof provenance.resolved === 'boolean'
354
- );
355
- }
356
-
357
- // Assesses one provenanced screen's reference. Returns { fault, copyAdvisory }:
358
- // a fatal fault makes the screen unverifiable; a copy advisory downgrades only
359
- // the copy channel (the screen still renders and is judged on every non-copy
360
- // channel). An absent reference wins over a downgrade — there is nothing to
361
- // judge against.
362
- function assessReferenceProvenance(screen, provenance, renderLocale, referenceExists) {
363
- if (!referenceExists) {
364
- return {
365
- fault: {
366
- code: 'REFERENCE_ABSENT',
367
- message: 'reference image for ' + screen + ' is absent',
368
- },
369
- copyAdvisory: null,
370
- };
371
- }
372
- if (provenance.resolved === false) {
373
- return {
374
- fault: null,
375
- copyAdvisory: {
376
- code: 'copy_unverifiable',
377
- reason: 'unresolved_placeholders',
378
- message:
379
- 'reference for ' + screen + ' carries unresolved copy placeholders ' +
380
- '(resolved=false); copy comparisons are skipped — non-copy judging ' +
381
- 'still holds',
382
- },
383
- };
384
- }
385
- if (renderLocale !== null && provenance.locale !== renderLocale) {
386
- return {
387
- fault: null,
388
- copyAdvisory: {
389
- code: 'copy_unverifiable',
390
- reason: 'locale_mismatch',
391
- message:
392
- 'reference for ' + screen + ' is locale "' + provenance.locale +
393
- '" but the render locale is "' + renderLocale + '"; copy comparisons ' +
394
- 'are skipped — non-copy judging still holds',
395
- },
396
- };
397
- }
398
- return { fault: null, copyAdvisory: null };
399
- }
400
-
401
- function outputTail(result) {
402
- var combined = ((result.stdout || '') + '\n' + (result.stderr || '')).trim();
403
- if (combined.length > 2000) combined = '...' + combined.slice(-2000);
404
- return combined;
405
- }
406
-
407
- function runSwift(args) {
408
- log('swift ' + args.join(' '));
409
- var result = cp.spawnSync('swift', args, {
410
- encoding: 'utf-8',
411
- maxBuffer: 16 * 1024 * 1024,
412
- });
413
- if (result.stdout) process.stdout.write(result.stdout);
414
- if (result.stderr) process.stderr.write(result.stderr);
415
- if (result.error) {
416
- result.status = result.status == null ? 127 : result.status;
417
- result.stderr =
418
- (result.stderr || '') + String(result.error.message || result.error);
419
- }
420
- return result;
421
- }
422
-
423
- function renderWithHarness(renderable, packagePath) {
424
- // Snapshot the user package's input files BEFORE any Swift build, so build
425
- // products (.build, DerivedData) never perturb the content key. A package is
426
- // safely cacheable only when it is hermetic — its own files are its whole
427
- // dependency closure; a non-hermetic one emits no digest and so is never
428
- // skipped by a consumer.
429
- var packageFiles = __fidCollectPackageFiles(packagePath);
430
- var packageHermetic = __fidIsHermetic(packageFiles);
431
- var harnessDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-fidelity-harness-'));
432
- registerPathReplacement(harnessDir, '<harness>');
433
- log('harness package: ' + harnessDir);
434
- try {
435
- var screens = renderable.map(function (entry) { return entry.screen; });
436
- var generatorOptions = {
437
- packagePath: packagePath,
438
- packageRef: path.basename(packagePath),
439
- target: CONFIG.target,
440
- width: CONFIG.width,
441
- height: CONFIG.height,
442
- scale: CONFIG.scale,
443
- };
444
-
445
- fs.writeFileSync(
446
- path.join(harnessDir, 'Package.swift'),
447
- generateHarnessPackageSwift(screens, generatorOptions)
448
- );
449
- var probeDir = path.join(harnessDir, 'Sources', 'RenderProbe');
450
- fs.mkdirSync(probeDir, { recursive: true });
451
- fs.writeFileSync(path.join(probeDir, 'Probe.swift'), generateProbeSwift(generatorOptions));
452
- screens.forEach(function (screen) {
453
- var sourceDir = path.join(harnessDir, 'Sources', 'Render_' + screen);
454
- fs.mkdirSync(sourceDir, { recursive: true });
455
- fs.writeFileSync(
456
- path.join(sourceDir, 'Render.swift'),
457
- generateScreenSwift(screen, generatorOptions)
458
- );
459
- });
460
-
461
- // Harness-level gate: the probe imports the user package without touching
462
- // any screen. If it fails, the package does not resolve/compile for
463
- // macOS and EVERY screen is marked RENDER_UNSUPPORTED.
464
- var probe = runSwift(['build', '--package-path', harnessDir, '--target', 'RenderProbe']);
465
- if (probe.status !== 0) {
466
- var probeDetail = outputTail(probe);
467
- currentEntries.forEach(function (entry) {
468
- setError(
469
- entry,
470
- 'RENDER_UNSUPPORTED',
471
- 'the package at ' + packagePath + ' does not build for macOS:\n' + probeDetail
472
- );
473
- });
474
- return;
475
- }
476
-
477
- renderable.forEach(function (entry) {
478
- var product = 'Render_' + entry.screen;
479
- var build = runSwift(['build', '--package-path', harnessDir, '--product', product]);
480
- if (build.status !== 0) {
481
- setError(
482
- entry,
483
- 'VIEW_COMPILE_FAILED',
484
- 'harness for ' + entry.screen + ' did not compile (is ' + entry.screen +
485
- ' a public View with a parameterless init in ' + CONFIG.target + '?):\n' +
486
- outputTail(build)
487
- );
488
- return;
489
- }
490
- var relative = 'ui-fidelity/rendered/' + entry.screen + '.png';
491
- var outputPath = path.resolve(artifactsDir, relative);
492
- var run = runSwift(['run', '--package-path', harnessDir, '--skip-build', product, outputPath]);
493
- if (run.status !== 0) {
494
- setError(
495
- entry,
496
- 'RENDER_FAILED',
497
- 'renderer for ' + entry.screen + ' exited with code ' + run.status + ':\n' + outputTail(run)
498
- );
499
- return;
500
- }
501
- if (!fs.existsSync(outputPath)) {
502
- setError(
503
- entry,
504
- 'RENDER_FAILED',
505
- 'renderer for ' + entry.screen + ' exited 0 but produced no PNG at ' + outputPath
506
- );
507
- return;
508
- }
509
- entry.rendered_image_path = relative;
510
- entry.status = 'rendered';
511
- entry.error = null;
512
- // Content key over the package source + this screen's reference: lets a
513
- // consumer skip a re-render whose inputs are unchanged. Emitted only for a
514
- // hermetic package; otherwise omitted so no stale verdict can be replayed.
515
- if (packageHermetic) {
516
- var referenceBytes = null;
517
- try {
518
- if (entry.reference_image_path) {
519
- referenceBytes = fs.readFileSync(path.resolve(artifactsDir, entry.reference_image_path));
520
- }
521
- } catch (digestReferenceError) {
522
- referenceBytes = null;
523
- }
524
- entry.source_digest = __fidSourceDigest(packageFiles, referenceBytes);
525
- }
526
- log('rendered ' + entry.screen + ' -> ' + relative);
527
- });
528
- } finally {
529
- if (process.env.UI_FIDELITY_KEEP_HARNESS) {
530
- log('keeping harness package (UI_FIDELITY_KEEP_HARNESS set)');
531
- } else {
532
- try {
533
- fs.rmSync(harnessDir, { recursive: true, force: true });
534
- } catch (cleanupError) {
535
- // best effort
536
- }
537
- }
538
- }
539
- }
540
-
541
- function main() {
542
- // 1. Read and validate .ci/inputs/params.json.
543
- var paramsProblem = null;
544
- var params = null;
545
- if (!fs.existsSync(paramsPath)) {
546
- paramsProblem = 'not found at ' + paramsPath;
547
- } else {
548
- try {
549
- params = JSON.parse(fs.readFileSync(paramsPath, 'utf-8'));
550
- } catch (parseError) {
551
- paramsProblem = 'is not valid JSON: ' +
552
- (parseError && parseError.message ? parseError.message : String(parseError));
553
- }
554
- }
555
- if (!paramsProblem) {
556
- if (
557
- !params || typeof params !== 'object' || Array.isArray(params) ||
558
- !params.screens || typeof params.screens !== 'object' || Array.isArray(params.screens)
559
- ) {
560
- paramsProblem =
561
- 'must have the shape { "screens": { "<ViewTypeName>": "<referenceFileBasename>" } }';
562
- }
563
- }
564
- if (paramsProblem) {
565
- // Screens cannot be enumerated, so the result document is written with
566
- // an empty screens array and the step fails.
567
- logError('params.json ' + paramsProblem);
568
- writeResult();
569
- return 1;
570
- }
571
-
572
- // Reference-provenance sidecar (optional): per-screen export provenance plus
573
- // the run's declared render locale. The gate arms per screen from provenance
574
- // presence; the locale is only needed for the locale comparison itself.
575
- var renderLocale =
576
- typeof params.render_locale === 'string' && params.render_locale !== ''
577
- ? params.render_locale
578
- : null;
579
- var provenanceMap =
580
- params.provenance && typeof params.provenance === 'object' && !Array.isArray(params.provenance)
581
- ? params.provenance
582
- : {};
583
-
584
- var screens = Object.keys(params.screens);
585
- currentEntries = screens.map(function (screen) {
586
- return {
587
- screen: screen,
588
- status: 'render_failed',
589
- error: null,
590
- copy_advisory: null,
591
- reference_image_path: null,
592
- rendered_image_path: null,
593
- };
594
- });
595
- log('screens: ' + (screens.join(', ') || '(none)'));
596
-
597
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'rendered'), { recursive: true });
598
- fs.mkdirSync(path.join(artifactsDir, 'ui-fidelity', 'references'), { recursive: true });
599
-
600
- // 2. Per-screen validation + reference copies. Each screen gets its OWN
601
- // copy named by screen, even when two screens share a reference
602
- // basename.
603
- currentEntries.forEach(function (entry) {
604
- if (!SWIFT_IDENTIFIER.test(entry.screen)) {
605
- setError(
606
- entry,
607
- 'INVALID_SCREEN_NAME',
608
- 'screen name ' + JSON.stringify(entry.screen) + ' is not a valid Swift type identifier'
609
- );
610
- return;
611
- }
612
- var basename = params.screens[entry.screen];
613
- if (
614
- typeof basename !== 'string' || basename === '' ||
615
- basename === '.' || basename === '..' ||
616
- basename !== path.basename(basename)
617
- ) {
618
- setError(
619
- entry,
620
- 'REFERENCE_MISSING',
621
- 'reference for ' + entry.screen + ' must be a plain file basename inside ' +
622
- inputsDir + ', got: ' + JSON.stringify(basename)
623
- );
624
- return;
625
- }
626
- var source = path.join(inputsDir, basename);
627
- var sourceStat = null;
628
- try {
629
- sourceStat = fs.statSync(source);
630
- } catch (statError) {
631
- sourceStat = null;
632
- }
633
- var referenceExists = !!(sourceStat && sourceStat.isFile());
634
-
635
- var screenProvenance = provenanceMap[entry.screen];
636
- if (hasReferenceProvenance(screenProvenance)) {
637
- // Provenance assessment (armed by provenance presence): an absent
638
- // reference is FATAL (unverifiable — never a silent PASS); unresolved
639
- // placeholders and locale mismatches downgrade ONLY the copy channel —
640
- // the screen renders and is judged on every non-copy channel, and the
641
- // downgrade rides in the result as a structured copy_advisory.
642
- var assessment = assessReferenceProvenance(
643
- entry.screen,
644
- screenProvenance,
645
- renderLocale,
646
- referenceExists
647
- );
648
- if (assessment.fault) {
649
- setUnverifiable(entry, assessment.fault.code, assessment.fault.message);
650
- return;
651
- }
652
- if (assessment.copyAdvisory) {
653
- entry.copy_advisory = {
654
- code: assessment.copyAdvisory.code,
655
- reason: assessment.copyAdvisory.reason,
656
- message: sanitizeMessage(assessment.copyAdvisory.message),
657
- };
658
- log(
659
- entry.screen + ': copy channel downgraded (' +
660
- assessment.copyAdvisory.reason + '), non-copy judging proceeds'
661
- );
662
- }
663
- } else if (!referenceExists) {
664
- // Legacy (no provenance for this screen): an absent reference stays a
665
- // render_failed REFERENCE_MISSING, exactly as before.
666
- setError(
667
- entry,
668
- 'REFERENCE_MISSING',
669
- sourceStat
670
- ? 'reference for ' + entry.screen + ' is not a regular file: ' + source
671
- : 'reference image not found: ' + source
672
- );
673
- return;
674
- }
675
- var relative = 'ui-fidelity/references/' + entry.screen + '.png';
676
- // The copy is guarded per screen so an unexpected filesystem failure
677
- // never escapes this forEach and takes the other screens down with it.
678
- try {
679
- fs.copyFileSync(source, path.join(artifactsDir, relative));
680
- } catch (copyError) {
681
- setError(
682
- entry,
683
- 'REFERENCE_MISSING',
684
- 'could not copy reference for ' + entry.screen + ': ' +
685
- (copyError && copyError.message ? copyError.message : String(copyError))
686
- );
687
- return;
688
- }
689
- entry.reference_image_path = relative;
690
- });
691
-
692
- // 3. Render the remaining screens through the synthesized harness.
693
- var renderable = currentEntries.filter(function (entry) { return entry.error === null; });
694
- if (renderable.length > 0) {
695
- if (!fs.existsSync(resolvedPackagePath)) {
696
- currentEntries.forEach(function (entry) {
697
- setError(entry, 'RENDER_UNSUPPORTED', 'package_path does not exist: ' + resolvedPackagePath);
698
- });
699
- } else {
700
- renderWithHarness(renderable, resolvedPackagePath);
701
- }
702
- } else if (currentEntries.length > 0) {
703
- log('no renderable screens, skipping harness build');
704
- }
705
-
706
- // 4. Exit code: zero only when every screen rendered.
707
- writeResult();
708
- var failed = currentEntries.filter(function (entry) { return entry.status !== 'rendered'; });
709
- if (failed.length > 0) {
710
- logError(failed.length + ' of ' + currentEntries.length + ' screen(s) not rendered');
711
- return 1;
712
- }
713
- log('all ' + currentEntries.length + ' screen(s) rendered');
714
- return 0;
715
- }
716
-
717
- var exitCode = 1;
718
- try {
719
- exitCode = main();
720
- } catch (unexpectedError) {
721
- logError(
722
- 'unexpected failure: ' +
723
- (unexpectedError && unexpectedError.stack ? unexpectedError.stack : String(unexpectedError))
724
- );
725
- try {
726
- currentEntries.forEach(function (entry) {
727
- if (entry.status !== 'rendered' && entry.error === null) {
728
- setError(
729
- entry,
730
- 'INTERNAL_ERROR',
731
- String(unexpectedError && unexpectedError.message ? unexpectedError.message : unexpectedError)
732
- );
733
- }
734
- });
735
- writeResult();
736
- } catch (writeError) {
737
- logError('could not write protocol-result.json: ' + writeError);
738
- }
739
- exitCode = 1;
740
- }
741
- process.exit(exitCode);
742
- `;
743
- /**
744
- * Runtime support for package_source "inputs": locating, extracting, and
745
- * validating the shipped archive, plus target discovery from its manifest.
746
- * Inserted into the runtime script only when package_source is explicitly
747
- * configured. Plain dependency-free CJS; must not contain backticks, "${",
748
- * or bare $UPPERCASE tokens (the pre-execution validator would parse those
749
- * as variable references).
750
- */
751
- const RENDER_SCRIPT_PACKAGE_STAGE = String.raw `
752
- // ---- shipped-package stage (package_source: "inputs") ----
753
- //
754
- // The caller ships a SwiftPM package as .ci/inputs/package.tar.gz. Before
755
- // any rendering, the archive is located, extracted into a scratch
756
- // directory, and validated. Failures here are PER-BUILD structured errors,
757
- // recorded as the result document's top-level error object -- distinct from
758
- // per-screen render errors, which keep their v1 semantics. The extracted
759
- // size is bounded as a decompression-bomb guard.
760
-
761
- var PACKAGE_ARCHIVE_NAME = 'package.tar.gz';
762
- var DEFAULT_MAX_EXTRACTED_BYTES = 32 * 1024 * 1024;
763
- var maxExtractedBytes =
764
- Number(process.env.UI_FIDELITY_MAX_PACKAGE_BYTES || '') || DEFAULT_MAX_EXTRACTED_BYTES;
765
-
766
- var buildError = null;
767
- var activeTarget = CONFIG.target;
768
- var shippedExtractDir = null;
769
-
770
- function setBuildError(code, message) {
771
- buildError = { code: code, message: sanitizeMessage(message) };
772
- logError(code + ': ' + buildError.message);
773
- }
774
-
775
- function runTar(args) {
776
- var result = cp.spawnSync('tar', args, {
777
- encoding: 'utf-8',
778
- maxBuffer: 64 * 1024 * 1024,
779
- });
780
- if (result.error) {
781
- result.status = result.status == null ? 127 : result.status;
782
- result.stderr =
783
- (result.stderr || '') + String(result.error.message || result.error);
784
- }
785
- return result;
786
- }
787
-
788
- function isUnsafeArchiveMember(name) {
789
- if (name.charAt(0) === '/') return true;
790
- var segments = name.split('/');
791
- for (var i = 0; i < segments.length; i++) {
792
- if (segments[i] === '..') return true;
793
- }
794
- return false;
795
- }
796
-
797
- function extractedSize(dir) {
798
- var total = 0;
799
- var stack = [dir];
800
- while (stack.length > 0) {
801
- var current = stack.pop();
802
- var names = fs.readdirSync(current);
803
- for (var i = 0; i < names.length; i++) {
804
- var entryPath = path.join(current, names[i]);
805
- var stat = fs.lstatSync(entryPath);
806
- if (stat.isDirectory()) stack.push(entryPath);
807
- else total += stat.size;
808
- }
809
- }
810
- return total;
811
- }
812
-
813
- // Entries that macOS archiving tools add but that say nothing about the
814
- // package layout are ignored when locating the package root.
815
- function isJunkEntry(name) {
816
- return name === '.DS_Store' || name === '__MACOSX' || name.indexOf('._') === 0;
817
- }
818
-
819
- function hasManifest(dir) {
820
- try {
821
- return fs.statSync(path.join(dir, 'Package.swift')).isFile();
822
- } catch (statError) {
823
- return false;
824
- }
825
- }
826
-
827
- // Package-root rule: Package.swift at the extraction root wins; otherwise
828
- // exactly one top-level directory with Package.swift at its top is the
829
- // root; anything else is a structured per-build error.
830
- function findPackageRoot(extractDir) {
831
- if (hasManifest(extractDir)) return extractDir;
832
- var entries = fs.readdirSync(extractDir).filter(function (name) {
833
- return !isJunkEntry(name);
834
- });
835
- if (entries.length === 0) {
836
- setBuildError('PACKAGE_ARCHIVE_INVALID', PACKAGE_ARCHIVE_NAME + ' is empty');
837
- return null;
838
- }
839
- if (entries.length > 1) {
840
- setBuildError(
841
- 'PACKAGE_ARCHIVE_INVALID',
842
- PACKAGE_ARCHIVE_NAME + ' must contain a single package root; found ' +
843
- entries.length + ' top-level entries (' + entries.sort().join(', ') +
844
- ') and no Package.swift at the archive root'
845
- );
846
- return null;
847
- }
848
- var rootPath = path.join(extractDir, entries[0]);
849
- if (!fs.statSync(rootPath).isDirectory()) {
850
- setBuildError(
851
- 'PACKAGE_ARCHIVE_INVALID',
852
- PACKAGE_ARCHIVE_NAME + ' must contain a package directory; its only ' +
853
- 'top-level entry (' + entries[0] + ') is not a directory'
854
- );
855
- return null;
856
- }
857
- if (!hasManifest(rootPath)) {
858
- setBuildError(
859
- 'PACKAGE_MANIFEST_MISSING',
860
- 'no Package.swift at the top of the package root (' + entries[0] + ') in ' +
861
- PACKAGE_ARCHIVE_NAME
862
- );
863
- return null;
864
- }
865
- return rootPath;
866
- }
867
-
868
- // When no target input is configured, the target is discovered from the
869
- // shipped package's manifest: the package must declare exactly one library
870
- // product. The harness depends on the package via .product(name:package:),
871
- // so library products are the unit of discovery.
872
- function resolveTargetFromManifest(packageRoot) {
873
- var dump = runSwift(['package', '--package-path', packageRoot, 'dump-package']);
874
- if (dump.status !== 0) {
875
- setBuildError(
876
- 'PACKAGE_TARGET_UNRESOLVED',
877
- 'could not read the shipped package manifest (swift package dump-package failed):\n' +
878
- outputTail(dump)
879
- );
880
- return null;
881
- }
882
- var manifest = null;
883
- try {
884
- manifest = JSON.parse(dump.stdout);
885
- } catch (parseError) {
886
- setBuildError(
887
- 'PACKAGE_TARGET_UNRESOLVED',
888
- 'swift package dump-package produced unparseable output: ' +
889
- (parseError && parseError.message ? parseError.message : String(parseError))
890
- );
891
- return null;
892
- }
893
- var libraries = ((manifest && manifest.products) || [])
894
- .filter(function (product) {
895
- return product && product.type && typeof product.type === 'object' &&
896
- 'library' in product.type;
897
- })
898
- .map(function (product) { return String(product.name); });
899
- if (libraries.length === 0) {
900
- setBuildError(
901
- 'PACKAGE_TARGET_UNRESOLVED',
902
- 'the shipped package declares no library products; declare exactly one ' +
903
- 'library product, or set the target input'
904
- );
905
- return null;
906
- }
907
- if (libraries.length > 1) {
908
- setBuildError(
909
- 'PACKAGE_TARGET_UNRESOLVED',
910
- 'the shipped package declares ' + libraries.length + ' library products (' +
911
- libraries.sort().join(', ') + '); set the target input to pick one'
912
- );
913
- return null;
914
- }
915
- if (!SWIFT_IDENTIFIER.test(libraries[0])) {
916
- setBuildError(
917
- 'PACKAGE_TARGET_UNRESOLVED',
918
- 'discovered library product ' + JSON.stringify(libraries[0]) +
919
- ' is not a valid Swift module identifier'
920
- );
921
- return null;
922
- }
923
- return libraries[0];
924
- }
925
-
926
- // Locates, extracts, and validates the shipped archive. Returns the package
927
- // root to build from, or null after recording a structured build error.
928
- function prepareShippedPackage() {
929
- var archivePath = path.join(inputsDir, PACKAGE_ARCHIVE_NAME);
930
- var archiveStat = null;
931
- try {
932
- archiveStat = fs.statSync(archivePath);
933
- } catch (statError) {
934
- archiveStat = null;
935
- }
936
- if (!archiveStat || !archiveStat.isFile()) {
937
- setBuildError(
938
- 'PACKAGE_ARCHIVE_MISSING',
939
- 'no ' + PACKAGE_ARCHIVE_NAME + ' in ' + inputsDir + '; ship the SwiftPM ' +
940
- 'package as a run input named ' + PACKAGE_ARCHIVE_NAME
941
- );
942
- return null;
943
- }
944
-
945
- // bsdtar and GNU tar both refuse absolute and dot-dot member paths by
946
- // default; the listing is checked explicitly anyway so the guard does not
947
- // depend on the host tar implementation. A failed listing also catches
948
- // unreadable or corrupt archives before anything touches the disk.
949
- var listing = runTar(['-tzf', archivePath]);
950
- if (listing.status !== 0) {
951
- setBuildError(
952
- 'PACKAGE_ARCHIVE_INVALID',
953
- PACKAGE_ARCHIVE_NAME + ' is not a readable gzipped tar archive:\n' +
954
- outputTail(listing)
955
- );
956
- return null;
957
- }
958
- var members = (listing.stdout || '').split('\n').filter(function (name) {
959
- return name !== '';
960
- });
961
- var unsafe = members.filter(isUnsafeArchiveMember);
962
- if (unsafe.length > 0) {
963
- setBuildError(
964
- 'PACKAGE_ARCHIVE_INVALID',
965
- PACKAGE_ARCHIVE_NAME + ' contains unsafe member paths (absolute or dot-dot): ' +
966
- unsafe.slice(0, 5).join(', ')
967
- );
968
- return null;
969
- }
970
-
971
- shippedExtractDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ui-fidelity-package-'));
972
- registerPathReplacement(shippedExtractDir, '<package>');
973
- log('extracting ' + PACKAGE_ARCHIVE_NAME + ' (' + archiveStat.size + ' bytes)');
974
- var extraction = runTar(['-xzf', archivePath, '-C', shippedExtractDir]);
975
- if (extraction.status !== 0) {
976
- setBuildError(
977
- 'PACKAGE_ARCHIVE_INVALID',
978
- PACKAGE_ARCHIVE_NAME + ' could not be extracted:\n' + outputTail(extraction)
979
- );
980
- return null;
981
- }
982
-
983
- var totalBytes = extractedSize(shippedExtractDir);
984
- if (totalBytes > maxExtractedBytes) {
985
- setBuildError(
986
- 'PACKAGE_ARCHIVE_TOO_LARGE',
987
- PACKAGE_ARCHIVE_NAME + ' extracts to ' + totalBytes + ' bytes, over the ' +
988
- maxExtractedBytes + '-byte limit; ship a leaner source slice'
989
- );
990
- return null;
991
- }
992
-
993
- var packageRoot = findPackageRoot(shippedExtractDir);
994
- if (packageRoot === null) return null;
995
- var rootLabel = path.relative(shippedExtractDir, packageRoot);
996
- log('package root: ' + (rootLabel === '' ? '.' : rootLabel));
997
-
998
- if (activeTarget === null) {
999
- var discovered = resolveTargetFromManifest(packageRoot);
1000
- if (discovered === null) return null;
1001
- log('discovered library target: ' + discovered);
1002
- activeTarget = discovered;
1003
- }
1004
- return packageRoot;
1005
- }
1006
-
1007
- function cleanupShippedPackage() {
1008
- if (shippedExtractDir === null) return;
1009
- if (process.env.UI_FIDELITY_KEEP_HARNESS) {
1010
- log('keeping extracted package (UI_FIDELITY_KEEP_HARNESS set)');
1011
- return;
1012
- }
1013
- try {
1014
- fs.rmSync(shippedExtractDir, { recursive: true, force: true });
1015
- } catch (cleanupError) {
1016
- // best effort
1017
- }
1018
- shippedExtractDir = null;
1019
- }
1020
- `;
1021
- /**
1022
- * Runtime support for asset catalogs (package_source variants only).
1023
- *
1024
- * `swift build` copies a target's .xcassets into its resource bundle but never
1025
- * runs actool, so SwiftUI's Image("name") finds no compiled catalog and the
1026
- * image does not resolve. After the harness builds, compile every .xcassets in
1027
- * the package with actool into the harness bin directory (= the render
1028
- * executable's Bundle.main), so unmodified Image("name") references resolve.
1029
- * One actool invocation over all catalogs (it merges them). Best-effort:
1030
- * a compile failure is logged and rendering continues -- the affected images
1031
- * simply will not appear, which the human comparing the screens will see.
1032
- */
1033
- const RENDER_SCRIPT_ASSET_CATALOG_STAGE = String.raw `
1034
- // ---- asset-catalog stage (package_source variants) ----
1035
-
1036
- var ASSET_CATALOG_MIN_MACOS = '13.0';
1037
-
1038
- // Recursively collect *.xcassets directories under root, skipping .build and
1039
- // the catalogs' own contents (a catalog is a leaf for this scan).
1040
- function findAssetCatalogs(root) {
1041
- var found = [];
1042
- var stack = [root];
1043
- while (stack.length > 0) {
1044
- var dir = stack.pop();
1045
- var entries;
1046
- try {
1047
- entries = fs.readdirSync(dir, { withFileTypes: true });
1048
- } catch (readError) {
1049
- continue;
1050
- }
1051
- for (var i = 0; i < entries.length; i++) {
1052
- var entry = entries[i];
1053
- if (!entry.isDirectory()) continue;
1054
- if (entry.name === '.build') continue;
1055
- var full = path.join(dir, entry.name);
1056
- if (entry.name.slice(-9) === '.xcassets') {
1057
- found.push(full);
1058
- } else {
1059
- stack.push(full);
1060
- }
1061
- }
1062
- }
1063
- return found.sort();
1064
- }
1065
-
1066
- // Run actool over the given catalogs, emitting Assets.car into outDir.
1067
- // Best-effort: a failure is logged (warn-and-continue) and rendering proceeds.
1068
- function compileCatalogsInto(catalogs, outDir, label) {
1069
- try {
1070
- fs.mkdirSync(outDir, { recursive: true });
1071
- } catch (mkdirError) {
1072
- // best effort -- actool reports a missing output dir itself
1073
- }
1074
- var args = ['actool'].concat(catalogs);
1075
- args.push(
1076
- '--compile', outDir,
1077
- '--platform', 'macosx',
1078
- '--minimum-deployment-target', ASSET_CATALOG_MIN_MACOS,
1079
- '--output-format', 'human-readable-text'
1080
- );
1081
- log('xcrun ' + args.join(' '));
1082
- var result = cp.spawnSync('xcrun', args, {
1083
- encoding: 'utf-8',
1084
- maxBuffer: 16 * 1024 * 1024,
1085
- });
1086
- if (result.stdout) process.stdout.write(result.stdout);
1087
- if (result.stderr) process.stderr.write(result.stderr);
1088
- if (result.error || result.status !== 0) {
1089
- var detail = (result.stderr || '') +
1090
- (result.error ? String(result.error.message || result.error) : '');
1091
- logError(
1092
- 'asset-catalog compile failed for ' + label + ' (continuing; catalog ' +
1093
- 'images may not resolve): ' + sanitizeMessage(detail.trim())
1094
- );
1095
- }
1096
- }
1097
-
1098
- // SwiftUI resolves Image("name") against Bundle.main and
1099
- // Image("name", bundle: .module) against a target's own resource bundle.
1100
- // swift build copies a .process'd .xcassets into the module bundle but never
1101
- // compiles it, and a loose catalog is copied nowhere. So compile twice:
1102
- // the package's source catalogs into the bin dir (= Bundle.main), AND each
1103
- // module bundle's copied catalog in place (= Bundle.module). This covers both
1104
- // reference idioms, so the agent need not know which bundle the renderer uses.
1105
- function compileAssetCatalogs(packageDir, harnessDir) {
1106
- var sourceCatalogs = findAssetCatalogs(packageDir);
1107
- if (sourceCatalogs.length === 0) return;
1108
-
1109
- var binResult = runSwift(['build', '--package-path', harnessDir, '--show-bin-path']);
1110
- if (binResult.status !== 0) {
1111
- logError('asset-catalog compile skipped: could not resolve the build bin path');
1112
- return;
1113
- }
1114
- var binPath = String(binResult.stdout || '').trim().split('\n').pop().trim();
1115
- if (!binPath) {
1116
- logError('asset-catalog compile skipped: empty build bin path');
1117
- return;
1118
- }
1119
-
1120
- // Bundle.main: the package's source catalogs, merged into the bin dir.
1121
- compileCatalogsInto(sourceCatalogs, binPath, 'Bundle.main');
1122
-
1123
- // Bundle.module: each *.bundle in the bin dir that carries a copied catalog,
1124
- // compiled in place so Image(..., bundle: .module) resolves.
1125
- var entries;
1126
- try {
1127
- entries = fs.readdirSync(binPath, { withFileTypes: true });
1128
- } catch (readError) {
1129
- entries = [];
1130
- }
1131
- for (var i = 0; i < entries.length; i++) {
1132
- var entry = entries[i];
1133
- if (!entry.isDirectory() || entry.name.slice(-7) !== '.bundle') continue;
1134
- var bundleDir = path.join(binPath, entry.name);
1135
- var bundleCatalogs = findAssetCatalogs(bundleDir);
1136
- if (bundleCatalogs.length > 0) {
1137
- compileCatalogsInto(bundleCatalogs, bundleDir, entry.name);
1138
- }
1139
- }
1140
- }
1141
- `;
1142
- /**
1143
- * Replaces exactly one occurrence of `anchor` in `source`. Throws when the
1144
- * anchor is missing or ambiguous, so any drift between the v1 runtime text
1145
- * and the package-source variant fails loudly at module load.
1146
- */
1147
- function replaceOnce(source, anchor, replacement) {
1148
- const first = source.indexOf(anchor);
1149
- if (first === -1 || source.indexOf(anchor, first + anchor.length) !== -1) {
1150
- throw new Error('ui-fidelity-render: expected exactly one occurrence of anchor: ' + anchor);
1151
- }
1152
- return source.slice(0, first) + replacement + source.slice(first + anchor.length);
1153
- }
1154
- /**
1155
- * Builds the runtime main for explicitly-configured package_source ("repo"
1156
- * or "inputs") by patching the v1 runtime text. RENDER_SCRIPT_MAIN itself is
1157
- * never modified: omitting package_source keeps the generated script
1158
- * byte-identical to v1 (locked by a snapshot test), and the patch points
1159
- * below are the complete, reviewable diff between the two modes.
1160
- */
1161
- function buildPackageSourceMain() {
1162
- let main = RENDER_SCRIPT_MAIN;
1163
- // The result document gains the configured package source and a top-level
1164
- // per-build error slot, distinct from per-screen errors.
1165
- main = replaceOnce(main, 'var doc = { renderer: RENDERER, screens: currentEntries };', 'var doc = {\n' +
1166
- ' renderer: RENDERER,\n' +
1167
- ' package_source: CONFIG.packageSource,\n' +
1168
- ' error: buildError,\n' +
1169
- ' screens: currentEntries,\n' +
1170
- ' };');
1171
- // packagePath is null with package_source "inputs": the package to build
1172
- // from is only known after extraction.
1173
- main = replaceOnce(main, "var resolvedPackagePath = path.resolve(CONFIG.packagePath);\n" +
1174
- 'registerPathReplacement(\n' +
1175
- ' resolvedPackagePath,\n' +
1176
- " path.isAbsolute(CONFIG.packagePath) ? '<package_path>' : CONFIG.packagePath\n" +
1177
- ');', 'var resolvedPackagePath =\n' +
1178
- ' CONFIG.packagePath === null ? null : path.resolve(CONFIG.packagePath);\n' +
1179
- 'if (resolvedPackagePath !== null) {\n' +
1180
- ' registerPathReplacement(\n' +
1181
- ' resolvedPackagePath,\n' +
1182
- " path.isAbsolute(CONFIG.packagePath) ? '<package_path>' : CONFIG.packagePath\n" +
1183
- ' );\n' +
1184
- '}');
1185
- // The harness target may be discovered at runtime from the shipped
1186
- // manifest, so renderWithHarness reads the resolved activeTarget.
1187
- main = replaceOnce(main, 'target: CONFIG.target,', 'target: activeTarget,');
1188
- main = replaceOnce(main, "' a public View with a parameterless init in ' + CONFIG.target + '?):\\n' +", "' a public View with a parameterless init in ' + activeTarget + '?):\\n' +");
1189
- // The shipped-package stage runs before any harness build. It is reached
1190
- // even when no screen is renderable, so packaging mistakes are always
1191
- // reported; its scratch directory is cleaned up afterwards.
1192
- main = replaceOnce(main, ' // 3. Render the remaining screens through the synthesized harness.\n' +
1193
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1194
- ' if (renderable.length > 0) {\n' +
1195
- ' if (!fs.existsSync(resolvedPackagePath)) {\n' +
1196
- ' currentEntries.forEach(function (entry) {\n' +
1197
- " setError(entry, 'RENDER_UNSUPPORTED', 'package_path does not exist: ' + resolvedPackagePath);\n" +
1198
- ' });\n' +
1199
- ' } else {\n' +
1200
- ' renderWithHarness(renderable, resolvedPackagePath);\n' +
1201
- ' }\n' +
1202
- ' } else if (currentEntries.length > 0) {\n' +
1203
- " log('no renderable screens, skipping harness build');\n" +
1204
- ' }', ' // 3. Resolve the package to build from, then render the remaining\n' +
1205
- ' // screens through the synthesized harness.\n' +
1206
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1207
- " if (CONFIG.packageSource === 'inputs') {\n" +
1208
- ' try {\n' +
1209
- ' var shippedRoot = prepareShippedPackage();\n' +
1210
- ' if (shippedRoot !== null) {\n' +
1211
- ' if (renderable.length > 0) {\n' +
1212
- ' renderWithHarness(renderable, shippedRoot);\n' +
1213
- ' } else if (currentEntries.length > 0) {\n' +
1214
- " log('no renderable screens, skipping harness build');\n" +
1215
- ' }\n' +
1216
- ' }\n' +
1217
- ' } finally {\n' +
1218
- ' cleanupShippedPackage();\n' +
1219
- ' }\n' +
1220
- ' } else if (renderable.length > 0) {\n' +
1221
- ' if (!fs.existsSync(resolvedPackagePath)) {\n' +
1222
- ' currentEntries.forEach(function (entry) {\n' +
1223
- " setError(entry, 'RENDER_UNSUPPORTED', 'package_path does not exist: ' + resolvedPackagePath);\n" +
1224
- ' });\n' +
1225
- ' } else {\n' +
1226
- ' renderWithHarness(renderable, resolvedPackagePath);\n' +
1227
- ' }\n' +
1228
- ' } else if (currentEntries.length > 0) {\n' +
1229
- " log('no renderable screens, skipping harness build');\n" +
1230
- ' }');
1231
- // A per-build package error fails the step even when no screen reached
1232
- // the harness (including the zero-screens case).
1233
- main = replaceOnce(main, ' // 4. Exit code: zero only when every screen rendered.\n' +
1234
- ' writeResult();\n', ' // 4. Exit code: zero only when the shipped package (if any) was valid\n' +
1235
- ' // and every screen rendered.\n' +
1236
- ' writeResult();\n' +
1237
- ' if (buildError !== null) {\n' +
1238
- " logError('package validation failed: ' + buildError.code);\n" +
1239
- ' return 1;\n' +
1240
- ' }\n');
1241
- // The shipped-package stage is defined ahead of main().
1242
- main = replaceOnce(main, 'function main() {', RENDER_SCRIPT_PACKAGE_STAGE + '\nfunction main() {');
1243
- // Asset-catalog stage: define the helpers ahead of renderWithHarness, then
1244
- // compile any catalogs once the package has proven it builds (after the
1245
- // probe) and before the per-screen renders that depend on the images.
1246
- main = replaceOnce(main, 'function renderWithHarness(renderable, packagePath) {', RENDER_SCRIPT_ASSET_CATALOG_STAGE + '\nfunction renderWithHarness(renderable, packagePath) {');
1247
- main = replaceOnce(main, ' renderable.forEach(function (entry) {', ' compileAssetCatalogs(packagePath, harnessDir);\n\n' +
1248
- ' renderable.forEach(function (entry) {');
1249
- // Render-trimming stage: define the helpers ahead of renderWithHarness, then
1250
- // crop each rendered PNG to its content bounding box right after it renders.
1251
- main = replaceOnce(main, 'function renderWithHarness(renderable, packagePath) {', RENDER_SCRIPT_TRIM_STAGE + '\nfunction renderWithHarness(renderable, packagePath) {');
1252
- main = replaceOnce(main, " log('rendered ' + entry.screen + ' -> ' + relative);", " log('rendered ' + entry.screen + ' -> ' + relative);\n" +
1253
- ' trimRenderedImage(entry, outputPath);');
1254
- // Per-screen result entries gain the trimmed-render fields: the aligned
1255
- // (reference-sized) image and the rendered/logical/reference dimensions.
1256
- // They stay null until a screen renders and is trimmed.
1257
- main = replaceOnce(main, ' reference_image_path: null,\n' +
1258
- ' rendered_image_path: null,\n' +
1259
- ' };', ' reference_image_path: null,\n' +
1260
- ' rendered_image_path: null,\n' +
1261
- ' aligned_image_path: null,\n' +
1262
- ' dimensions: null,\n' +
1263
- ' };');
1264
- return main;
1265
- }
1266
- const RENDER_SCRIPT_MAIN_WITH_PACKAGE_SOURCE = buildPackageSourceMain();
1267
- /**
1268
- * Builds the runtime main for package_source "url" (fidelity-linux 03) by
1269
- * patching the package_source variant. Three departures, all forced by the
1270
- * same fact — a clone-free worker has no checkout:
1271
- *
1272
- * 1. The package is DOWNLOADED (curl, a runner requirement in this mode) to
1273
- * `.ci/inputs/package.tar.gz` and extracted BEFORE the per-screen
1274
- * reference stage, because committed references travel inside it. A failed
1275
- * download is one structured per-build error, never per-screen noise.
1276
- * 2. Each screen's reference is the committed repo-relative path resolved
1277
- * INSIDE the extracted package: it must sit under packagePath, and the
1278
- * remainder resolves against the extracted package root.
1279
- * 3. The harness stage reuses the already-extracted root (no second extract);
1280
- * cleanup mirrors the inputs path. Mirrors the Android step's url mode so
1281
- * both platform kinds accept the same dispatched inputs.
1282
- */
1283
- function buildPackageUrlMain() {
1284
- let main = RENDER_SCRIPT_MAIN_WITH_PACKAGE_SOURCE;
1285
- // Download + extract FIRST — the reference stage below reads from inside
1286
- // the extracted package.
1287
- main = replaceOnce(main, ' // 2. Per-screen validation + reference copies. Each screen gets its OWN\n' +
1288
- ' // copy named by screen, even when two screens share a reference\n' +
1289
- ' // basename.\n' +
1290
- ' currentEntries.forEach(function (entry) {', ' // 2a. Download + extract the shipped package FIRST (URL mode): committed\n' +
1291
- ' // references live inside it, so the per-screen reference copies below\n' +
1292
- ' // need the extracted package root. A failed download or an invalid\n' +
1293
- ' // archive is ONE structured per-build error; per-screen reference\n' +
1294
- ' // work then stops cleanly (no bogus REFERENCE_MISSING noise).\n' +
1295
- ' var urlPackageRoot = null;\n' +
1296
- ' (function () {\n' +
1297
- ' try {\n' +
1298
- ' fs.mkdirSync(inputsDir, { recursive: true });\n' +
1299
- ' } catch (mkdirError) {\n' +
1300
- ' // the download below reports the real failure\n' +
1301
- ' }\n' +
1302
- ' var dest = path.join(inputsDir, PACKAGE_ARCHIVE_NAME);\n' +
1303
- " log('downloading render package');\n" +
1304
- ' var download = cp.spawnSync(\n' +
1305
- " 'curl',\n" +
1306
- " ['-fsSL', '--retry', '2', '--max-time', '300', '-o', dest, CONFIG.packageUrl],\n" +
1307
- " { encoding: 'utf-8', maxBuffer: 16 * 1024 * 1024 }\n" +
1308
- ' );\n' +
1309
- ' if (download.error) {\n' +
1310
- ' download.status = download.status == null ? 127 : download.status;\n' +
1311
- " download.stderr = (download.stderr || '') + String(download.error.message || download.error);\n" +
1312
- ' }\n' +
1313
- ' if (download.status !== 0) {\n' +
1314
- ' setBuildError(\n' +
1315
- " 'PACKAGE_DOWNLOAD_FAILED',\n" +
1316
- " 'render package download failed (curl exit ' + download.status + '):\\n' + outputTail(download)\n" +
1317
- ' );\n' +
1318
- ' return;\n' +
1319
- ' }\n' +
1320
- ' urlPackageRoot = prepareShippedPackage();\n' +
1321
- ' })();\n' +
1322
- '\n' +
1323
- ' // 2. Per-screen validation + reference copies. Each screen gets its OWN\n' +
1324
- ' // copy named by screen, even when two screens share a reference\n' +
1325
- ' // basename.\n' +
1326
- ' currentEntries.forEach(function (entry) {');
1327
- // References resolve INSIDE the extracted package: repo-relative path minus
1328
- // the packagePath prefix, against the extracted package root.
1329
- main = replaceOnce(main, 'var basename = params.screens[entry.screen];\n' +
1330
- ' if (\n' +
1331
- " typeof basename !== 'string' || basename === '' ||\n" +
1332
- " basename === '.' || basename === '..' ||\n" +
1333
- ' basename !== path.basename(basename)\n' +
1334
- ' ) {\n' +
1335
- ' setError(\n' +
1336
- ' entry,\n' +
1337
- " 'REFERENCE_MISSING',\n" +
1338
- " 'reference for ' + entry.screen + ' must be a plain file basename inside ' +\n" +
1339
- " inputsDir + ', got: ' + JSON.stringify(basename)\n" +
1340
- ' );\n' +
1341
- ' return;\n' +
1342
- ' }\n' +
1343
- ' var source = path.join(inputsDir, basename);', 'var refPath = params.screens[entry.screen];\n' +
1344
- " if (typeof refPath !== 'string' || refPath === '') {\n" +
1345
- ' setError(\n' +
1346
- ' entry,\n' +
1347
- " 'REFERENCE_MISSING',\n" +
1348
- " 'reference for ' + entry.screen + ' must be a non-empty repo-relative path, got: ' +\n" +
1349
- ' JSON.stringify(refPath)\n' +
1350
- ' );\n' +
1351
- ' return;\n' +
1352
- ' }\n' +
1353
- ' if (urlPackageRoot === null) {\n' +
1354
- ' // Package unavailable: the per-build error above carries the cause.\n' +
1355
- ' return;\n' +
1356
- ' }\n' +
1357
- " var packagePrefix = CONFIG.packagePath.replace(/^\\.\\//, '').replace(/\\/+$/, '');\n" +
1358
- " var normalizedRef = refPath.replace(/^\\.\\//, '');\n" +
1359
- " if (normalizedRef.indexOf(packagePrefix + '/') !== 0) {\n" +
1360
- ' setError(\n' +
1361
- ' entry,\n' +
1362
- " 'REFERENCE_MISSING',\n" +
1363
- " 'reference for ' + entry.screen + ' must live inside the shipped package (' +\n" +
1364
- " packagePrefix + '), got: ' + JSON.stringify(refPath)\n" +
1365
- ' );\n' +
1366
- ' return;\n' +
1367
- ' }\n' +
1368
- ' var source = path.join(urlPackageRoot, normalizedRef.slice(packagePrefix.length + 1));');
1369
- // Harness stage: build from the package extracted in stage 2a — no second
1370
- // extract. This variant only ever runs with packageSource "url".
1371
- main = replaceOnce(main, ' // 3. Resolve the package to build from, then render the remaining\n' +
1372
- ' // screens through the synthesized harness.\n' +
1373
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1374
- " if (CONFIG.packageSource === 'inputs') {\n" +
1375
- ' try {\n' +
1376
- ' var shippedRoot = prepareShippedPackage();\n' +
1377
- ' if (shippedRoot !== null) {\n' +
1378
- ' if (renderable.length > 0) {\n' +
1379
- ' renderWithHarness(renderable, shippedRoot);\n' +
1380
- ' } else if (currentEntries.length > 0) {\n' +
1381
- " log('no renderable screens, skipping harness build');\n" +
1382
- ' }\n' +
1383
- ' }\n' +
1384
- ' } finally {\n' +
1385
- ' cleanupShippedPackage();\n' +
1386
- ' }\n' +
1387
- ' } else if (renderable.length > 0) {\n' +
1388
- ' if (!fs.existsSync(resolvedPackagePath)) {\n' +
1389
- ' currentEntries.forEach(function (entry) {\n' +
1390
- " setError(entry, 'RENDER_UNSUPPORTED', 'package_path does not exist: ' + resolvedPackagePath);\n" +
1391
- ' });\n' +
1392
- ' } else {\n' +
1393
- ' renderWithHarness(renderable, resolvedPackagePath);\n' +
1394
- ' }\n' +
1395
- ' } else if (currentEntries.length > 0) {\n' +
1396
- " log('no renderable screens, skipping harness build');\n" +
1397
- ' }', ' // 3. Render from the package downloaded + extracted in stage 2a. The\n' +
1398
- ' // extraction already reported packaging mistakes as the per-build\n' +
1399
- ' // error, so a null root here just skips the harness build.\n' +
1400
- ' var renderable = currentEntries.filter(function (entry) { return entry.error === null; });\n' +
1401
- ' try {\n' +
1402
- ' if (urlPackageRoot !== null) {\n' +
1403
- ' if (renderable.length > 0) {\n' +
1404
- ' renderWithHarness(renderable, urlPackageRoot);\n' +
1405
- ' } else if (currentEntries.length > 0) {\n' +
1406
- " log('no renderable screens, skipping harness build');\n" +
1407
- ' }\n' +
1408
- ' }\n' +
1409
- ' } finally {\n' +
1410
- ' cleanupShippedPackage();\n' +
1411
- ' }');
1412
- return main;
1413
- }
1414
- const RENDER_SCRIPT_MAIN_URL = buildPackageUrlMain();
1415
- /**
1416
- * Patches the reference-sourcing block to read each screen's reference from a
1417
- * repo-relative path (resolved against the checkout) instead of a plain
1418
- * basename under `.ci/inputs/`. Applied on top of EITHER base main (v1 or the
1419
- * package_source variant) — the package patches never touch this block, so the
1420
- * anchor is present and unique in both. The downstream stat/copy logic is
1421
- * unchanged; only how `source` is computed differs.
1422
- */
1423
- function buildReferenceRepoMain(base) {
1424
- return replaceOnce(base, 'var basename = params.screens[entry.screen];\n' +
1425
- ' if (\n' +
1426
- " typeof basename !== 'string' || basename === '' ||\n" +
1427
- " basename === '.' || basename === '..' ||\n" +
1428
- ' basename !== path.basename(basename)\n' +
1429
- ' ) {\n' +
1430
- ' setError(\n' +
1431
- ' entry,\n' +
1432
- " 'REFERENCE_MISSING',\n" +
1433
- " 'reference for ' + entry.screen + ' must be a plain file basename inside ' +\n" +
1434
- " inputsDir + ', got: ' + JSON.stringify(basename)\n" +
1435
- ' );\n' +
1436
- ' return;\n' +
1437
- ' }\n' +
1438
- ' var source = path.join(inputsDir, basename);', 'var refPath = params.screens[entry.screen];\n' +
1439
- " if (typeof refPath !== 'string' || refPath === '') {\n" +
1440
- ' setError(\n' +
1441
- ' entry,\n' +
1442
- " 'REFERENCE_MISSING',\n" +
1443
- " 'reference for ' + entry.screen + ' must be a non-empty repo-relative path, got: ' +\n" +
1444
- ' JSON.stringify(refPath)\n' +
1445
- ' );\n' +
1446
- ' return;\n' +
1447
- ' }\n' +
1448
- ' var source = path.resolve(refPath);');
1449
- }
1450
- /**
1451
- * Evaluates the embedded generator source and returns the real functions, so
1452
- * unit tests exercise exactly the code that ships inside the runtime script.
1453
- */
1454
- export function getRenderScriptInternals() {
1455
- const factory = new Function(RENDER_SCRIPT_GENERATORS +
1456
- '\nreturn { swiftStringLiteral: swiftStringLiteral,' +
1457
- ' generateHarnessPackageSwift: generateHarnessPackageSwift,' +
1458
- ' generateProbeSwift: generateProbeSwift,' +
1459
- ' generateScreenSwift: generateScreenSwift };');
1460
- return factory();
1461
- }
1462
- /**
1463
- * Generates the self-contained runtime node script for the step.
1464
- * Pure function of its config — exported so tests can execute the script.
1465
- *
1466
- * When config.packageSource is absent the output is byte-identical to the
1467
- * pre-package_source script (JSON.stringify drops undefined keys, and the
1468
- * unpatched v1 runtime is used).
1469
- */
1470
- export function generateRenderScript(config) {
1471
- let main = config.packageSource === undefined
1472
- ? RENDER_SCRIPT_MAIN
1473
- : config.packageSource === 'url'
1474
- ? RENDER_SCRIPT_MAIN_URL
1475
- : RENDER_SCRIPT_MAIN_WITH_PACKAGE_SOURCE;
1476
- // Reference-from-repo is an independent dimension layered on top of whichever
1477
- // package base applies. Absent → byte-identical reference block (v1).
1478
- if (config.referenceSource === 'repo') {
1479
- main = buildReferenceRepoMain(main);
1480
- }
1481
- return [
1482
- "'use strict';",
1483
- '// ui-fidelity-render runtime script (generated by cibuild at YAML conversion time)',
1484
- 'var CONFIG = ' + JSON.stringify(config) + ';',
1485
- RENDER_SCRIPT_GENERATORS,
1486
- RENDER_SCRIPT_SOURCE_DIGEST_STAGE,
1487
- main,
1488
- ].join('\n');
1489
- }
1490
- /**
1491
- * ui-fidelity-render step executor.
1492
- * Returns a node-kind StepDef whose script performs the render at runtime on
1493
- * the macOS runner.
1494
- */
1495
- export class UiFidelityRenderStepExecutor extends BaseStepExecutor {
1496
- getValidationRequirements(inputs, _env, _config) {
1497
- if (inputs && (inputs.package_source === 'inputs' || inputs.package_source === 'url')) {
1498
- // The package ships as .ci/inputs/package.tar.gz at run time (uploaded
1499
- // run input, or downloaded from the signed package_url in url mode), so
1500
- // neither package_path nor target is required up front.
1501
- const requirements = [
1502
- this.requireCommand('swift', 'Swift toolchain used to build and run the render harness', 'Install Xcode (or the Swift toolchain) on the runner'),
1503
- this.requireCommand('tar', 'Archive tool used to extract the shipped package', 'Install tar on the runner'),
1504
- ];
1505
- if (inputs.package_source === 'url') {
1506
- requirements.push(this.requireCommand('curl', 'Downloader used to fetch the render package by signed URL', 'Install curl on the runner'));
1507
- }
1508
- return requirements;
1509
- }
1510
- return [
1511
- this.requireInput('package_path', inputs, 'Path to the SwiftPM package containing the screens'),
1512
- this.requireInput('target', inputs, 'SPM library product to import in the render harness'),
1513
- this.requireCommand('swift', 'Swift toolchain used to build and run the render harness', 'Install Xcode (or the Swift toolchain) on the runner'),
1514
- ];
1515
- }
1516
- async execute(inputs, _env, _config) {
1517
- const stepName = 'ui-fidelity-render';
1518
- // Omitted package_source means v1 repo behavior with a byte-identical
1519
- // generated script; an explicit value (repo or inputs) is recorded in
1520
- // the script config and the result document.
1521
- const rawPackageSource = this.getInput(inputs, 'package_source', undefined);
1522
- let packageSource;
1523
- if (rawPackageSource !== undefined) {
1524
- const value = String(rawPackageSource);
1525
- if (!PACKAGE_SOURCES.includes(value)) {
1526
- throw new Error(`Invalid package_source '${value}' for step '${stepName}': ` +
1527
- `expected one of: ${PACKAGE_SOURCES.join(', ')}`);
1528
- }
1529
- packageSource = value;
1530
- }
1531
- let packagePath = null;
1532
- let target = null;
1533
- let packageUrl;
1534
- if (packageSource === 'inputs' || packageSource === 'url') {
1535
- // package_path names no local directory here: inputs mode ignores it
1536
- // (the package arrives as a run input); url mode requires it as the
1537
- // repo-relative prefix committed reference paths sit under, inside the
1538
- // downloaded package. target is optional and discovered from the
1539
- // shipped manifest when the package declares exactly one library
1540
- // product.
1541
- if (packageSource === 'url') {
1542
- packageUrl = String(this.getRequiredInput(inputs, 'package_url', stepName));
1543
- if (!/^https?:\/\//.test(packageUrl)) {
1544
- throw new Error(`Invalid package_url for step '${stepName}': expected an http(s) URL`);
1545
- }
1546
- packagePath = String(this.getRequiredInput(inputs, 'package_path', stepName));
1547
- }
1548
- const configuredTarget = this.getInput(inputs, 'target', undefined);
1549
- target = configuredTarget === undefined ? null : String(configuredTarget);
1550
- }
1551
- else {
1552
- packagePath = String(this.getRequiredInput(inputs, 'package_path', stepName));
1553
- target = String(this.getRequiredInput(inputs, 'target', stepName));
1554
- }
1555
- if (target !== null && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(target)) {
1556
- throw new Error(`Invalid target '${target}' for step '${stepName}': ` +
1557
- 'must be a Swift module identifier (letters, digits, underscores)');
1558
- }
1559
- // reference_source mirrors package_source: validate when provided, but only
1560
- // record (and patch the script for) the non-default "repo" so omitting it —
1561
- // or setting the default "inputs" — keeps the v1 reference block intact.
1562
- const rawReferenceSource = this.getInput(inputs, 'reference_source', undefined);
1563
- let referenceSource;
1564
- if (rawReferenceSource !== undefined) {
1565
- const value = String(rawReferenceSource);
1566
- if (!REFERENCE_SOURCES.includes(value)) {
1567
- throw new Error(`Invalid reference_source '${value}' for step '${stepName}': ` +
1568
- `expected one of: ${REFERENCE_SOURCES.join(', ')}`);
1569
- }
1570
- // URL mode owns reference sourcing (inside the extracted package) —
1571
- // an explicit reference_source alongside it is a contradiction.
1572
- if (packageSource === 'url') {
1573
- throw new Error(`Invalid reference_source for step '${stepName}': ` +
1574
- "package_source 'url' resolves references inside the downloaded " +
1575
- 'package; omit reference_source');
1576
- }
1577
- if (value === 'repo') {
1578
- referenceSource = 'repo';
1579
- }
1580
- }
1581
- const { width, height } = parseRenderSize(String(this.getInput(inputs, 'render_size', DEFAULT_RENDER_SIZE)));
1582
- const scale = parseScale(this.getInput(inputs, 'scale', DEFAULT_SCALE));
1583
- const config = { packagePath, target, width, height, scale };
1584
- if (packageSource !== undefined) {
1585
- config.packageSource = packageSource;
1586
- }
1587
- if (packageUrl !== undefined) {
1588
- config.packageUrl = packageUrl;
1589
- }
1590
- if (referenceSource !== undefined) {
1591
- config.referenceSource = referenceSource;
1592
- }
1593
- const script = generateRenderScript(config);
1594
- return this.createNodeStep(script, stepName);
1595
- }
1596
- }
1597
- //# sourceMappingURL=ui-fidelity-render.js.map