@invarn/cibuild 2.9.5 → 2.9.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/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 -1092
- 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 -61
- package/dist/src/yaml/steps/gradle-memory.d.ts.map +0 -1
- package/dist/src/yaml/steps/gradle-memory.js +0 -386
- 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/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,1350 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Xcode step implementations (xcodebuild, xcode-test)
|
|
3
|
-
*/
|
|
4
|
-
import { existsSync } from 'node:fs';
|
|
5
|
-
import { BaseStepExecutor } from './base.js';
|
|
6
|
-
import { resolveDestinationInput } from './xcode-destination.js';
|
|
7
|
-
import { APP_PRODUCT_RESOLVER_SOURCE } from './xcode-app-product.js';
|
|
8
|
-
import { pinsSimulatorArm64, simulatorArchsLines, withRuntimeSimulatorArchs } from './xcode-simulator-archs.js';
|
|
9
|
-
import { refusedFetchHelpers, retryOnRefusedFetch } from './git-fetch-retry.js';
|
|
10
|
-
/**
|
|
11
|
-
* Where derived data goes: outside the source tree, keyed by the output dir.
|
|
12
|
-
*
|
|
13
|
-
* It used to be "$DERIVED_DATA_PATH", and OUTPUT_DIR is inside the
|
|
14
|
-
* checkout — so every resolved SPM package sat in the working tree, where an
|
|
15
|
-
* app's own lint or format build phase walks it. vinhnx/Clendar's SwiftLint
|
|
16
|
-
* phase failed the build on 145 serious violations, all of them in
|
|
17
|
-
* third-party packages under build/app/DerivedData/SourcePackages/checkouts.
|
|
18
|
-
* A developer's Xcode keeps derived data under ~/Library, so no repository has
|
|
19
|
-
* a reason to exclude that directory, and it should not have to.
|
|
20
|
-
*
|
|
21
|
-
* CIBUILD_SCRATCH is the runner's per-build local root, which holds the
|
|
22
|
-
* checkout rather than sitting inside it; outside a sandbox it falls back to
|
|
23
|
-
* TMPDIR. The key is a checksum of the absolute output dir, so two steps that
|
|
24
|
-
* name the same output_dir still share derived data (the iOS template relies
|
|
25
|
-
* on that to avoid compiling the app twice) and two that name different ones
|
|
26
|
-
* stay apart, as they did before.
|
|
27
|
-
*
|
|
28
|
-
* **Never under /tmp** (nightly-baseline 58). analogcode/Swift-Radio-Pro's
|
|
29
|
-
* LNPopupController builds its header search paths by deleting
|
|
30
|
-
* `Context.packageDirectory` from the paths FileManager lists, and under /tmp
|
|
31
|
-
* the two disagree by `/private`: xcodebuild folds `/private/tmp` back to
|
|
32
|
-
* `/tmp` however the path is given, and FileManager does not, so every header
|
|
33
|
-
* path came out eight characters short ("troller/…"). Resolving the path did
|
|
34
|
-
* not help, for that reason. The guest's scratch root is
|
|
35
|
-
* `/tmp/cibuild-work-<id>`, so there derived data goes to the build user's
|
|
36
|
-
* real home instead. Measured on runner-mac-01 2026-10-03: the same build
|
|
37
|
-
* fails under /tmp and succeeds under the home, in a fresh VM each. Not
|
|
38
|
-
* /var/folders (TMPDIR): it built correctly there too. SwiftPM caches a
|
|
39
|
-
* manifest's evaluation, relative paths and all, so the first evaluation in a
|
|
40
|
-
* VM decides — another reason the root must be right from the first build.
|
|
41
|
-
*/
|
|
42
|
-
function derivedDataPathLines() {
|
|
43
|
-
return [
|
|
44
|
-
'# Derived data outside the source tree, keyed by the output dir, and never under /tmp',
|
|
45
|
-
'cibuild_dd_root="${CIBUILD_SCRATCH:-${TMPDIR:-/tmp}}"',
|
|
46
|
-
'case "$(cd "$cibuild_dd_root" 2>/dev/null && pwd -P)" in /private/tmp|/private/tmp/*) cibuild_dd_root="${CIBUILD_USER_HOME:-$HOME}/Library/Developer/cibuild" ;; esac',
|
|
47
|
-
'DERIVED_DATA_PATH="$cibuild_dd_root/cibuild-derived-data/$(printf \'%s\' "$OUTPUT_DIR" | cksum | cut -d\' \' -f1)"',
|
|
48
|
-
'mkdir -p "$DERIVED_DATA_PATH"',
|
|
49
|
-
'# Its real path: /tmp and TMPDIR are symlinks on macOS, and a package that builds paths by text arithmetic breaks on them',
|
|
50
|
-
'DERIVED_DATA_PATH="$(cd "$DERIVED_DATA_PATH" && pwd -P)"',
|
|
51
|
-
'echo "Derived data: $DERIVED_DATA_PATH"',
|
|
52
|
-
];
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Xcodebuild step executor
|
|
56
|
-
* Builds iOS/macOS projects using xcodebuild
|
|
57
|
-
*/
|
|
58
|
-
export class XcodeBuildStepExecutor extends BaseStepExecutor {
|
|
59
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
60
|
-
const requirements = [];
|
|
61
|
-
// xcodebuild command must exist
|
|
62
|
-
requirements.push(this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required for iOS builds', 'Install Xcode from the App Store, then run: xcode-select --install'));
|
|
63
|
-
// Required inputs
|
|
64
|
-
requirements.push(this.requireInput('project_path', inputs, 'Path to .xcodeproj or .xcworkspace', 'Provide project_path input in YAML'));
|
|
65
|
-
requirements.push(this.requireInput('scheme', inputs, 'Xcode scheme name', 'Provide scheme input in YAML'));
|
|
66
|
-
// Project file - runtime check only if it doesn't already exist locally
|
|
67
|
-
if (inputs.project_path && !existsSync(inputs.project_path)) {
|
|
68
|
-
requirements.push(this.runtimeRequirement(inputs.project_path, 'Xcode project/workspace file', 'file', 'the checkout'));
|
|
69
|
-
}
|
|
70
|
-
if (inputs.destination === 'auto') {
|
|
71
|
-
requirements.push(this.requireCommand('xcrun', 'xcrun is required to resolve destination: auto via xcrun simctl', 'Install Xcode and run: xcode-select --install'));
|
|
72
|
-
}
|
|
73
|
-
return requirements;
|
|
74
|
-
}
|
|
75
|
-
async execute(inputs, env, config) {
|
|
76
|
-
const stepName = 'xcodebuild';
|
|
77
|
-
// Get project path - required
|
|
78
|
-
let projectPath = this.getRequiredInput(inputs, 'project_path', stepName);
|
|
79
|
-
// Resolve path identifiers
|
|
80
|
-
if (projectPath.startsWith('builds:')) {
|
|
81
|
-
projectPath = projectPath.replace('builds:', `${config.paths.buildsDir}/`);
|
|
82
|
-
}
|
|
83
|
-
// Get scheme - required
|
|
84
|
-
const scheme = this.getRequiredInput(inputs, 'scheme', stepName);
|
|
85
|
-
// Get configuration - defaults to Release
|
|
86
|
-
const configuration = this.getInput(inputs, 'configuration', 'Release');
|
|
87
|
-
// Get destination - defaults to generic/platform=iOS.
|
|
88
|
-
// `auto` triggers runtime resolution via `xcrun simctl`.
|
|
89
|
-
const destination = resolveDestinationInput(this.getInput(inputs, 'destination', 'generic/platform=iOS'));
|
|
90
|
-
// Get output directory - defaults to build
|
|
91
|
-
const outputDir = this.getInput(inputs, 'output_dir', 'build');
|
|
92
|
-
// Get clean build flag
|
|
93
|
-
const isCleanBuild = this.getInput(inputs, 'is_clean_build', false);
|
|
94
|
-
// Get xcconfig content - optional
|
|
95
|
-
const xcconfigContent = this.getInput(inputs, 'xcconfig_content', '');
|
|
96
|
-
const commands = [];
|
|
97
|
-
commands.push('# Xcode Build');
|
|
98
|
-
commands.push(`echo "Building project: ${this.escapeBash(projectPath)}"`);
|
|
99
|
-
commands.push(`echo "Scheme: ${this.escapeBash(scheme)}"`);
|
|
100
|
-
commands.push(`echo "Configuration: ${this.escapeBash(configuration)}"`);
|
|
101
|
-
// Create output directory and resolve to absolute path. The absolute path
|
|
102
|
-
// is the load-bearing part: a relative one let xcodebuild create a `build`
|
|
103
|
-
// directory inside each SPM package checkout in DerivedData, which fails
|
|
104
|
-
// with "File exists but is not a directory" on repeated runs. That was
|
|
105
|
-
// originally fixed by making CONFIGURATION_BUILD_DIR absolute; it is now
|
|
106
|
-
// -derivedDataPath that is absolute instead, which fixes the same thing
|
|
107
|
-
// without flattening the product tree.
|
|
108
|
-
commands.push('');
|
|
109
|
-
commands.push('# Create output directory and resolve to absolute path');
|
|
110
|
-
commands.push(`mkdir -p '${this.escapeBash(outputDir)}'`);
|
|
111
|
-
commands.push(`OUTPUT_DIR="$(cd '${this.escapeBash(outputDir)}' && pwd)"`);
|
|
112
|
-
commands.push(...derivedDataPathLines());
|
|
113
|
-
// Create xcconfig file if content provided
|
|
114
|
-
if (xcconfigContent && xcconfigContent.trim() !== '') {
|
|
115
|
-
commands.push('');
|
|
116
|
-
commands.push('# Create custom xcconfig file');
|
|
117
|
-
commands.push('XCCONFIG_FILE=".ci-custom.xcconfig"');
|
|
118
|
-
commands.push('cat > "$XCCONFIG_FILE" << \'EOF\'');
|
|
119
|
-
commands.push(xcconfigContent);
|
|
120
|
-
commands.push('EOF');
|
|
121
|
-
commands.push('echo "Custom xcconfig created"');
|
|
122
|
-
}
|
|
123
|
-
// Determine if workspace or project
|
|
124
|
-
commands.push('');
|
|
125
|
-
commands.push('# Determine project type');
|
|
126
|
-
const escapedPath = this.escapeBash(projectPath);
|
|
127
|
-
commands.push(`if [[ '${escapedPath}' == *.xcworkspace ]]; then`);
|
|
128
|
-
commands.push(' PROJECT_TYPE="-workspace"');
|
|
129
|
-
commands.push('else');
|
|
130
|
-
commands.push(' PROJECT_TYPE="-project"');
|
|
131
|
-
commands.push('fi');
|
|
132
|
-
// Resolve the package graph before building, and retry that on a refused
|
|
133
|
-
// fetch. Resolution is seconds and the build is minutes, so the retry is
|
|
134
|
-
// spent on the cheap half. The two -derivedDataPath arguments move
|
|
135
|
-
// together: the build below reads the same derived data and finds the
|
|
136
|
-
// graph already resolved. Split them and the retry is spent on work the
|
|
137
|
-
// build discards.
|
|
138
|
-
commands.push('');
|
|
139
|
-
commands.push('# Resolve package dependencies first — see git-fetch-retry');
|
|
140
|
-
commands.push(...refusedFetchHelpers());
|
|
141
|
-
commands.push(...retryOnRefusedFetch({
|
|
142
|
-
label: 'Package resolution',
|
|
143
|
-
command: 'xcodebuild -resolvePackageDependencies' +
|
|
144
|
-
` "$PROJECT_TYPE" '${escapedPath}'` +
|
|
145
|
-
` -scheme '${this.escapeBash(scheme)}'` +
|
|
146
|
-
' -derivedDataPath "$DERIVED_DATA_PATH"',
|
|
147
|
-
}));
|
|
148
|
-
// Build xcodebuild command
|
|
149
|
-
commands.push('');
|
|
150
|
-
commands.push('# Build xcodebuild command');
|
|
151
|
-
let xcodebuildCmd = 'xcodebuild';
|
|
152
|
-
// Add clean if requested
|
|
153
|
-
if (isCleanBuild) {
|
|
154
|
-
xcodebuildCmd += ' clean';
|
|
155
|
-
}
|
|
156
|
-
xcodebuildCmd += ' build';
|
|
157
|
-
xcodebuildCmd += ` "$PROJECT_TYPE" '${escapedPath}'`;
|
|
158
|
-
xcodebuildCmd += ` -scheme '${this.escapeBash(scheme)}'`;
|
|
159
|
-
xcodebuildCmd += ` -configuration '${this.escapeBash(configuration)}'`;
|
|
160
|
-
xcodebuildCmd += ` -destination '${this.escapeBash(destination)}'`;
|
|
161
|
-
// A derived data path (see derivedDataPathLines), rather than CONFIGURATION_BUILD_DIR.
|
|
162
|
-
// Same reasoning as XcodeBuildForSimulatorStepExecutor below, where the
|
|
163
|
-
// full mechanism and the control are written up: the flat directory
|
|
164
|
-
// collapses xcodebuild's per-configuration/per-platform product separation
|
|
165
|
-
// and any graph with two tasks producing the same output name is refused
|
|
166
|
-
// with "Multiple commands produce". It also desynchronises CocoaPods, whose
|
|
167
|
-
// PODS_CONFIGURATION_BUILD_DIR follows BUILD_DIR and not this setting.
|
|
168
|
-
xcodebuildCmd += ` -derivedDataPath "$DERIVED_DATA_PATH"`;
|
|
169
|
-
// Add xcconfig if provided
|
|
170
|
-
if (xcconfigContent && xcconfigContent.trim() !== '') {
|
|
171
|
-
xcodebuildCmd += ' -xcconfig "$XCCONFIG_FILE"';
|
|
172
|
-
}
|
|
173
|
-
commands.push(xcodebuildCmd);
|
|
174
|
-
// Show build results
|
|
175
|
-
commands.push('');
|
|
176
|
-
commands.push('# Build completed');
|
|
177
|
-
commands.push('echo "Build completed successfully"');
|
|
178
|
-
// Products live under the per-platform Products directory now, not flat in
|
|
179
|
-
// $OUTPUT_DIR, so point the listing at where they actually are — otherwise
|
|
180
|
-
// the log says "artifacts in build/" above an empty directory.
|
|
181
|
-
commands.push('echo "Build artifacts in: $DERIVED_DATA_PATH/Build/Products"');
|
|
182
|
-
commands.push('ls -lhR "$DERIVED_DATA_PATH/Build/Products" 2>/dev/null || true');
|
|
183
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
184
|
-
return this.createScriptStep(script, stepName);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Xcode test step executor
|
|
189
|
-
* Runs tests for iOS/macOS projects using xcodebuild test
|
|
190
|
-
*/
|
|
191
|
-
export class XcodeTestStepExecutor extends BaseStepExecutor {
|
|
192
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
193
|
-
const requirements = [];
|
|
194
|
-
// xcodebuild command must exist
|
|
195
|
-
requirements.push(this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required for iOS tests', 'Install Xcode from the App Store, then run: xcode-select --install'));
|
|
196
|
-
// Required inputs
|
|
197
|
-
requirements.push(this.requireInput('project_path', inputs, 'Path to .xcodeproj or .xcworkspace', 'Provide project_path input in YAML'));
|
|
198
|
-
requirements.push(this.requireInput('scheme', inputs, 'Xcode scheme name', 'Provide scheme input in YAML'));
|
|
199
|
-
// Project file - runtime check only if it doesn't already exist locally
|
|
200
|
-
if (inputs.project_path && !existsSync(inputs.project_path)) {
|
|
201
|
-
requirements.push(this.runtimeRequirement(inputs.project_path, 'Xcode project/workspace file', 'file', 'the checkout'));
|
|
202
|
-
}
|
|
203
|
-
if (inputs.destination === 'auto') {
|
|
204
|
-
requirements.push(this.requireCommand('xcrun', 'xcrun is required to resolve destination: auto via xcrun simctl', 'Install Xcode and run: xcode-select --install'));
|
|
205
|
-
}
|
|
206
|
-
return requirements;
|
|
207
|
-
}
|
|
208
|
-
async execute(inputs, env, config) {
|
|
209
|
-
const stepName = 'xcode-test';
|
|
210
|
-
// Get project path - required
|
|
211
|
-
let projectPath = this.getRequiredInput(inputs, 'project_path', stepName);
|
|
212
|
-
// Resolve path identifiers
|
|
213
|
-
if (projectPath.startsWith('builds:')) {
|
|
214
|
-
projectPath = projectPath.replace('builds:', `${config.paths.buildsDir}/`);
|
|
215
|
-
}
|
|
216
|
-
// Get scheme - required
|
|
217
|
-
const scheme = this.getRequiredInput(inputs, 'scheme', stepName);
|
|
218
|
-
// Get destination - defaults to platform=iOS Simulator.
|
|
219
|
-
// `auto` triggers runtime resolution via `xcrun simctl`.
|
|
220
|
-
const destination = resolveDestinationInput(this.getInput(inputs, 'destination', 'platform=iOS Simulator,name=iPhone 14,OS=latest'));
|
|
221
|
-
// Get test plan - optional
|
|
222
|
-
const testPlan = this.getInput(inputs, 'test_plan', '');
|
|
223
|
-
// Get code coverage flag
|
|
224
|
-
const isCodeCoverageEnabled = this.getInput(inputs, 'is_code_coverage_enabled', false);
|
|
225
|
-
const commands = [];
|
|
226
|
-
commands.push('# Xcode Test');
|
|
227
|
-
commands.push(`echo "Testing project: ${this.escapeBash(projectPath)}"`);
|
|
228
|
-
commands.push(`echo "Scheme: ${this.escapeBash(scheme)}"`);
|
|
229
|
-
// Determine if workspace or project
|
|
230
|
-
commands.push('');
|
|
231
|
-
commands.push('# Determine project type');
|
|
232
|
-
const escapedPath = this.escapeBash(projectPath);
|
|
233
|
-
commands.push(`if [[ '${escapedPath}' == *.xcworkspace ]]; then`);
|
|
234
|
-
commands.push(' PROJECT_TYPE="-workspace"');
|
|
235
|
-
commands.push('else');
|
|
236
|
-
commands.push(' PROJECT_TYPE="-project"');
|
|
237
|
-
commands.push('fi');
|
|
238
|
-
// Build xcodebuild test command
|
|
239
|
-
commands.push('');
|
|
240
|
-
commands.push('# Run tests');
|
|
241
|
-
let xcodebuildCmd = 'xcodebuild test';
|
|
242
|
-
xcodebuildCmd += ` "$PROJECT_TYPE" '${escapedPath}'`;
|
|
243
|
-
xcodebuildCmd += ` -scheme '${this.escapeBash(scheme)}'`;
|
|
244
|
-
xcodebuildCmd += ` -destination '${this.escapeBash(destination)}'`;
|
|
245
|
-
// Add test plan if specified
|
|
246
|
-
if (testPlan && testPlan.trim() !== '') {
|
|
247
|
-
xcodebuildCmd += ` -testPlan '${this.escapeBash(testPlan)}'`;
|
|
248
|
-
}
|
|
249
|
-
// Add code coverage if enabled
|
|
250
|
-
if (isCodeCoverageEnabled) {
|
|
251
|
-
xcodebuildCmd += ' -enableCodeCoverage YES';
|
|
252
|
-
}
|
|
253
|
-
commands.push(xcodebuildCmd);
|
|
254
|
-
// Show test results
|
|
255
|
-
commands.push('');
|
|
256
|
-
commands.push('# Tests completed');
|
|
257
|
-
commands.push('echo "Tests completed successfully"');
|
|
258
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
259
|
-
return this.createScriptStep(script, stepName);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* xcode-archive step executor
|
|
264
|
-
* Archives and exports an iOS app to IPA, mirroring bitrise xcode-archive@5.
|
|
265
|
-
*/
|
|
266
|
-
export class XcodeArchiveStepExecutor extends BaseStepExecutor {
|
|
267
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
268
|
-
const requirements = [];
|
|
269
|
-
requirements.push(this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required for iOS builds', 'Install Xcode from the App Store, then run: xcode-select --install'));
|
|
270
|
-
requirements.push(this.requireInput('project_path', inputs, 'Path to .xcodeproj or .xcworkspace', 'Provide project_path input in YAML'));
|
|
271
|
-
requirements.push(this.requireInput('scheme', inputs, 'Xcode scheme name', 'Provide scheme input in YAML'));
|
|
272
|
-
// Project file - runtime check only if it doesn't already exist locally
|
|
273
|
-
if (inputs.project_path && !existsSync(inputs.project_path)) {
|
|
274
|
-
requirements.push(this.runtimeRequirement(inputs.project_path, 'Xcode project/workspace file', 'file', 'the checkout'));
|
|
275
|
-
}
|
|
276
|
-
return requirements;
|
|
277
|
-
}
|
|
278
|
-
getOutputs() {
|
|
279
|
-
return [
|
|
280
|
-
{ name: 'CIBUILD_IPA_PATH', type: 'environment', description: 'Path to the exported IPA file' },
|
|
281
|
-
{ name: 'CIBUILD_XCARCHIVE_PATH', type: 'environment', description: 'Path to the generated .xcarchive' },
|
|
282
|
-
{ name: 'CIBUILD_DSYM_PATH', type: 'environment', description: 'Path to the zipped dSYM files' },
|
|
283
|
-
{ name: 'CIBUILD_APP_PATH', type: 'environment', description: 'Path to the .app inside the xcarchive (when code signing is off)' },
|
|
284
|
-
];
|
|
285
|
-
}
|
|
286
|
-
async execute(inputs, env, config) {
|
|
287
|
-
const stepName = 'xcode-archive';
|
|
288
|
-
let projectPath = this.getRequiredInput(inputs, 'project_path', stepName);
|
|
289
|
-
if (projectPath.startsWith('builds:')) {
|
|
290
|
-
projectPath = projectPath.replace('builds:', `${config.paths.buildsDir}/`);
|
|
291
|
-
}
|
|
292
|
-
const scheme = this.getRequiredInput(inputs, 'scheme', stepName);
|
|
293
|
-
const configuration = this.getInput(inputs, 'configuration', 'Release');
|
|
294
|
-
const platform = this.getInput(inputs, 'platform', 'iOS');
|
|
295
|
-
const distributionMethod = this.getInput(inputs, 'distribution_method', 'development');
|
|
296
|
-
const performClean = this.getInput(inputs, 'perform_clean_action', 'no');
|
|
297
|
-
const compileBitcode = this.getInput(inputs, 'compile_bitcode', 'yes');
|
|
298
|
-
const uploadBitcode = this.getInput(inputs, 'upload_bitcode', 'yes');
|
|
299
|
-
const automaticCodeSigning = this.getInput(inputs, 'automatic_code_signing', 'off');
|
|
300
|
-
const xcodebuildOptions = this.getInput(inputs, 'xcodebuild_options', '');
|
|
301
|
-
const xcconfigContent = this.getInput(inputs, 'xcconfig_content', 'COMPILER_INDEX_STORE_ENABLE = NO');
|
|
302
|
-
const exportOptionsPlistContent = this.getInput(inputs, 'export_options_plist_content', '');
|
|
303
|
-
const signingStyleInput = this.getInput(inputs, 'signing_style', 'automatic');
|
|
304
|
-
const provisioningProfilesInput = this.getInput(inputs, 'provisioning_profiles', '');
|
|
305
|
-
const outputDir = this.getInput(inputs, 'output_dir', 'build/ipa');
|
|
306
|
-
const artifactName = this.getInput(inputs, 'artifact_name', '');
|
|
307
|
-
const escapedPath = this.escapeBash(projectPath);
|
|
308
|
-
const escapedScheme = this.escapeBash(scheme);
|
|
309
|
-
const escapedConfig = this.escapeBash(configuration);
|
|
310
|
-
const commands = [];
|
|
311
|
-
commands.push('# xcode-archive — archive and export iOS app to IPA');
|
|
312
|
-
commands.push(`echo "Project: ${escapedPath}"`);
|
|
313
|
-
commands.push(`echo "Scheme: ${escapedScheme}"`);
|
|
314
|
-
commands.push(`echo "Configuration: ${escapedConfig}"`);
|
|
315
|
-
commands.push(`echo "Distribution method: ${this.escapeBash(distributionMethod)}"`);
|
|
316
|
-
commands.push('');
|
|
317
|
-
// Determine project type
|
|
318
|
-
commands.push('# Determine project type (-workspace vs -project)');
|
|
319
|
-
commands.push(`if [[ '${escapedPath}' == *.xcworkspace ]]; then`);
|
|
320
|
-
commands.push(' PROJECT_TYPE="-workspace"');
|
|
321
|
-
commands.push('else');
|
|
322
|
-
commands.push(' PROJECT_TYPE="-project"');
|
|
323
|
-
commands.push('fi');
|
|
324
|
-
commands.push('');
|
|
325
|
-
// Derive artifact name
|
|
326
|
-
if (artifactName) {
|
|
327
|
-
commands.push(`ARTIFACT_NAME="${this.escapeBash(artifactName)}"`);
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
commands.push(`ARTIFACT_NAME="${escapedScheme}"`);
|
|
331
|
-
}
|
|
332
|
-
// Paths
|
|
333
|
-
commands.push(`OUTPUT_DIR="${this.escapeBash(outputDir)}"`);
|
|
334
|
-
commands.push('ARCHIVE_PATH="$OUTPUT_DIR/archive/${ARTIFACT_NAME}.xcarchive"');
|
|
335
|
-
commands.push('mkdir -p "$(dirname "$ARCHIVE_PATH")"');
|
|
336
|
-
commands.push('mkdir -p "$OUTPUT_DIR"');
|
|
337
|
-
commands.push('');
|
|
338
|
-
// Write xcconfig override
|
|
339
|
-
commands.push('# Write xcconfig overrides');
|
|
340
|
-
commands.push('XCCONFIG_PATH="$(mktemp -t cibuild-xcconfig).xcconfig"');
|
|
341
|
-
// When automatic_code_signing is off (default), disable code signing so
|
|
342
|
-
// archives succeed on CI without a development team / provisioning profile.
|
|
343
|
-
let effectiveXcconfig = xcconfigContent.trim() || 'COMPILER_INDEX_STORE_ENABLE = NO';
|
|
344
|
-
if (automaticCodeSigning === 'off') {
|
|
345
|
-
effectiveXcconfig += '\nCODE_SIGNING_ALLOWED = NO';
|
|
346
|
-
effectiveXcconfig += '\nCODE_SIGN_IDENTITY = -';
|
|
347
|
-
}
|
|
348
|
-
commands.push(`cat > "$XCCONFIG_PATH" << 'XCCONFIG_EOF'`);
|
|
349
|
-
commands.push(effectiveXcconfig);
|
|
350
|
-
commands.push('XCCONFIG_EOF');
|
|
351
|
-
commands.push('');
|
|
352
|
-
// Detect Apple API key for cloud-managed signing (xcodebuild
|
|
353
|
-
// -allowProvisioningUpdates). When APPLE_API_KEY_ID/ISSUER_ID/KEY_PATH
|
|
354
|
-
// are resolved by the env-resolver (from .cibuild-secrets.json or
|
|
355
|
-
// CIBUILD_SW__* env vars), embed the .p8 into the generated script via
|
|
356
|
-
// heredoc and bake the IDs as literals. Reading from $APPLE_API_KEY_ID
|
|
357
|
-
// at script runtime would not work because cibuild's runner only
|
|
358
|
-
// forwards process.env to spawned scripts, and EnvResolver-decoded
|
|
359
|
-
// secrets live in its in-memory map, not process.env.
|
|
360
|
-
let xcodeAuthFlags = '';
|
|
361
|
-
if (automaticCodeSigning !== 'off') {
|
|
362
|
-
const apiKeyId = (env['APPLE_API_KEY_ID'] || '').trim();
|
|
363
|
-
const apiIssuerId = (env['APPLE_API_ISSUER_ID'] || '').trim();
|
|
364
|
-
const apiKeySecret = env['APPLE_API_KEY_PATH'] || '';
|
|
365
|
-
// Secret may be raw PEM (`-----BEGIN PRIVATE KEY-----…`) or base64-encoded.
|
|
366
|
-
let p8Pem = '';
|
|
367
|
-
if (apiKeySecret) {
|
|
368
|
-
if (apiKeySecret.trimStart().startsWith('-----BEGIN')) {
|
|
369
|
-
p8Pem = apiKeySecret;
|
|
370
|
-
}
|
|
371
|
-
else {
|
|
372
|
-
try {
|
|
373
|
-
p8Pem = Buffer.from(apiKeySecret, 'base64').toString('utf-8');
|
|
374
|
-
}
|
|
375
|
-
catch {
|
|
376
|
-
p8Pem = '';
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
const p8LooksValid = p8Pem.includes('-----BEGIN') && p8Pem.includes('PRIVATE KEY');
|
|
381
|
-
if (apiKeyId && apiIssuerId && p8LooksValid) {
|
|
382
|
-
commands.push('# Apple API key resolved — embedding for cloud-managed signing');
|
|
383
|
-
// BSD/macOS mktemp requires X's at the end of the template; any chars
|
|
384
|
-
// after them are literal and not substituted. Don't add a .p8 suffix.
|
|
385
|
-
commands.push('API_KEY_TEMP_FILE="$(mktemp /tmp/cibuild-api-key.XXXXXX)"');
|
|
386
|
-
commands.push(`trap 'rm -f "$API_KEY_TEMP_FILE"' EXIT`);
|
|
387
|
-
// Quoted heredoc so the PEM body isn't subject to shell expansion.
|
|
388
|
-
commands.push(`cat > "$API_KEY_TEMP_FILE" << 'CIBUILD_API_KEY_EOF'`);
|
|
389
|
-
commands.push(p8Pem.replace(/\r\n/g, '\n').replace(/\n+$/, ''));
|
|
390
|
-
commands.push('CIBUILD_API_KEY_EOF');
|
|
391
|
-
commands.push('echo "✓ Cloud-managed signing enabled (API key: '
|
|
392
|
-
+ this.escapeBash(apiKeyId) + ')"');
|
|
393
|
-
commands.push('');
|
|
394
|
-
// Build a bash-syntax fragment that gets concatenated directly into
|
|
395
|
-
// the archive/export command lines. Single quotes here are real shell
|
|
396
|
-
// quotes (consumed by bash) — unlike the previous approach that put
|
|
397
|
-
// them inside a doubly-quoted variable assignment, where they would
|
|
398
|
-
// become literal characters in the value.
|
|
399
|
-
xcodeAuthFlags =
|
|
400
|
-
` -allowProvisioningUpdates` +
|
|
401
|
-
` -authenticationKeyPath "$API_KEY_TEMP_FILE"` +
|
|
402
|
-
` -authenticationKeyID '${this.escapeBash(apiKeyId)}'` +
|
|
403
|
-
` -authenticationKeyIssuerID '${this.escapeBash(apiIssuerId)}'`;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
// Build xcodebuild archive command
|
|
407
|
-
commands.push('# Run xcodebuild archive');
|
|
408
|
-
let archiveCmd = 'xcodebuild';
|
|
409
|
-
if (performClean === 'yes')
|
|
410
|
-
archiveCmd += ' clean';
|
|
411
|
-
archiveCmd += ' archive';
|
|
412
|
-
archiveCmd += ` "$PROJECT_TYPE" '${escapedPath}'`;
|
|
413
|
-
archiveCmd += ` -scheme '${escapedScheme}'`;
|
|
414
|
-
archiveCmd += ` -configuration '${escapedConfig}'`;
|
|
415
|
-
archiveCmd += ` -destination 'generic/platform=${this.escapeBash(platform)}'`;
|
|
416
|
-
archiveCmd += ' -archivePath "$ARCHIVE_PATH"';
|
|
417
|
-
archiveCmd += ' -xcconfig "$XCCONFIG_PATH"';
|
|
418
|
-
// When code signing is off, also pass settings as command-line build
|
|
419
|
-
// settings so they take highest priority and override any target-level
|
|
420
|
-
// signing configuration in the Xcode project.
|
|
421
|
-
if (automaticCodeSigning === 'off') {
|
|
422
|
-
archiveCmd += ' CODE_SIGNING_ALLOWED=NO CODE_SIGN_IDENTITY=- CODE_SIGNING_REQUIRED=NO';
|
|
423
|
-
}
|
|
424
|
-
else {
|
|
425
|
-
archiveCmd += xcodeAuthFlags;
|
|
426
|
-
// Don't override CODE_SIGN_IDENTITY here. Xcode 26.3+ rejects an explicit
|
|
427
|
-
// identity when the project is configured for automatic signing
|
|
428
|
-
// ("conflicting provisioning settings" at GatherProvisioningInputs).
|
|
429
|
-
// Archive with the project's automatic identity (Development); the
|
|
430
|
-
// exportArchive step re-signs with Distribution via exportOptionsPlist
|
|
431
|
-
// (method=app-store + signingStyle=automatic), mirroring Xcode IDE's
|
|
432
|
-
// Archive → Distribute flow. DEVELOPMENT_TEAM is still injected so SPM
|
|
433
|
-
// frameworks without an explicit team in Package.swift can sign.
|
|
434
|
-
const teamId = (env['APPLE_TEAM_ID'] || '').trim();
|
|
435
|
-
if (teamId) {
|
|
436
|
-
archiveCmd += ` DEVELOPMENT_TEAM='${this.escapeBash(teamId)}'`;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
if (xcodebuildOptions) {
|
|
440
|
-
archiveCmd += ` ${xcodebuildOptions}`;
|
|
441
|
-
}
|
|
442
|
-
commands.push(archiveCmd);
|
|
443
|
-
commands.push('');
|
|
444
|
-
commands.push('echo "✓ Archive created: $ARCHIVE_PATH"');
|
|
445
|
-
commands.push('');
|
|
446
|
-
// Write output env var helper (used in both signing and non-signing paths)
|
|
447
|
-
commands.push('# Export output environment variables');
|
|
448
|
-
commands.push('ENVSTORE_FILE="${ENVMAN_ENVSTORE_PATH:-.ci/.envstore.json}"');
|
|
449
|
-
commands.push('export_env() {');
|
|
450
|
-
commands.push(' local key="$1" val="$2"');
|
|
451
|
-
commands.push(' if [ -f "$ENVSTORE_FILE" ]; then');
|
|
452
|
-
commands.push(' node -e "');
|
|
453
|
-
commands.push(' const fs=require(\'fs\');');
|
|
454
|
-
commands.push(' const s=JSON.parse(fs.readFileSync(process.argv[1],\'utf-8\'));');
|
|
455
|
-
commands.push(' s.envs=s.envs||[];');
|
|
456
|
-
commands.push(' s.envs=s.envs.filter(e=>e.key!==process.argv[2]);');
|
|
457
|
-
commands.push(' s.envs.push({key:process.argv[2],value:process.argv[3]});');
|
|
458
|
-
commands.push(' fs.writeFileSync(process.argv[1],JSON.stringify(s));');
|
|
459
|
-
commands.push(' " "$ENVSTORE_FILE" "$key" "$val" 2>/dev/null || true');
|
|
460
|
-
commands.push(' fi');
|
|
461
|
-
commands.push(' export "$key=$val"');
|
|
462
|
-
commands.push(' echo " $key=$val"');
|
|
463
|
-
commands.push('}');
|
|
464
|
-
commands.push('');
|
|
465
|
-
if (automaticCodeSigning === 'off') {
|
|
466
|
-
// Signing is disabled — skip IPA export (unsigned archives cannot be exported).
|
|
467
|
-
// Output the xcarchive path and the .app inside it instead.
|
|
468
|
-
commands.push('# Code signing disabled — export xcarchive path only');
|
|
469
|
-
commands.push('export_env CIBUILD_XCARCHIVE_PATH "$(pwd)/$ARCHIVE_PATH"');
|
|
470
|
-
commands.push('');
|
|
471
|
-
commands.push('# Locate .app inside the xcarchive for downstream use');
|
|
472
|
-
commands.push('APP_PATH=$(find "$ARCHIVE_PATH/Products" -name "*.app" -maxdepth 3 2>/dev/null | head -1 || true)');
|
|
473
|
-
commands.push('[ -n "$APP_PATH" ] && export_env CIBUILD_APP_PATH "$(pwd)/$APP_PATH" || true');
|
|
474
|
-
commands.push('');
|
|
475
|
-
commands.push('echo "✓ xcode-archive complete (unsigned — code signing disabled)"');
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
// Signing enabled — generate ExportOptions.plist and export to IPA
|
|
479
|
-
commands.push('# Generate ExportOptions.plist');
|
|
480
|
-
commands.push('EXPORT_PLIST="$(mktemp -t ExportOptions).plist"');
|
|
481
|
-
if (exportOptionsPlistContent.trim()) {
|
|
482
|
-
commands.push(`cat > "$EXPORT_PLIST" << 'PLIST_EOF'`);
|
|
483
|
-
commands.push(exportOptionsPlistContent);
|
|
484
|
-
commands.push('PLIST_EOF');
|
|
485
|
-
}
|
|
486
|
-
else {
|
|
487
|
-
// Map deprecated "development" method name to "debugging" (Apple renamed it in Xcode 15)
|
|
488
|
-
const exportMethod = distributionMethod === 'development' ? 'debugging' : distributionMethod;
|
|
489
|
-
const compileBitcodeVal = compileBitcode === 'yes' ? 'true' : 'false';
|
|
490
|
-
const uploadBitcodeVal = uploadBitcode === 'yes' ? 'true' : 'false';
|
|
491
|
-
const signingStyle = signingStyleInput === 'manual' ? 'manual' : 'automatic';
|
|
492
|
-
// Parse provisioning_profiles input: each non-empty line is `bundleId=ProfileName`.
|
|
493
|
-
// Lines starting with # are treated as comments. Whitespace is trimmed.
|
|
494
|
-
const profileEntries = [];
|
|
495
|
-
if (signingStyle === 'manual' && provisioningProfilesInput.trim()) {
|
|
496
|
-
for (const rawLine of provisioningProfilesInput.split('\n')) {
|
|
497
|
-
const line = rawLine.trim();
|
|
498
|
-
if (!line || line.startsWith('#'))
|
|
499
|
-
continue;
|
|
500
|
-
const eq = line.indexOf('=');
|
|
501
|
-
if (eq === -1)
|
|
502
|
-
continue;
|
|
503
|
-
const bundleId = line.slice(0, eq).trim();
|
|
504
|
-
const profile = line.slice(eq + 1).trim();
|
|
505
|
-
if (bundleId && profile)
|
|
506
|
-
profileEntries.push({ bundleId, profile });
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
if (signingStyle === 'manual') {
|
|
510
|
-
if (profileEntries.length === 0) {
|
|
511
|
-
throw new Error(`xcode-archive: signing_style=manual requires provisioning_profiles input ` +
|
|
512
|
-
`(newline-separated 'bundleId=ProfileName' pairs).`);
|
|
513
|
-
}
|
|
514
|
-
// Manual export needs APPLE_TEAM_ID at runtime — fail fast with a clear message.
|
|
515
|
-
commands.push('# signing_style=manual — verify APPLE_TEAM_ID is available');
|
|
516
|
-
commands.push('if [ -z "${APPLE_TEAM_ID:-}" ]; then');
|
|
517
|
-
commands.push(' echo "❌ Error: signing_style=manual requires APPLE_TEAM_ID env var." >&2');
|
|
518
|
-
commands.push(' echo " Set via: ci secrets add APPLE_TEAM_ID -w <workflow>" >&2');
|
|
519
|
-
commands.push(' exit 1');
|
|
520
|
-
commands.push('fi');
|
|
521
|
-
commands.push('');
|
|
522
|
-
}
|
|
523
|
-
// Manual mode references ${APPLE_TEAM_ID}, which requires an *unquoted* heredoc
|
|
524
|
-
// delimiter so bash performs variable expansion. Automatic mode uses single-quoted
|
|
525
|
-
// delimiter so the plist content is preserved literally.
|
|
526
|
-
const heredocOpen = signingStyle === 'manual'
|
|
527
|
-
? `cat > "$EXPORT_PLIST" << PLIST_EOF`
|
|
528
|
-
: `cat > "$EXPORT_PLIST" << 'PLIST_EOF'`;
|
|
529
|
-
commands.push(heredocOpen);
|
|
530
|
-
commands.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
531
|
-
commands.push('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">');
|
|
532
|
-
commands.push('<plist version="1.0">');
|
|
533
|
-
commands.push('<dict>');
|
|
534
|
-
commands.push(' <key>method</key>');
|
|
535
|
-
commands.push(` <string>${exportMethod}</string>`);
|
|
536
|
-
commands.push(' <key>signingStyle</key>');
|
|
537
|
-
commands.push(` <string>${signingStyle}</string>`);
|
|
538
|
-
if (signingStyle === 'manual') {
|
|
539
|
-
commands.push(' <key>teamID</key>');
|
|
540
|
-
commands.push(' <string>${APPLE_TEAM_ID}</string>');
|
|
541
|
-
commands.push(' <key>provisioningProfiles</key>');
|
|
542
|
-
commands.push(' <dict>');
|
|
543
|
-
for (const { bundleId, profile } of profileEntries) {
|
|
544
|
-
commands.push(` <key>${bundleId}</key>`);
|
|
545
|
-
commands.push(` <string>${profile}</string>`);
|
|
546
|
-
}
|
|
547
|
-
commands.push(' </dict>');
|
|
548
|
-
}
|
|
549
|
-
if (exportMethod !== 'app-store') {
|
|
550
|
-
commands.push(' <key>compileBitcode</key>');
|
|
551
|
-
commands.push(` <${compileBitcodeVal}/>`);
|
|
552
|
-
}
|
|
553
|
-
if (exportMethod === 'app-store') {
|
|
554
|
-
commands.push(' <key>uploadBitcode</key>');
|
|
555
|
-
commands.push(` <${uploadBitcodeVal}/>`);
|
|
556
|
-
}
|
|
557
|
-
commands.push('</dict>');
|
|
558
|
-
commands.push('</plist>');
|
|
559
|
-
commands.push('PLIST_EOF');
|
|
560
|
-
}
|
|
561
|
-
commands.push('');
|
|
562
|
-
// Export archive to IPA
|
|
563
|
-
commands.push('# Export archive to IPA');
|
|
564
|
-
let exportCmd = 'xcodebuild -exportArchive';
|
|
565
|
-
exportCmd += ' -archivePath "$ARCHIVE_PATH"';
|
|
566
|
-
exportCmd += ' -exportOptionsPlist "$EXPORT_PLIST"';
|
|
567
|
-
exportCmd += ' -exportPath "$OUTPUT_DIR"';
|
|
568
|
-
exportCmd += xcodeAuthFlags;
|
|
569
|
-
commands.push(exportCmd);
|
|
570
|
-
commands.push('');
|
|
571
|
-
// Locate generated IPA
|
|
572
|
-
commands.push('# Locate generated IPA and export paths');
|
|
573
|
-
commands.push('IPA_PATH=$(find "$OUTPUT_DIR" -name "*.ipa" | head -1)');
|
|
574
|
-
commands.push('DSYM_PATH=$(find "$OUTPUT_DIR" -name "*.dSYM.zip" 2>/dev/null | head -1 || true)');
|
|
575
|
-
commands.push('');
|
|
576
|
-
commands.push('if [ -z "$IPA_PATH" ]; then');
|
|
577
|
-
commands.push(' echo "Error: IPA not found in $OUTPUT_DIR" >&2');
|
|
578
|
-
commands.push(' exit 1');
|
|
579
|
-
commands.push('fi');
|
|
580
|
-
commands.push('');
|
|
581
|
-
commands.push('echo "✓ IPA created: $IPA_PATH"');
|
|
582
|
-
commands.push('ls -lh "$IPA_PATH"');
|
|
583
|
-
commands.push('');
|
|
584
|
-
commands.push('export_env CIBUILD_IPA_PATH "$IPA_PATH"');
|
|
585
|
-
commands.push('export_env CIBUILD_XCARCHIVE_PATH "$(pwd)/$ARCHIVE_PATH"');
|
|
586
|
-
commands.push('[ -n "$DSYM_PATH" ] && export_env CIBUILD_DSYM_PATH "$DSYM_PATH" || true');
|
|
587
|
-
commands.push('');
|
|
588
|
-
commands.push('echo "✓ xcode-archive complete"');
|
|
589
|
-
commands.push('rm -f "$EXPORT_PLIST"');
|
|
590
|
-
}
|
|
591
|
-
commands.push('rm -f "$XCCONFIG_PATH"');
|
|
592
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
593
|
-
return this.createScriptStep(script, stepName);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* iOS Archive step executor
|
|
598
|
-
* Creates an IPA file from a .app bundle
|
|
599
|
-
*/
|
|
600
|
-
export class IosArchiveStepExecutor extends BaseStepExecutor {
|
|
601
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
602
|
-
const requirements = [];
|
|
603
|
-
// Required input
|
|
604
|
-
requirements.push(this.requireInput('app_path', inputs, 'Path to .app bundle', 'Provide app_path input pointing to your built .app file'));
|
|
605
|
-
// zip command for creating IPA
|
|
606
|
-
requirements.push(this.requireCommand('zip', 'zip utility for creating IPA files', 'zip should be pre-installed on macOS'));
|
|
607
|
-
// .app file - runtime check (created by xcodebuild)
|
|
608
|
-
if (inputs.app_path) {
|
|
609
|
-
requirements.push(this.runtimeRequirement(inputs.app_path, '.app bundle file', 'directory', 'xcodebuild'));
|
|
610
|
-
}
|
|
611
|
-
return requirements;
|
|
612
|
-
}
|
|
613
|
-
async execute(inputs, env, config) {
|
|
614
|
-
const stepName = 'ios-archive';
|
|
615
|
-
// Get app path - required
|
|
616
|
-
let appPath = this.getRequiredInput(inputs, 'app_path', stepName);
|
|
617
|
-
// Resolve path identifiers
|
|
618
|
-
if (appPath.startsWith('builds:')) {
|
|
619
|
-
appPath = appPath.replace('builds:', `${config.paths.buildsDir}/`);
|
|
620
|
-
}
|
|
621
|
-
// Get output path - defaults to artifacts/
|
|
622
|
-
const outputPath = this.getInput(inputs, 'output_path', 'artifacts');
|
|
623
|
-
// Get output name - optional, will derive from .app name if not provided
|
|
624
|
-
const outputName = this.getInput(inputs, 'output_name', '');
|
|
625
|
-
const commands = [];
|
|
626
|
-
commands.push('# iOS Archive - Create IPA');
|
|
627
|
-
commands.push(`echo "Creating IPA from: ${this.escapeBash(appPath)}"`);
|
|
628
|
-
// Check if .app exists
|
|
629
|
-
commands.push('');
|
|
630
|
-
commands.push('# Verify .app bundle exists');
|
|
631
|
-
commands.push(`if [ ! -d '${this.escapeBash(appPath)}' ]; then`);
|
|
632
|
-
commands.push(` echo "Error: .app bundle not found at ${this.escapeBash(appPath)}"`);
|
|
633
|
-
commands.push(' exit 1');
|
|
634
|
-
commands.push('fi');
|
|
635
|
-
// Determine output IPA name
|
|
636
|
-
commands.push('');
|
|
637
|
-
commands.push('# Determine IPA name');
|
|
638
|
-
if (outputName && outputName.trim() !== '') {
|
|
639
|
-
// Use provided name
|
|
640
|
-
const cleanName = outputName.replace(/\.ipa$/, '');
|
|
641
|
-
commands.push(`IPA_NAME="${this.escapeBash(cleanName)}"`);
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
// Derive from .app bundle name
|
|
645
|
-
commands.push(`APP_BUNDLE='${this.escapeBash(appPath)}'`);
|
|
646
|
-
commands.push('IPA_NAME=$(basename "$APP_BUNDLE" .app)');
|
|
647
|
-
}
|
|
648
|
-
commands.push('echo "IPA name: $IPA_NAME.ipa"');
|
|
649
|
-
// Create IPA
|
|
650
|
-
commands.push('');
|
|
651
|
-
commands.push('# Create IPA structure');
|
|
652
|
-
commands.push('TEMP_DIR=$(mktemp -d)');
|
|
653
|
-
commands.push('mkdir -p "$TEMP_DIR/Payload"');
|
|
654
|
-
commands.push('');
|
|
655
|
-
commands.push('# Copy .app to Payload directory');
|
|
656
|
-
commands.push(`cp -r '${this.escapeBash(appPath)}' "$TEMP_DIR/Payload/"`);
|
|
657
|
-
commands.push('');
|
|
658
|
-
commands.push('# Create IPA (zip with .ipa extension)');
|
|
659
|
-
commands.push('cd "$TEMP_DIR"');
|
|
660
|
-
commands.push(`zip -r "$IPA_NAME.ipa" Payload -q`);
|
|
661
|
-
commands.push('cd - > /dev/null');
|
|
662
|
-
commands.push('');
|
|
663
|
-
commands.push('# Create output directory and move IPA');
|
|
664
|
-
commands.push(`mkdir -p '${this.escapeBash(outputPath)}'`);
|
|
665
|
-
commands.push(`mv "$TEMP_DIR/$IPA_NAME.ipa" '${this.escapeBash(outputPath)}/'`);
|
|
666
|
-
commands.push('');
|
|
667
|
-
commands.push('# Cleanup temp directory');
|
|
668
|
-
commands.push('rm -rf "$TEMP_DIR"');
|
|
669
|
-
commands.push('');
|
|
670
|
-
commands.push('# Show result');
|
|
671
|
-
commands.push('echo "✓ IPA created successfully"');
|
|
672
|
-
commands.push(`ls -lh '${this.escapeBash(outputPath)}'/"$IPA_NAME.ipa"`);
|
|
673
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
674
|
-
return this.createScriptStep(script, stepName);
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
/**
|
|
678
|
-
* OTA install step executor.
|
|
679
|
-
* Generates manifest.plist and a QR code for itms-services:// OTA installation.
|
|
680
|
-
* The IPA and manifest must be hosted at user-provided HTTPS URLs.
|
|
681
|
-
*/
|
|
682
|
-
export class OtaInstallStepExecutor extends BaseStepExecutor {
|
|
683
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
684
|
-
return [
|
|
685
|
-
this.requireInput('ipa_url', inputs, 'HTTPS URL where the IPA file will be hosted', 'Provide the full HTTPS URL to where the IPA will be uploaded'),
|
|
686
|
-
this.requireInput('bundle_id', inputs, 'iOS bundle identifier (e.g. com.example.app)'),
|
|
687
|
-
this.requireInput('bundle_version', inputs, 'App version string (e.g. 1.2.3)'),
|
|
688
|
-
this.requireInput('title', inputs, 'App display name shown during install'),
|
|
689
|
-
];
|
|
690
|
-
}
|
|
691
|
-
getOutputs() {
|
|
692
|
-
return [
|
|
693
|
-
{
|
|
694
|
-
name: 'CIBUILD_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL',
|
|
695
|
-
type: 'environment',
|
|
696
|
-
description: 'QR code image URL for the OTA install page (api.qrserver.com link)',
|
|
697
|
-
},
|
|
698
|
-
];
|
|
699
|
-
}
|
|
700
|
-
async execute(inputs, _env, _config) {
|
|
701
|
-
const stepName = 'ota-install';
|
|
702
|
-
const ipaUrl = this.getRequiredInput(inputs, 'ipa_url', stepName);
|
|
703
|
-
const bundleId = this.getRequiredInput(inputs, 'bundle_id', stepName);
|
|
704
|
-
const bundleVersion = this.getRequiredInput(inputs, 'bundle_version', stepName);
|
|
705
|
-
const title = this.getRequiredInput(inputs, 'title', stepName);
|
|
706
|
-
const outputDir = this.getInput(inputs, 'output_dir', '.ci/artifacts');
|
|
707
|
-
const manifestUrlInput = this.getInput(inputs, 'manifest_url', '');
|
|
708
|
-
const escapedIpaUrl = this.escapeBash(ipaUrl);
|
|
709
|
-
const escapedBundleId = this.escapeBash(bundleId);
|
|
710
|
-
const escapedBundleVersion = this.escapeBash(bundleVersion);
|
|
711
|
-
const escapedTitle = this.escapeBash(title);
|
|
712
|
-
const escapedOutputDir = this.escapeBash(outputDir);
|
|
713
|
-
const commands = [];
|
|
714
|
-
commands.push('# ota-install — generate OTA manifest + QR code');
|
|
715
|
-
commands.push(`IPA_URL='${escapedIpaUrl}'`);
|
|
716
|
-
commands.push(`BUNDLE_ID='${escapedBundleId}'`);
|
|
717
|
-
commands.push(`BUNDLE_VERSION='${escapedBundleVersion}'`);
|
|
718
|
-
commands.push(`TITLE='${escapedTitle}'`);
|
|
719
|
-
commands.push(`OUTPUT_DIR='${escapedOutputDir}'`);
|
|
720
|
-
commands.push('');
|
|
721
|
-
// Derive manifest URL if not provided
|
|
722
|
-
if (manifestUrlInput.trim()) {
|
|
723
|
-
commands.push(`MANIFEST_URL='${this.escapeBash(manifestUrlInput)}'`);
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
commands.push('# Derive manifest URL from IPA URL (same directory, manifest.plist filename)');
|
|
727
|
-
commands.push('MANIFEST_URL="$(dirname "$IPA_URL")/manifest.plist"');
|
|
728
|
-
}
|
|
729
|
-
commands.push('');
|
|
730
|
-
// Build OTA link
|
|
731
|
-
commands.push('OTA_URL="itms-services://?action=download-manifest&url=${MANIFEST_URL}"');
|
|
732
|
-
commands.push('');
|
|
733
|
-
// Create output dir and write manifest.plist
|
|
734
|
-
commands.push('mkdir -p "$OUTPUT_DIR"');
|
|
735
|
-
commands.push('');
|
|
736
|
-
commands.push('# Write manifest.plist');
|
|
737
|
-
commands.push('cat > "$OUTPUT_DIR/manifest.plist" << PLIST_EOF');
|
|
738
|
-
commands.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
739
|
-
commands.push('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">');
|
|
740
|
-
commands.push('<plist version="1.0">');
|
|
741
|
-
commands.push('<dict>');
|
|
742
|
-
commands.push(' <key>items</key>');
|
|
743
|
-
commands.push(' <array>');
|
|
744
|
-
commands.push(' <dict>');
|
|
745
|
-
commands.push(' <key>assets</key>');
|
|
746
|
-
commands.push(' <array>');
|
|
747
|
-
commands.push(' <dict>');
|
|
748
|
-
commands.push(' <key>kind</key><string>software-package</string>');
|
|
749
|
-
commands.push(' <key>url</key><string>${IPA_URL}</string>');
|
|
750
|
-
commands.push(' </dict>');
|
|
751
|
-
commands.push(' </array>');
|
|
752
|
-
commands.push(' <key>metadata</key>');
|
|
753
|
-
commands.push(' <dict>');
|
|
754
|
-
commands.push(' <key>bundle-identifier</key><string>${BUNDLE_ID}</string>');
|
|
755
|
-
commands.push(' <key>bundle-version</key><string>${BUNDLE_VERSION}</string>');
|
|
756
|
-
commands.push(' <key>kind</key><string>software</string>');
|
|
757
|
-
commands.push(' <key>title</key><string>${TITLE}</string>');
|
|
758
|
-
commands.push(' </dict>');
|
|
759
|
-
commands.push(' </dict>');
|
|
760
|
-
commands.push(' </array>');
|
|
761
|
-
commands.push('</dict>');
|
|
762
|
-
commands.push('</plist>');
|
|
763
|
-
commands.push('PLIST_EOF');
|
|
764
|
-
commands.push('');
|
|
765
|
-
commands.push('echo "✓ manifest.plist written to $OUTPUT_DIR/manifest.plist"');
|
|
766
|
-
commands.push('');
|
|
767
|
-
// Generate QR code (qrencode preferred; graceful fallback)
|
|
768
|
-
commands.push('# Generate QR code');
|
|
769
|
-
commands.push('if command -v qrencode &>/dev/null; then');
|
|
770
|
-
commands.push(' echo ""');
|
|
771
|
-
commands.push(' echo "Scan to install:"');
|
|
772
|
-
commands.push(' qrencode -t ansiutf8 "$OTA_URL"');
|
|
773
|
-
commands.push('else');
|
|
774
|
-
commands.push(' echo "ℹ Install qrencode for terminal QR: brew install qrencode"');
|
|
775
|
-
commands.push('fi');
|
|
776
|
-
commands.push('');
|
|
777
|
-
// Print summary
|
|
778
|
-
commands.push('echo ""');
|
|
779
|
-
commands.push('echo "─────────────────────────────────────────"');
|
|
780
|
-
commands.push('echo "OTA Install Summary"');
|
|
781
|
-
commands.push('echo "─────────────────────────────────────────"');
|
|
782
|
-
commands.push('echo "IPA URL: $IPA_URL"');
|
|
783
|
-
commands.push('echo "Manifest URL: $MANIFEST_URL"');
|
|
784
|
-
commands.push('echo "OTA Link: $OTA_URL"');
|
|
785
|
-
commands.push('echo ""');
|
|
786
|
-
commands.push('echo "Host the IPA and manifest.plist at the URLs above, then share the OTA link."');
|
|
787
|
-
commands.push('echo "─────────────────────────────────────────"');
|
|
788
|
-
commands.push('');
|
|
789
|
-
// Export QR image URL to envstore (api.qrserver.com image link, usable in notifications)
|
|
790
|
-
commands.push('# Export QR image URL to envstore for downstream steps (e.g. Slack notifications)');
|
|
791
|
-
commands.push('ENCODED_OTA="$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))" "$OTA_URL" 2>/dev/null || printf "%s" "$OTA_URL" | sed \'s/ /%20/g; s/:/%3A/g; s|/|%2F|g; s/?/%3F/g; s/=/%3D/g; s/&/%26/g\')"');
|
|
792
|
-
commands.push('QR_IMAGE_URL="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${ENCODED_OTA}"');
|
|
793
|
-
commands.push('');
|
|
794
|
-
commands.push('ENVSTORE_FILE="${ENVMAN_ENVSTORE_PATH:-.ci/.envstore.json}"');
|
|
795
|
-
commands.push('if [ -f "$ENVSTORE_FILE" ]; then');
|
|
796
|
-
commands.push(' node -e "');
|
|
797
|
-
commands.push(' const fs=require(\'fs\');');
|
|
798
|
-
commands.push(' const s=JSON.parse(fs.readFileSync(process.argv[1],\'utf-8\'));');
|
|
799
|
-
commands.push(' s.envs=s.envs||[];');
|
|
800
|
-
commands.push(' s.envs=s.envs.filter(e=>e.key!==\'CIBUILD_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL\');');
|
|
801
|
-
commands.push(' s.envs.push({key:\'CIBUILD_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL\',value:process.argv[2]});');
|
|
802
|
-
commands.push(' fs.writeFileSync(process.argv[1],JSON.stringify(s));');
|
|
803
|
-
commands.push(' " "$ENVSTORE_FILE" "$QR_IMAGE_URL" 2>/dev/null || true');
|
|
804
|
-
commands.push('fi');
|
|
805
|
-
commands.push('export CIBUILD_PUBLIC_INSTALL_PAGE_QR_CODE_IMAGE_URL="$QR_IMAGE_URL"');
|
|
806
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
807
|
-
return this.createScriptStep(script, stepName);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Builds the app and tests without executing them.
|
|
812
|
-
* Produces an .xctestrun file for use with xcode-test-without-building.
|
|
813
|
-
*/
|
|
814
|
-
export class XcodeBuildForTestStepExecutor extends BaseStepExecutor {
|
|
815
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
816
|
-
const requirements = [];
|
|
817
|
-
requirements.push(this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required', 'Install Xcode, then run: xcode-select --install'));
|
|
818
|
-
requirements.push(this.requireInput('project_path', inputs, 'Path to .xcodeproj or .xcworkspace'));
|
|
819
|
-
requirements.push(this.requireInput('scheme', inputs, 'Xcode scheme name'));
|
|
820
|
-
if (inputs.project_path && !existsSync(inputs.project_path)) {
|
|
821
|
-
requirements.push(this.runtimeRequirement(inputs.project_path, 'Xcode project/workspace file', 'file', 'the checkout'));
|
|
822
|
-
}
|
|
823
|
-
if (inputs.destination === 'auto') {
|
|
824
|
-
requirements.push(this.requireCommand('xcrun', 'xcrun is required to resolve destination: auto via xcrun simctl', 'Install Xcode and run: xcode-select --install'));
|
|
825
|
-
}
|
|
826
|
-
return requirements;
|
|
827
|
-
}
|
|
828
|
-
getOutputs() {
|
|
829
|
-
return [
|
|
830
|
-
{ name: 'CIBUILD_TEST_BUNDLE_PATH', type: 'environment', description: 'Directory containing the built test bundle' },
|
|
831
|
-
{ name: 'CIBUILD_XCTESTRUN_FILE_PATH', type: 'environment', description: 'Path to the generated .xctestrun file' },
|
|
832
|
-
];
|
|
833
|
-
}
|
|
834
|
-
async execute(inputs, _env, _config) {
|
|
835
|
-
const stepName = 'xcode-build-for-test';
|
|
836
|
-
const projectPath = this.getRequiredInput(inputs, 'project_path', stepName);
|
|
837
|
-
const scheme = this.getRequiredInput(inputs, 'scheme', stepName);
|
|
838
|
-
const configuration = this.getInput(inputs, 'configuration', 'Debug');
|
|
839
|
-
// `auto` triggers runtime resolution via `xcrun simctl`.
|
|
840
|
-
const destination = resolveDestinationInput(this.getInput(inputs, 'destination', 'generic/platform=iOS Simulator'));
|
|
841
|
-
const testPlan = this.getInput(inputs, 'test_plan', '');
|
|
842
|
-
const xcconfigContent = this.getInput(inputs, 'xcconfig_content', '');
|
|
843
|
-
const xcodebuildOptions = this.getInput(inputs, 'xcodebuild_options', '');
|
|
844
|
-
const outputDir = this.getInput(inputs, 'output_dir', 'build');
|
|
845
|
-
const escapedPath = this.escapeBash(projectPath);
|
|
846
|
-
const escapedScheme = this.escapeBash(scheme);
|
|
847
|
-
const commands = [];
|
|
848
|
-
commands.push('# xcode-build-for-test — build-for-testing');
|
|
849
|
-
commands.push(`echo "Building for testing: ${escapedPath}"`);
|
|
850
|
-
commands.push(`echo "Scheme: ${escapedScheme}"`);
|
|
851
|
-
commands.push('');
|
|
852
|
-
// Output directory
|
|
853
|
-
commands.push(`mkdir -p '${this.escapeBash(outputDir)}'`);
|
|
854
|
-
commands.push(`OUTPUT_DIR="$(cd '${this.escapeBash(outputDir)}' && pwd)"`);
|
|
855
|
-
commands.push(...derivedDataPathLines());
|
|
856
|
-
commands.push('');
|
|
857
|
-
// xcconfig
|
|
858
|
-
if (xcconfigContent.trim()) {
|
|
859
|
-
commands.push('XCCONFIG_FILE="$(mktemp -t cibuild-xcconfig).xcconfig"');
|
|
860
|
-
commands.push(`cat > "$XCCONFIG_FILE" << 'XCCONFIG_EOF'`);
|
|
861
|
-
commands.push(xcconfigContent);
|
|
862
|
-
commands.push('XCCONFIG_EOF');
|
|
863
|
-
commands.push('');
|
|
864
|
-
}
|
|
865
|
-
// Determine project type
|
|
866
|
-
commands.push(`if [[ '${escapedPath}' == *.xcworkspace ]]; then`);
|
|
867
|
-
commands.push(' PROJECT_TYPE="-workspace"');
|
|
868
|
-
commands.push('else');
|
|
869
|
-
commands.push(' PROJECT_TYPE="-project"');
|
|
870
|
-
commands.push('fi');
|
|
871
|
-
commands.push('');
|
|
872
|
-
// Resolve the package graph on its own, ahead of the build, so a refused
|
|
873
|
-
// fetch costs a retry of something that takes seconds rather than a re-run
|
|
874
|
-
// of build-for-testing, which takes minutes. The build that follows reads
|
|
875
|
-
// the same derived data and finds the graph already resolved.
|
|
876
|
-
commands.push('# Resolve package dependencies first — see git-fetch-retry');
|
|
877
|
-
commands.push(...refusedFetchHelpers());
|
|
878
|
-
commands.push(...retryOnRefusedFetch({
|
|
879
|
-
label: 'Package resolution',
|
|
880
|
-
command: 'xcodebuild -resolvePackageDependencies' +
|
|
881
|
-
` "$PROJECT_TYPE" '${escapedPath}'` +
|
|
882
|
-
` -scheme '${escapedScheme}'` +
|
|
883
|
-
' -derivedDataPath "$DERIVED_DATA_PATH"',
|
|
884
|
-
}));
|
|
885
|
-
const pinsArm64 = pinsSimulatorArm64(xcodebuildOptions);
|
|
886
|
-
if (pinsArm64) {
|
|
887
|
-
commands.push(...simulatorArchsLines({
|
|
888
|
-
projectPath: escapedPath,
|
|
889
|
-
scheme: escapedScheme,
|
|
890
|
-
configuration: this.escapeBash(configuration),
|
|
891
|
-
destination: this.escapeBash(destination),
|
|
892
|
-
xcconfig: Boolean(xcconfigContent.trim()),
|
|
893
|
-
}));
|
|
894
|
-
}
|
|
895
|
-
// Build command
|
|
896
|
-
let cmd = 'xcodebuild build-for-testing';
|
|
897
|
-
cmd += ` "$PROJECT_TYPE" '${escapedPath}'`;
|
|
898
|
-
cmd += ` -scheme '${escapedScheme}'`;
|
|
899
|
-
cmd += ` -configuration '${this.escapeBash(configuration)}'`;
|
|
900
|
-
cmd += ` -destination '${this.escapeBash(destination)}'`;
|
|
901
|
-
cmd += ` -derivedDataPath "$DERIVED_DATA_PATH"`;
|
|
902
|
-
if (testPlan) {
|
|
903
|
-
cmd += ` -testPlan '${this.escapeBash(testPlan)}'`;
|
|
904
|
-
}
|
|
905
|
-
if (xcconfigContent.trim()) {
|
|
906
|
-
cmd += ' -xcconfig "$XCCONFIG_FILE"';
|
|
907
|
-
}
|
|
908
|
-
if (xcodebuildOptions) {
|
|
909
|
-
cmd += ` ${pinsArm64 ? withRuntimeSimulatorArchs(xcodebuildOptions) : xcodebuildOptions}`;
|
|
910
|
-
}
|
|
911
|
-
commands.push(cmd);
|
|
912
|
-
commands.push('');
|
|
913
|
-
// Locate .xctestrun file
|
|
914
|
-
commands.push('# Locate generated .xctestrun file');
|
|
915
|
-
commands.push('XCTESTRUN_PATH=$(find "$DERIVED_DATA_PATH/Build/Products" -name "*.xctestrun" | head -1)');
|
|
916
|
-
commands.push('if [ -z "$XCTESTRUN_PATH" ]; then');
|
|
917
|
-
commands.push(' echo "❌ Error: .xctestrun file not found"');
|
|
918
|
-
commands.push(' exit 1');
|
|
919
|
-
commands.push('fi');
|
|
920
|
-
commands.push('');
|
|
921
|
-
commands.push('TEST_BUNDLE_DIR="$(dirname "$XCTESTRUN_PATH")"');
|
|
922
|
-
commands.push('echo "✅ Build for testing complete"');
|
|
923
|
-
commands.push('echo "xctestrun: $XCTESTRUN_PATH"');
|
|
924
|
-
commands.push('');
|
|
925
|
-
// Export env vars
|
|
926
|
-
commands.push('envman add --key CIBUILD_TEST_BUNDLE_PATH --value "$TEST_BUNDLE_DIR"');
|
|
927
|
-
commands.push('envman add --key CIBUILD_XCTESTRUN_FILE_PATH --value "$XCTESTRUN_PATH"');
|
|
928
|
-
if (xcconfigContent.trim()) {
|
|
929
|
-
commands.push('rm -f "$XCCONFIG_FILE"');
|
|
930
|
-
}
|
|
931
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
932
|
-
return this.createScriptStep(script, stepName);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Runs pre-compiled tests from an .xctestrun file.
|
|
937
|
-
*/
|
|
938
|
-
export class XcodeTestWithoutBuildingStepExecutor extends BaseStepExecutor {
|
|
939
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
940
|
-
const requirements = [
|
|
941
|
-
this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required', 'Install Xcode, then run: xcode-select --install'),
|
|
942
|
-
];
|
|
943
|
-
if (inputs.destination === 'auto') {
|
|
944
|
-
requirements.push(this.requireCommand('xcrun', 'xcrun is required to resolve destination: auto via xcrun simctl', 'Install Xcode and run: xcode-select --install'));
|
|
945
|
-
}
|
|
946
|
-
return requirements;
|
|
947
|
-
}
|
|
948
|
-
getOutputs() {
|
|
949
|
-
return [
|
|
950
|
-
{ name: 'CIBUILD_XCRESULT_PATH', type: 'environment', description: 'Path to the .xcresult test result bundle' },
|
|
951
|
-
];
|
|
952
|
-
}
|
|
953
|
-
async execute(inputs, _env, _config) {
|
|
954
|
-
const stepName = 'xcode-test-without-building';
|
|
955
|
-
const xctestrun = this.getInput(inputs, 'xctestrun', '$CIBUILD_XCTESTRUN_FILE_PATH');
|
|
956
|
-
// `auto` triggers runtime resolution via `xcrun simctl`.
|
|
957
|
-
const destination = resolveDestinationInput(this.getInput(inputs, 'destination', 'platform=iOS Simulator,name=iPhone 15,OS=latest'));
|
|
958
|
-
const onlyTesting = this.getInput(inputs, 'only_testing', '');
|
|
959
|
-
const skipTesting = this.getInput(inputs, 'skip_testing', '');
|
|
960
|
-
const repetitionMode = this.getInput(inputs, 'test_repetition_mode', 'none');
|
|
961
|
-
const maxRepetitions = this.getInput(inputs, 'maximum_test_repetitions', '3');
|
|
962
|
-
const xcodebuildOptions = this.getInput(inputs, 'xcodebuild_options', '');
|
|
963
|
-
const commands = [];
|
|
964
|
-
commands.push('# xcode-test-without-building');
|
|
965
|
-
commands.push('echo "Running pre-built tests..."');
|
|
966
|
-
commands.push('');
|
|
967
|
-
// Resolve xctestrun path
|
|
968
|
-
commands.push(`XCTESTRUN_FILE="${xctestrun}"`);
|
|
969
|
-
commands.push('if [ -z "$XCTESTRUN_FILE" ]; then');
|
|
970
|
-
commands.push(' echo "❌ Error: xctestrun file path is empty. Run xcode-build-for-test first."');
|
|
971
|
-
commands.push(' exit 1');
|
|
972
|
-
commands.push('fi');
|
|
973
|
-
commands.push('if [ ! -f "$XCTESTRUN_FILE" ]; then');
|
|
974
|
-
commands.push(' echo "❌ Error: xctestrun file not found: $XCTESTRUN_FILE"');
|
|
975
|
-
commands.push(' exit 1');
|
|
976
|
-
commands.push('fi');
|
|
977
|
-
commands.push('echo "xctestrun: $XCTESTRUN_FILE"');
|
|
978
|
-
commands.push('');
|
|
979
|
-
// Result bundle path
|
|
980
|
-
commands.push('RESULT_BUNDLE_PATH="build/test-results/$(date +%Y%m%d_%H%M%S).xcresult"');
|
|
981
|
-
commands.push('mkdir -p "$(dirname "$RESULT_BUNDLE_PATH")"');
|
|
982
|
-
commands.push('');
|
|
983
|
-
// Build command
|
|
984
|
-
let cmd = 'xcodebuild test-without-building';
|
|
985
|
-
cmd += ' -xctestrun "$XCTESTRUN_FILE"';
|
|
986
|
-
cmd += ` -destination '${this.escapeBash(destination)}'`;
|
|
987
|
-
cmd += ' -resultBundlePath "$RESULT_BUNDLE_PATH"';
|
|
988
|
-
// Test filtering
|
|
989
|
-
if (onlyTesting) {
|
|
990
|
-
for (const test of onlyTesting.split('\n').filter(Boolean)) {
|
|
991
|
-
cmd += ` -only-testing:'${this.escapeBash(test.trim())}'`;
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
if (skipTesting) {
|
|
995
|
-
for (const test of skipTesting.split('\n').filter(Boolean)) {
|
|
996
|
-
cmd += ` -skip-testing:'${this.escapeBash(test.trim())}'`;
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
// Repetition
|
|
1000
|
-
if (repetitionMode && repetitionMode !== 'none') {
|
|
1001
|
-
if (repetitionMode === 'until_failure') {
|
|
1002
|
-
cmd += ' -run-tests-until-failure';
|
|
1003
|
-
}
|
|
1004
|
-
else if (repetitionMode === 'retry_on_failure') {
|
|
1005
|
-
cmd += ' -retry-tests-on-failure';
|
|
1006
|
-
}
|
|
1007
|
-
cmd += ` -test-iterations ${maxRepetitions}`;
|
|
1008
|
-
}
|
|
1009
|
-
if (xcodebuildOptions) {
|
|
1010
|
-
cmd += ` ${xcodebuildOptions}`;
|
|
1011
|
-
}
|
|
1012
|
-
commands.push(cmd);
|
|
1013
|
-
commands.push('');
|
|
1014
|
-
commands.push('echo "✅ Tests completed"');
|
|
1015
|
-
commands.push('echo "Result bundle: $RESULT_BUNDLE_PATH"');
|
|
1016
|
-
commands.push('');
|
|
1017
|
-
// Export
|
|
1018
|
-
commands.push('envman add --key CIBUILD_XCRESULT_PATH --value "$RESULT_BUNDLE_PATH"');
|
|
1019
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
1020
|
-
return this.createScriptStep(script, stepName);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* Builds an iOS/tvOS/watchOS app for the simulator.
|
|
1025
|
-
* Produces a .app bundle with code signing disabled by default.
|
|
1026
|
-
*/
|
|
1027
|
-
export class XcodeBuildForSimulatorStepExecutor extends BaseStepExecutor {
|
|
1028
|
-
getValidationRequirements(inputs, _env, _config) {
|
|
1029
|
-
const requirements = [];
|
|
1030
|
-
requirements.push(this.requireCommand('xcodebuild', 'Xcode Command Line Tools are required', 'Install Xcode, then run: xcode-select --install'));
|
|
1031
|
-
requirements.push(this.requireInput('project_path', inputs, 'Path to .xcodeproj or .xcworkspace'));
|
|
1032
|
-
requirements.push(this.requireInput('scheme', inputs, 'Xcode scheme name'));
|
|
1033
|
-
if (inputs.project_path && !existsSync(inputs.project_path)) {
|
|
1034
|
-
requirements.push(this.runtimeRequirement(inputs.project_path, 'Xcode project/workspace file', 'file', 'the checkout'));
|
|
1035
|
-
}
|
|
1036
|
-
if (inputs.destination === 'auto') {
|
|
1037
|
-
requirements.push(this.requireCommand('xcrun', 'xcrun is required to resolve destination: auto via xcrun simctl', 'Install Xcode and run: xcode-select --install'));
|
|
1038
|
-
}
|
|
1039
|
-
return requirements;
|
|
1040
|
-
}
|
|
1041
|
-
getOutputs() {
|
|
1042
|
-
return [
|
|
1043
|
-
{ name: 'CIBUILD_APP_DIR_PATH', type: 'environment', description: 'Path to the generated .app directory' },
|
|
1044
|
-
];
|
|
1045
|
-
}
|
|
1046
|
-
async execute(inputs, _env, _config) {
|
|
1047
|
-
const stepName = 'xcode-build-for-simulator';
|
|
1048
|
-
const projectPath = this.getRequiredInput(inputs, 'project_path', stepName);
|
|
1049
|
-
const scheme = this.getRequiredInput(inputs, 'scheme', stepName);
|
|
1050
|
-
const configuration = this.getInput(inputs, 'configuration', '');
|
|
1051
|
-
// `auto` triggers runtime resolution via `xcrun simctl`.
|
|
1052
|
-
const destination = resolveDestinationInput(this.getInput(inputs, 'destination', 'generic/platform=iOS Simulator'));
|
|
1053
|
-
const performClean = this.getInput(inputs, 'perform_clean_action', 'no');
|
|
1054
|
-
const xcconfigContent = this.getInput(inputs, 'xcconfig_content', 'CODE_SIGNING_ALLOWED=NO');
|
|
1055
|
-
const xcodebuildOptions = this.getInput(inputs, 'xcodebuild_options', '');
|
|
1056
|
-
const outputDir = this.getInput(inputs, 'output_dir', 'build');
|
|
1057
|
-
const escapedPath = this.escapeBash(projectPath);
|
|
1058
|
-
const escapedScheme = this.escapeBash(scheme);
|
|
1059
|
-
const commands = [];
|
|
1060
|
-
commands.push('# xcode-build-for-simulator');
|
|
1061
|
-
commands.push(`echo "Building for simulator: ${escapedPath}"`);
|
|
1062
|
-
commands.push(`echo "Scheme: ${escapedScheme}"`);
|
|
1063
|
-
commands.push('');
|
|
1064
|
-
// Output dir
|
|
1065
|
-
commands.push(`mkdir -p '${this.escapeBash(outputDir)}'`);
|
|
1066
|
-
commands.push(`OUTPUT_DIR="$(cd '${this.escapeBash(outputDir)}' && pwd)"`);
|
|
1067
|
-
commands.push(...derivedDataPathLines());
|
|
1068
|
-
commands.push('');
|
|
1069
|
-
// xcconfig
|
|
1070
|
-
commands.push('XCCONFIG_FILE="$(mktemp -t cibuild-xcconfig).xcconfig"');
|
|
1071
|
-
if (xcconfigContent.trim()) {
|
|
1072
|
-
commands.push(`cat > "$XCCONFIG_FILE" << 'XCCONFIG_EOF'`);
|
|
1073
|
-
commands.push(xcconfigContent);
|
|
1074
|
-
commands.push('XCCONFIG_EOF');
|
|
1075
|
-
}
|
|
1076
|
-
else {
|
|
1077
|
-
commands.push('echo "CODE_SIGNING_ALLOWED=NO" > "$XCCONFIG_FILE"');
|
|
1078
|
-
}
|
|
1079
|
-
commands.push('');
|
|
1080
|
-
// Project type
|
|
1081
|
-
commands.push(`if [[ '${escapedPath}' == *.xcworkspace ]]; then`);
|
|
1082
|
-
commands.push(' PROJECT_TYPE="-workspace"');
|
|
1083
|
-
commands.push('else');
|
|
1084
|
-
commands.push(' PROJECT_TYPE="-project"');
|
|
1085
|
-
commands.push('fi');
|
|
1086
|
-
commands.push('');
|
|
1087
|
-
// Resolve the package graph before building, under the refused-fetch
|
|
1088
|
-
// retry. This is the step an iOS app is normally built with, so it is the
|
|
1089
|
-
// one a refused dependency fetch lands on first. The build below reads the
|
|
1090
|
-
// same derived data and finds the graph already resolved — the two
|
|
1091
|
-
// -derivedDataPath arguments have to move together, or the resolve
|
|
1092
|
-
// populates one location and the build re-resolves into another, which
|
|
1093
|
-
// silently spends the retry on work the build then throws away.
|
|
1094
|
-
commands.push('# Resolve package dependencies first — see git-fetch-retry');
|
|
1095
|
-
commands.push(...refusedFetchHelpers());
|
|
1096
|
-
commands.push(...retryOnRefusedFetch({
|
|
1097
|
-
label: 'Package resolution',
|
|
1098
|
-
command: 'xcodebuild -resolvePackageDependencies' +
|
|
1099
|
-
` "$PROJECT_TYPE" '${escapedPath}'` +
|
|
1100
|
-
` -scheme '${escapedScheme}'` +
|
|
1101
|
-
' -derivedDataPath "$DERIVED_DATA_PATH"',
|
|
1102
|
-
}));
|
|
1103
|
-
const pinsArm64 = pinsSimulatorArm64(xcodebuildOptions);
|
|
1104
|
-
if (pinsArm64) {
|
|
1105
|
-
commands.push(...simulatorArchsLines({
|
|
1106
|
-
projectPath: escapedPath,
|
|
1107
|
-
scheme: escapedScheme,
|
|
1108
|
-
configuration: configuration ? this.escapeBash(configuration) : '',
|
|
1109
|
-
destination: this.escapeBash(destination),
|
|
1110
|
-
xcconfig: true,
|
|
1111
|
-
}));
|
|
1112
|
-
}
|
|
1113
|
-
// Build command
|
|
1114
|
-
let cmd = 'xcodebuild';
|
|
1115
|
-
if (performClean === 'yes') {
|
|
1116
|
-
cmd += ' clean';
|
|
1117
|
-
}
|
|
1118
|
-
cmd += ' build';
|
|
1119
|
-
cmd += ` "$PROJECT_TYPE" '${escapedPath}'`;
|
|
1120
|
-
cmd += ` -scheme '${escapedScheme}'`;
|
|
1121
|
-
if (configuration) {
|
|
1122
|
-
cmd += ` -configuration '${this.escapeBash(configuration)}'`;
|
|
1123
|
-
}
|
|
1124
|
-
cmd += ` -destination '${this.escapeBash(destination)}'`;
|
|
1125
|
-
// -derivedDataPath rather than CONFIGURATION_BUILD_DIR. The latter was set
|
|
1126
|
-
// to keep xcodebuild from writing a `build` directory inside each SPM
|
|
1127
|
-
// package checkout in DerivedData ("File exists but is not a directory" on
|
|
1128
|
-
// repeated runs) — but note what that fix actually was: making the path
|
|
1129
|
-
// ABSOLUTE. Pointing derived data somewhere writable achieves the same
|
|
1130
|
-
// thing without the cost below, and matches xcode-build-for-test.
|
|
1131
|
-
//
|
|
1132
|
-
// The cost, measured 2026-09-09 on four of the six iOS corpus rows:
|
|
1133
|
-
// CONFIGURATION_BUILD_DIR overrides the per-configuration/per-platform
|
|
1134
|
-
// product separation xcodebuild normally provides under
|
|
1135
|
-
// DerivedData/Build/Products/<config>-<platform>/, flattening every target
|
|
1136
|
-
// in the graph into ONE directory. Two build tasks for the same target then
|
|
1137
|
-
// declare the same absolute output path and xcodebuild refuses the whole
|
|
1138
|
-
// build with "Multiple commands produce". Read the notes it prints: they
|
|
1139
|
-
// name the SAME target twice. It is not two packages colliding on a name,
|
|
1140
|
-
// it is one target built twice into one directory. 16 collisions on
|
|
1141
|
-
// jellyfin/Swiftfin, 266 on Automattic/pocket-casts-ios, 425 on
|
|
1142
|
-
// home-assistant/iOS.
|
|
1143
|
-
//
|
|
1144
|
-
// It also breaks CocoaPods, which is the same defect wearing a disguise.
|
|
1145
|
-
// CocoaPods derives PODS_CONFIGURATION_BUILD_DIR from BUILD_DIR, and
|
|
1146
|
-
// BUILD_DIR is NOT what CONFIGURATION_BUILD_DIR overrides — so pod products
|
|
1147
|
-
// are written to the flat directory while every generated copy-resources
|
|
1148
|
-
// script and linker search path still points into DerivedData. On
|
|
1149
|
-
// shenruisi/Stay that surfaced as "Resource SDWebImage.bundle not found.
|
|
1150
|
-
// Run pod install", for a bundle that had in fact been built, two
|
|
1151
|
-
// directories away.
|
|
1152
|
-
//
|
|
1153
|
-
// Proved by control, same repo and image and flags, only this line
|
|
1154
|
-
// differing: CONFIGURATION_BUILD_DIR reproduced the collision and exit 65
|
|
1155
|
-
// with no .app; -derivedDataPath reached BUILD SUCCEEDED in 2m06s and left
|
|
1156
|
-
// both Debug and Debug-iphonesimulator under Products — the separation the
|
|
1157
|
-
// flat directory destroys.
|
|
1158
|
-
cmd += ` -derivedDataPath "$DERIVED_DATA_PATH"`;
|
|
1159
|
-
cmd += ' -xcconfig "$XCCONFIG_FILE"';
|
|
1160
|
-
if (xcodebuildOptions) {
|
|
1161
|
-
cmd += ` ${pinsArm64 ? withRuntimeSimulatorArchs(xcodebuildOptions) : xcodebuildOptions}`;
|
|
1162
|
-
}
|
|
1163
|
-
commands.push(cmd);
|
|
1164
|
-
commands.push('');
|
|
1165
|
-
// Locate .app. It now lands under the per-platform Products directory
|
|
1166
|
-
// rather than flat in $OUTPUT_DIR, so the guard has to follow it — at
|
|
1167
|
-
// maxdepth 1 this found nothing and the step would fail every build with
|
|
1168
|
-
// ".app not found" while the app had compiled perfectly.
|
|
1169
|
-
//
|
|
1170
|
-
// Following the products is not enough on its own, though, because a scheme
|
|
1171
|
-
// that builds a watchOS or tvOS companion produces more than one bundle and
|
|
1172
|
-
// a scan has no way to tell them apart. Ask the scheme, which knows: see
|
|
1173
|
-
// xcode-app-product.ts. The scan below is what runs when the scheme cannot
|
|
1174
|
-
// answer — a project with no shared scheme is ordinary and builds fine, so
|
|
1175
|
-
// this resolves the same bundle it always did there.
|
|
1176
|
-
commands.push('# Locate generated .app — the one this scheme builds');
|
|
1177
|
-
commands.push('PRODUCTS_DIR="$DERIVED_DATA_PATH/Build/Products"');
|
|
1178
|
-
commands.push('APP_RESOLVER="$(mktemp -t cibuild-app-product).cjs"');
|
|
1179
|
-
commands.push(`cat > "$APP_RESOLVER" << 'CIBUILD_APP_PRODUCT_EOF'`);
|
|
1180
|
-
commands.push(APP_PRODUCT_RESOLVER_SOURCE.trim());
|
|
1181
|
-
commands.push('CIBUILD_APP_PRODUCT_EOF');
|
|
1182
|
-
commands.push('APP_PATH=$(node "$APP_RESOLVER" "$PRODUCTS_DIR"' +
|
|
1183
|
-
` '${escapedPath}' '${escapedScheme}' '${this.escapeBash(destination)}' 2>/dev/null || true)`);
|
|
1184
|
-
commands.push('rm -f "$APP_RESOLVER"');
|
|
1185
|
-
commands.push('if [ -z "$APP_PATH" ]; then');
|
|
1186
|
-
commands.push(' APP_PATH=$(find "$PRODUCTS_DIR" -maxdepth 2 -name "*.app" -type d 2>/dev/null | head -1)');
|
|
1187
|
-
commands.push('fi');
|
|
1188
|
-
commands.push('if [ -z "$APP_PATH" ]; then');
|
|
1189
|
-
commands.push(' echo "❌ Error: .app not found under $PRODUCTS_DIR"');
|
|
1190
|
-
commands.push(' exit 1');
|
|
1191
|
-
commands.push('fi');
|
|
1192
|
-
commands.push('');
|
|
1193
|
-
commands.push('echo "✅ Simulator build complete"');
|
|
1194
|
-
commands.push('echo "App: $APP_PATH"');
|
|
1195
|
-
commands.push('');
|
|
1196
|
-
// Export
|
|
1197
|
-
commands.push('envman add --key CIBUILD_APP_DIR_PATH --value "$APP_PATH"');
|
|
1198
|
-
commands.push('');
|
|
1199
|
-
// Deliver it, the way gradle-build delivers its .apk.
|
|
1200
|
-
//
|
|
1201
|
-
// gradle-build copies its own output into the artifacts directory; this
|
|
1202
|
-
// step located the bundle, exported the path and left it in DerivedData —
|
|
1203
|
-
// which the runner excludes from artifact discovery. So the app was
|
|
1204
|
-
// findable and deliberately not found, and an iOS proof could go green
|
|
1205
|
-
// handing the user nothing to install. Pipelines that added a collecting
|
|
1206
|
-
// step of their own delivered; every other one did not, which made
|
|
1207
|
-
// delivery a property of the pipeline rather than of the build.
|
|
1208
|
-
//
|
|
1209
|
-
// It zips rather than copying the bundle in, and that is not cosmetic. A
|
|
1210
|
-
// .app is a directory: staged as a directory it becomes one artifact per
|
|
1211
|
-
// file inside it — every asset, nib and framework separately — and an
|
|
1212
|
-
// internal symlink pointing outside the workspace fails the stage
|
|
1213
|
-
// outright. A .zip is a plain file, stages as exactly one artifact, and is
|
|
1214
|
-
// what someone downloading it wants anyway. ditto is the macOS-native
|
|
1215
|
-
// archiver and preserves the bundle's permissions and symlinks, which a
|
|
1216
|
-
// plain recursive zip does not do faithfully.
|
|
1217
|
-
commands.push('# Deliver the app as one artifact');
|
|
1218
|
-
commands.push('ARTIFACTS_DIR="${CIBUILD_ARTIFACTS_DIR:-.ci/artifacts}"');
|
|
1219
|
-
commands.push('mkdir -p "$ARTIFACTS_DIR"');
|
|
1220
|
-
commands.push('APP_NAME="$(basename "$APP_PATH")"');
|
|
1221
|
-
commands.push('rm -f "$ARTIFACTS_DIR/$APP_NAME.zip"');
|
|
1222
|
-
commands.push('ditto -c -k --keepParent "$APP_PATH" "$ARTIFACTS_DIR/$APP_NAME.zip"');
|
|
1223
|
-
commands.push('echo " → Collected: $APP_NAME.zip"');
|
|
1224
|
-
commands.push('echo "✅ Artifacts copied to: $ARTIFACTS_DIR"');
|
|
1225
|
-
commands.push('ls -lh "$ARTIFACTS_DIR"');
|
|
1226
|
-
commands.push('');
|
|
1227
|
-
commands.push('rm -f "$XCCONFIG_FILE"');
|
|
1228
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
1229
|
-
return this.createScriptStep(script, stepName);
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Exports an IPA from an existing .xcarchive using xcodebuild -exportArchive.
|
|
1234
|
-
*/
|
|
1235
|
-
export class ExportXcarchiveStepExecutor extends BaseStepExecutor {
|
|
1236
|
-
getValidationRequirements(_inputs, _env, _config) {
|
|
1237
|
-
return [
|
|
1238
|
-
this.requireCommand('xcodebuild', 'Xcode is required for archive export'),
|
|
1239
|
-
];
|
|
1240
|
-
}
|
|
1241
|
-
getOutputs() {
|
|
1242
|
-
return [
|
|
1243
|
-
{ name: 'CIBUILD_IPA_PATH', type: 'environment', description: 'Path to the exported IPA file' },
|
|
1244
|
-
{ name: 'CIBUILD_DSYM_PATH', type: 'environment', description: 'Path to the zipped dSYM files' },
|
|
1245
|
-
];
|
|
1246
|
-
}
|
|
1247
|
-
async execute(inputs, _env, _config) {
|
|
1248
|
-
const stepName = 'export-xcarchive';
|
|
1249
|
-
const archivePath = this.getRequiredInput(inputs, 'archive_path', stepName);
|
|
1250
|
-
const product = this.getInput(inputs, 'product', 'app');
|
|
1251
|
-
const distributionMethod = this.getInput(inputs, 'distribution_method', 'development');
|
|
1252
|
-
const compileBitcode = this.getInput(inputs, 'compile_bitcode', 'yes') === 'yes';
|
|
1253
|
-
const uploadBitcode = this.getInput(inputs, 'upload_bitcode', 'yes') === 'yes';
|
|
1254
|
-
const exportOptionsPlistContent = this.getInput(inputs, 'export_options_plist_content', '');
|
|
1255
|
-
const verbose = this.getInput(inputs, 'verbose_log', 'no') === 'yes';
|
|
1256
|
-
const commands = [];
|
|
1257
|
-
commands.push('# export-xcarchive — export IPA from .xcarchive');
|
|
1258
|
-
commands.push('echo "📦 Exporting IPA from xcarchive..."');
|
|
1259
|
-
commands.push('');
|
|
1260
|
-
commands.push(`ARCHIVE_PATH='${this.escapeBash(archivePath)}'`);
|
|
1261
|
-
commands.push('');
|
|
1262
|
-
// Validate archive exists
|
|
1263
|
-
commands.push('if [ ! -d "$ARCHIVE_PATH" ]; then');
|
|
1264
|
-
commands.push(' echo "❌ Error: Archive not found: $ARCHIVE_PATH"');
|
|
1265
|
-
commands.push(' exit 1');
|
|
1266
|
-
commands.push('fi');
|
|
1267
|
-
commands.push('');
|
|
1268
|
-
// Set up export directory
|
|
1269
|
-
commands.push('EXPORT_DIR="${CIBUILD_DEPLOY_DIR:-./artifacts}/export"');
|
|
1270
|
-
commands.push('mkdir -p "$EXPORT_DIR"');
|
|
1271
|
-
commands.push('');
|
|
1272
|
-
// Generate or use provided ExportOptions.plist
|
|
1273
|
-
commands.push('EXPORT_OPTIONS="$EXPORT_DIR/ExportOptions.plist"');
|
|
1274
|
-
if (exportOptionsPlistContent) {
|
|
1275
|
-
commands.push('cat > "$EXPORT_OPTIONS" << \'PLIST_EOF\'');
|
|
1276
|
-
commands.push(exportOptionsPlistContent);
|
|
1277
|
-
commands.push('PLIST_EOF');
|
|
1278
|
-
}
|
|
1279
|
-
else {
|
|
1280
|
-
// Map distribution method for Xcode 15.3+
|
|
1281
|
-
commands.push('# Determine distribution method');
|
|
1282
|
-
commands.push(`METHOD='${this.escapeBash(distributionMethod)}'`);
|
|
1283
|
-
commands.push('XCODE_VERSION=$(xcodebuild -version | head -1 | grep -oE "[0-9]+\\.[0-9]+" || echo "0.0")');
|
|
1284
|
-
commands.push('XCODE_MAJOR=$(echo "$XCODE_VERSION" | cut -d. -f1)');
|
|
1285
|
-
commands.push('XCODE_MINOR=$(echo "$XCODE_VERSION" | cut -d. -f2)');
|
|
1286
|
-
commands.push('if [ "$XCODE_MAJOR" -ge 16 ] || ([ "$XCODE_MAJOR" -eq 15 ] && [ "$XCODE_MINOR" -ge 3 ]); then');
|
|
1287
|
-
commands.push(' case "$METHOD" in');
|
|
1288
|
-
commands.push(' development) METHOD="debugging" ;;');
|
|
1289
|
-
commands.push(' app-store) METHOD="app-store-connect" ;;');
|
|
1290
|
-
commands.push(' ad-hoc) METHOD="release-testing" ;;');
|
|
1291
|
-
commands.push(' esac');
|
|
1292
|
-
commands.push('fi');
|
|
1293
|
-
commands.push('');
|
|
1294
|
-
commands.push('cat > "$EXPORT_OPTIONS" << PLIST_EOF');
|
|
1295
|
-
commands.push('<?xml version="1.0" encoding="UTF-8"?>');
|
|
1296
|
-
commands.push('<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">');
|
|
1297
|
-
commands.push('<plist version="1.0">');
|
|
1298
|
-
commands.push('<dict>');
|
|
1299
|
-
commands.push(' <key>method</key>');
|
|
1300
|
-
commands.push(' <string>$METHOD</string>');
|
|
1301
|
-
commands.push(` <key>compileBitcode</key>`);
|
|
1302
|
-
commands.push(` <${compileBitcode}/>`);
|
|
1303
|
-
commands.push(` <key>uploadBitcode</key>`);
|
|
1304
|
-
commands.push(` <${uploadBitcode}/>`);
|
|
1305
|
-
if (product === 'app-clip') {
|
|
1306
|
-
commands.push(' <key>exportForThinning</key>');
|
|
1307
|
-
commands.push(' <string>app-clip</string>');
|
|
1308
|
-
}
|
|
1309
|
-
commands.push('</dict>');
|
|
1310
|
-
commands.push('</plist>');
|
|
1311
|
-
commands.push('PLIST_EOF');
|
|
1312
|
-
}
|
|
1313
|
-
commands.push('');
|
|
1314
|
-
// Run xcodebuild -exportArchive
|
|
1315
|
-
const verboseFlag = verbose ? '' : ' -quiet';
|
|
1316
|
-
commands.push('echo "Exporting with method: $METHOD"');
|
|
1317
|
-
commands.push(`xcodebuild -exportArchive \\`);
|
|
1318
|
-
commands.push(` -archivePath "$ARCHIVE_PATH" \\`);
|
|
1319
|
-
commands.push(` -exportPath "$EXPORT_DIR" \\`);
|
|
1320
|
-
commands.push(` -exportOptionsPlist "$EXPORT_OPTIONS"${verboseFlag}`);
|
|
1321
|
-
commands.push('');
|
|
1322
|
-
// Find IPA
|
|
1323
|
-
commands.push('# Find exported IPA');
|
|
1324
|
-
commands.push('IPA_PATH=$(find "$EXPORT_DIR" -name "*.ipa" | head -1)');
|
|
1325
|
-
commands.push('if [ -z "$IPA_PATH" ]; then');
|
|
1326
|
-
commands.push(' echo "❌ Error: No IPA found in export directory"');
|
|
1327
|
-
commands.push(' exit 1');
|
|
1328
|
-
commands.push('fi');
|
|
1329
|
-
commands.push('echo "✅ Exported IPA: $IPA_PATH"');
|
|
1330
|
-
commands.push('');
|
|
1331
|
-
// Collect dSYMs
|
|
1332
|
-
commands.push('# Collect dSYMs');
|
|
1333
|
-
commands.push('DSYM_DIR="$ARCHIVE_PATH/dSYMs"');
|
|
1334
|
-
commands.push('DSYM_ZIP=""');
|
|
1335
|
-
commands.push('if [ -d "$DSYM_DIR" ] && [ "$(ls -A "$DSYM_DIR" 2>/dev/null)" ]; then');
|
|
1336
|
-
commands.push(' DSYM_ZIP="$EXPORT_DIR/dSYMs.zip"');
|
|
1337
|
-
commands.push(' cd "$DSYM_DIR" && zip -r "$DSYM_ZIP" . && cd -');
|
|
1338
|
-
commands.push(' echo "dSYMs: $DSYM_ZIP"');
|
|
1339
|
-
commands.push('fi');
|
|
1340
|
-
commands.push('');
|
|
1341
|
-
// Export env vars
|
|
1342
|
-
commands.push('envman add --key CIBUILD_IPA_PATH --value "$IPA_PATH"');
|
|
1343
|
-
commands.push('if [ -n "$DSYM_ZIP" ]; then');
|
|
1344
|
-
commands.push(' envman add --key CIBUILD_DSYM_PATH --value "$DSYM_ZIP"');
|
|
1345
|
-
commands.push('fi');
|
|
1346
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
1347
|
-
return this.createScriptStep(script, stepName);
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
//# sourceMappingURL=xcode.js.map
|