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