@invarn/cibuild 2.9.6 → 2.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-35SI42CD.mjs +1 -0
- package/dist/chunk-IC45KVLJ.mjs +1 -0
- package/dist/chunk-XDY4V23V.mjs +1 -0
- package/dist/cli.cjs +14 -14
- package/dist/commands.mjs +1 -0
- package/dist/lib.mjs +3934 -0
- package/package.json +6 -10
- package/dist/src/cli.d.ts +0 -3
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -399
- package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts +0 -38
- package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.d.ts.map +0 -1
- package/dist/src/commands/a-credential-target-follows-the-flavor-source-set.test.js +0 -222
- package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts +0 -41
- package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.d.ts.map +0 -1
- package/dist/src/commands/a-credential-two-workflows-cannot-share-is-declared-per-workflow.test.js +0 -324
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts +0 -2
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.d.ts.map +0 -1
- package/dist/src/commands/a-properties-stand-in-declares-the-keys-the-build-reads.test.js +0 -382
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts +0 -29
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.d.ts.map +0 -1
- package/dist/src/commands/an-already-stored-value-does-not-answer-the-target-question.test.js +0 -160
- package/dist/src/commands/android-gms-guard.test.d.ts +0 -9
- package/dist/src/commands/android-gms-guard.test.d.ts.map +0 -1
- package/dist/src/commands/android-gms-guard.test.js +0 -78
- package/dist/src/commands/android-java-version.test.d.ts +0 -12
- package/dist/src/commands/android-java-version.test.d.ts.map +0 -1
- package/dist/src/commands/android-java-version.test.js +0 -209
- package/dist/src/commands/android-scanner-java.test.d.ts +0 -2
- package/dist/src/commands/android-scanner-java.test.d.ts.map +0 -1
- package/dist/src/commands/android-scanner-java.test.js +0 -130
- package/dist/src/commands/android-scanner.d.ts +0 -250
- package/dist/src/commands/android-scanner.d.ts.map +0 -1
- package/dist/src/commands/android-scanner.js +0 -1639
- package/dist/src/commands/build.d.ts +0 -215
- package/dist/src/commands/build.d.ts.map +0 -1
- package/dist/src/commands/build.js +0 -1433
- package/dist/src/commands/build.test.d.ts +0 -13
- package/dist/src/commands/build.test.d.ts.map +0 -1
- package/dist/src/commands/build.test.js +0 -121
- package/dist/src/commands/detect-platform.d.ts +0 -13
- package/dist/src/commands/detect-platform.d.ts.map +0 -1
- package/dist/src/commands/detect-platform.js +0 -51
- package/dist/src/commands/detect-project.d.ts +0 -7
- package/dist/src/commands/detect-project.d.ts.map +0 -1
- package/dist/src/commands/detect-project.js +0 -12
- package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts +0 -26
- package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.d.ts.map +0 -1
- package/dist/src/commands/each-flavor-is-asked-for-its-own-properties-file.test.js +0 -196
- package/dist/src/commands/edit.d.ts +0 -12
- package/dist/src/commands/edit.d.ts.map +0 -1
- package/dist/src/commands/edit.js +0 -652
- package/dist/src/commands/edit.test.d.ts +0 -6
- package/dist/src/commands/edit.test.d.ts.map +0 -1
- package/dist/src/commands/edit.test.js +0 -45
- package/dist/src/commands/fidelity-trim.d.ts +0 -13
- package/dist/src/commands/fidelity-trim.d.ts.map +0 -1
- package/dist/src/commands/fidelity-trim.js +0 -21
- package/dist/src/commands/fidelity-trim.test.d.ts +0 -8
- package/dist/src/commands/fidelity-trim.test.d.ts.map +0 -1
- package/dist/src/commands/fidelity-trim.test.js +0 -79
- package/dist/src/commands/file-secret-collector.d.ts +0 -50
- package/dist/src/commands/file-secret-collector.d.ts.map +0 -1
- package/dist/src/commands/file-secret-collector.js +0 -217
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts +0 -38
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.d.ts.map +0 -1
- package/dist/src/commands/flavors-a-convention-plugin-creates-are-still-found.test.js +0 -300
- package/dist/src/commands/github-workflow.d.ts +0 -5
- package/dist/src/commands/github-workflow.d.ts.map +0 -1
- package/dist/src/commands/github-workflow.js +0 -45
- package/dist/src/commands/index.d.ts +0 -33
- package/dist/src/commands/index.d.ts.map +0 -1
- package/dist/src/commands/index.js +0 -33
- package/dist/src/commands/init.d.ts +0 -20
- package/dist/src/commands/init.d.ts.map +0 -1
- package/dist/src/commands/init.js +0 -264
- package/dist/src/commands/ios-scanner.d.ts +0 -104
- package/dist/src/commands/ios-scanner.d.ts.map +0 -1
- package/dist/src/commands/ios-scanner.js +0 -492
- package/dist/src/commands/ios-scheme-ranking.test.d.ts +0 -2
- package/dist/src/commands/ios-scheme-ranking.test.d.ts.map +0 -1
- package/dist/src/commands/ios-scheme-ranking.test.js +0 -264
- package/dist/src/commands/local-properties-reaches-every-workflow.test.d.ts +0 -30
- package/dist/src/commands/local-properties-reaches-every-workflow.test.d.ts.map +0 -1
- package/dist/src/commands/local-properties-reaches-every-workflow.test.js +0 -146
- package/dist/src/commands/reset.d.ts +0 -7
- package/dist/src/commands/reset.d.ts.map +0 -1
- package/dist/src/commands/reset.js +0 -81
- package/dist/src/commands/run.d.ts +0 -22
- package/dist/src/commands/run.d.ts.map +0 -1
- package/dist/src/commands/run.js +0 -141
- package/dist/src/commands/secrets-sync-workflow.d.ts +0 -15
- package/dist/src/commands/secrets-sync-workflow.d.ts.map +0 -1
- package/dist/src/commands/secrets-sync-workflow.js +0 -255
- package/dist/src/commands/secrets-upload.d.ts +0 -21
- package/dist/src/commands/secrets-upload.d.ts.map +0 -1
- package/dist/src/commands/secrets-upload.js +0 -177
- package/dist/src/commands/secrets-upload.test.d.ts +0 -5
- package/dist/src/commands/secrets-upload.test.d.ts.map +0 -1
- package/dist/src/commands/secrets-upload.test.js +0 -60
- package/dist/src/commands/validate.d.ts +0 -10
- package/dist/src/commands/validate.d.ts.map +0 -1
- package/dist/src/commands/validate.js +0 -46
- package/dist/src/config.d.ts +0 -3
- package/dist/src/config.d.ts.map +0 -1
- package/dist/src/config.js +0 -47
- package/dist/src/envman/cli.d.ts +0 -21
- package/dist/src/envman/cli.d.ts.map +0 -1
- package/dist/src/envman/cli.js +0 -240
- package/dist/src/envman/envman.d.ts +0 -83
- package/dist/src/envman/envman.d.ts.map +0 -1
- package/dist/src/envman/envman.js +0 -361
- package/dist/src/envman/envman.test.d.ts +0 -5
- package/dist/src/envman/envman.test.d.ts.map +0 -1
- package/dist/src/envman/envman.test.js +0 -236
- package/dist/src/envman/envstore-round-trip.test.d.ts +0 -27
- package/dist/src/envman/envstore-round-trip.test.d.ts.map +0 -1
- package/dist/src/envman/envstore-round-trip.test.js +0 -130
- package/dist/src/envman/index.d.ts +0 -23
- package/dist/src/envman/index.d.ts.map +0 -1
- package/dist/src/envman/index.js +0 -23
- package/dist/src/envman/shim.d.ts +0 -16
- package/dist/src/envman/shim.d.ts.map +0 -1
- package/dist/src/envman/shim.js +0 -66
- package/dist/src/envman/types.d.ts +0 -55
- package/dist/src/envman/types.d.ts.map +0 -1
- package/dist/src/envman/types.js +0 -12
- package/dist/src/lib.d.ts +0 -33
- package/dist/src/lib.d.ts.map +0 -1
- package/dist/src/lib.js +0 -37
- package/dist/src/pipeline.d.ts +0 -3
- package/dist/src/pipeline.d.ts.map +0 -1
- package/dist/src/pipeline.js +0 -57
- package/dist/src/runner-node-step.test.d.ts +0 -12
- package/dist/src/runner-node-step.test.d.ts.map +0 -1
- package/dist/src/runner-node-step.test.js +0 -61
- package/dist/src/runner.d.ts +0 -31
- package/dist/src/runner.d.ts.map +0 -1
- package/dist/src/runner.js +0 -240
- package/dist/src/shared/detect-project.d.ts +0 -57
- package/dist/src/shared/detect-project.d.ts.map +0 -1
- package/dist/src/shared/detect-project.js +0 -219
- package/dist/src/shared/detect-project.test.d.ts +0 -2
- package/dist/src/shared/detect-project.test.d.ts.map +0 -1
- package/dist/src/shared/detect-project.test.js +0 -161
- package/dist/src/shared/git.d.ts +0 -25
- package/dist/src/shared/git.d.ts.map +0 -1
- package/dist/src/shared/git.js +0 -53
- package/dist/src/shared/git.test.d.ts +0 -9
- package/dist/src/shared/git.test.d.ts.map +0 -1
- package/dist/src/shared/git.test.js +0 -66
- package/dist/src/shared/gitignore.d.ts +0 -6
- package/dist/src/shared/gitignore.d.ts.map +0 -1
- package/dist/src/shared/gitignore.js +0 -26
- package/dist/src/shared/gradle-settings.d.ts +0 -49
- package/dist/src/shared/gradle-settings.d.ts.map +0 -1
- package/dist/src/shared/gradle-settings.js +0 -138
- package/dist/src/shared/gradle-settings.test.d.ts +0 -2
- package/dist/src/shared/gradle-settings.test.d.ts.map +0 -1
- package/dist/src/shared/gradle-settings.test.js +0 -65
- package/dist/src/shared/prompts.d.ts +0 -30
- package/dist/src/shared/prompts.d.ts.map +0 -1
- package/dist/src/shared/prompts.js +0 -121
- package/dist/src/shared/xcode-container.test.d.ts +0 -2
- package/dist/src/shared/xcode-container.test.d.ts.map +0 -1
- package/dist/src/shared/xcode-container.test.js +0 -123
- package/dist/src/step-marker.d.ts +0 -12
- package/dist/src/step-marker.d.ts.map +0 -1
- package/dist/src/step-marker.js +0 -29
- package/dist/src/step-marker.test.d.ts +0 -11
- package/dist/src/step-marker.test.d.ts.map +0 -1
- package/dist/src/step-marker.test.js +0 -45
- package/dist/src/types.d.ts +0 -58
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js +0 -2
- package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts +0 -28
- package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.d.ts.map +0 -1
- package/dist/src/yaml/a-cache-path-must-not-swallow-a-credential.test.js +0 -223
- package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.d.ts +0 -20
- package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.d.ts.map +0 -1
- package/dist/src/yaml/a-declared-secret-reaches-the-file-step.test.js +0 -201
- package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts +0 -37
- package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.d.ts.map +0 -1
- package/dist/src/yaml/a-declared-slot-may-name-the-workflow-it-belongs-to.test.js +0 -255
- package/dist/src/yaml/a-missing-credential-is-loud.test.d.ts +0 -32
- package/dist/src/yaml/a-missing-credential-is-loud.test.d.ts.map +0 -1
- package/dist/src/yaml/a-missing-credential-is-loud.test.js +0 -301
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts +0 -18
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.d.ts.map +0 -1
- package/dist/src/yaml/a-runtime-requirement-names-a-real-provider.test.js +0 -66
- package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts +0 -34
- package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.d.ts.map +0 -1
- package/dist/src/yaml/a-scoped-slot-must-name-a-workflow-the-pipeline-has.test.js +0 -218
- package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts +0 -27
- package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.d.ts.map +0 -1
- package/dist/src/yaml/a-value-this-instance-stored-is-a-value-it-has.test.js +0 -126
- package/dist/src/yaml/bitrise-compat.d.ts +0 -65
- package/dist/src/yaml/bitrise-compat.d.ts.map +0 -1
- package/dist/src/yaml/bitrise-compat.js +0 -206
- package/dist/src/yaml/bitrise-compat.test.d.ts +0 -5
- package/dist/src/yaml/bitrise-compat.test.d.ts.map +0 -1
- package/dist/src/yaml/bitrise-compat.test.js +0 -347
- package/dist/src/yaml/cache-key-missing-file.test.d.ts +0 -12
- package/dist/src/yaml/cache-key-missing-file.test.d.ts.map +0 -1
- package/dist/src/yaml/cache-key-missing-file.test.js +0 -116
- package/dist/src/yaml/converter.d.ts +0 -103
- package/dist/src/yaml/converter.d.ts.map +0 -1
- package/dist/src/yaml/converter.js +0 -420
- package/dist/src/yaml/converter.test.d.ts +0 -5
- package/dist/src/yaml/converter.test.d.ts.map +0 -1
- package/dist/src/yaml/converter.test.js +0 -548
- package/dist/src/yaml/e2e.test.d.ts +0 -6
- package/dist/src/yaml/e2e.test.d.ts.map +0 -1
- package/dist/src/yaml/e2e.test.js +0 -446
- package/dist/src/yaml/env-resolver.d.ts +0 -218
- package/dist/src/yaml/env-resolver.d.ts.map +0 -1
- package/dist/src/yaml/env-resolver.js +0 -591
- package/dist/src/yaml/env-resolver.test.d.ts +0 -5
- package/dist/src/yaml/env-resolver.test.d.ts.map +0 -1
- package/dist/src/yaml/env-resolver.test.js +0 -502
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts +0 -16
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.d.ts.map +0 -1
- package/dist/src/yaml/every-build-log-witnesses-the-commit-on-disk.test.js +0 -126
- package/dist/src/yaml/fidelity-input-digest.d.ts +0 -37
- package/dist/src/yaml/fidelity-input-digest.d.ts.map +0 -1
- package/dist/src/yaml/fidelity-input-digest.js +0 -208
- package/dist/src/yaml/fidelity-input-digest.test.d.ts +0 -2
- package/dist/src/yaml/fidelity-input-digest.test.d.ts.map +0 -1
- package/dist/src/yaml/fidelity-input-digest.test.js +0 -167
- package/dist/src/yaml/fidelity-manifest.d.ts +0 -126
- package/dist/src/yaml/fidelity-manifest.d.ts.map +0 -1
- package/dist/src/yaml/fidelity-manifest.js +0 -134
- package/dist/src/yaml/fidelity-manifest.test.d.ts +0 -12
- package/dist/src/yaml/fidelity-manifest.test.d.ts.map +0 -1
- package/dist/src/yaml/fidelity-manifest.test.js +0 -251
- package/dist/src/yaml/interactive-prompts.d.ts +0 -71
- package/dist/src/yaml/interactive-prompts.d.ts.map +0 -1
- package/dist/src/yaml/interactive-prompts.js +0 -260
- package/dist/src/yaml/meta-helpers.d.ts +0 -28
- package/dist/src/yaml/meta-helpers.d.ts.map +0 -1
- package/dist/src/yaml/meta-helpers.js +0 -46
- package/dist/src/yaml/missing-env-handler.d.ts +0 -45
- package/dist/src/yaml/missing-env-handler.d.ts.map +0 -1
- package/dist/src/yaml/missing-env-handler.js +0 -64
- package/dist/src/yaml/parser.d.ts +0 -33
- package/dist/src/yaml/parser.d.ts.map +0 -1
- package/dist/src/yaml/parser.js +0 -148
- package/dist/src/yaml/parser.test.d.ts +0 -5
- package/dist/src/yaml/parser.test.d.ts.map +0 -1
- package/dist/src/yaml/parser.test.js +0 -83
- package/dist/src/yaml/pipeline-with-secrets.d.ts +0 -25
- package/dist/src/yaml/pipeline-with-secrets.d.ts.map +0 -1
- package/dist/src/yaml/pipeline-with-secrets.js +0 -76
- package/dist/src/yaml/platform-detector.d.ts +0 -83
- package/dist/src/yaml/platform-detector.d.ts.map +0 -1
- package/dist/src/yaml/platform-detector.js +0 -189
- package/dist/src/yaml/platform-detector.test.d.ts +0 -5
- package/dist/src/yaml/platform-detector.test.d.ts.map +0 -1
- package/dist/src/yaml/platform-detector.test.js +0 -525
- package/dist/src/yaml/preflight-validation.d.ts +0 -29
- package/dist/src/yaml/preflight-validation.d.ts.map +0 -1
- package/dist/src/yaml/preflight-validation.js +0 -21
- package/dist/src/yaml/secrets-manager.d.ts +0 -87
- package/dist/src/yaml/secrets-manager.d.ts.map +0 -1
- package/dist/src/yaml/secrets-manager.js +0 -230
- package/dist/src/yaml/shell-assignments.d.ts +0 -40
- package/dist/src/yaml/shell-assignments.d.ts.map +0 -1
- package/dist/src/yaml/shell-assignments.js +0 -77
- package/dist/src/yaml/step-validator-check-failure.test.d.ts +0 -24
- package/dist/src/yaml/step-validator-check-failure.test.d.ts.map +0 -1
- package/dist/src/yaml/step-validator-check-failure.test.js +0 -130
- package/dist/src/yaml/step-validator-self-assigned.test.d.ts +0 -23
- package/dist/src/yaml/step-validator-self-assigned.test.d.ts.map +0 -1
- package/dist/src/yaml/step-validator-self-assigned.test.js +0 -191
- package/dist/src/yaml/step-validator.d.ts +0 -67
- package/dist/src/yaml/step-validator.d.ts.map +0 -1
- package/dist/src/yaml/step-validator.js +0 -466
- package/dist/src/yaml/step-validator.test.d.ts +0 -18
- package/dist/src/yaml/step-validator.test.d.ts.map +0 -1
- package/dist/src/yaml/step-validator.test.js +0 -140
- package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.d.ts +0 -2
- package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/a-missing-pinned-jdk-yields-for-every-gradle-run.test.js +0 -96
- package/dist/src/yaml/steps/android-sign.d.ts +0 -35
- package/dist/src/yaml/steps/android-sign.d.ts.map +0 -1
- package/dist/src/yaml/steps/android-sign.js +0 -147
- package/dist/src/yaml/steps/android-variant-resolution.test.d.ts +0 -2
- package/dist/src/yaml/steps/android-variant-resolution.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/android-variant-resolution.test.js +0 -260
- package/dist/src/yaml/steps/android-version.d.ts +0 -26
- package/dist/src/yaml/steps/android-version.d.ts.map +0 -1
- package/dist/src/yaml/steps/android-version.js +0 -128
- package/dist/src/yaml/steps/android-version.test.d.ts +0 -5
- package/dist/src/yaml/steps/android-version.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/android-version.test.js +0 -196
- package/dist/src/yaml/steps/android.d.ts +0 -156
- package/dist/src/yaml/steps/android.d.ts.map +0 -1
- package/dist/src/yaml/steps/android.js +0 -1100
- package/dist/src/yaml/steps/app-store-deploy.d.ts +0 -48
- package/dist/src/yaml/steps/app-store-deploy.d.ts.map +0 -1
- package/dist/src/yaml/steps/app-store-deploy.js +0 -167
- package/dist/src/yaml/steps/base.d.ts +0 -263
- package/dist/src/yaml/steps/base.d.ts.map +0 -1
- package/dist/src/yaml/steps/base.js +0 -380
- package/dist/src/yaml/steps/bitrise-android-tools.d.ts +0 -26
- package/dist/src/yaml/steps/bitrise-android-tools.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-android-tools.js +0 -200
- package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts +0 -5
- package/dist/src/yaml/steps/bitrise-android-tools.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-android-tools.test.js +0 -280
- package/dist/src/yaml/steps/bitrise-apk-info.d.ts +0 -22
- package/dist/src/yaml/steps/bitrise-apk-info.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-apk-info.js +0 -144
- package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts +0 -5
- package/dist/src/yaml/steps/bitrise-apk-info.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-apk-info.test.js +0 -331
- package/dist/src/yaml/steps/bitrise-slack.d.ts +0 -49
- package/dist/src/yaml/steps/bitrise-slack.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-slack.js +0 -280
- package/dist/src/yaml/steps/bitrise-slack.test.d.ts +0 -5
- package/dist/src/yaml/steps/bitrise-slack.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-slack.test.js +0 -484
- package/dist/src/yaml/steps/bitrise-ssh.d.ts +0 -27
- package/dist/src/yaml/steps/bitrise-ssh.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-ssh.js +0 -134
- package/dist/src/yaml/steps/bitrise-ssh.test.d.ts +0 -5
- package/dist/src/yaml/steps/bitrise-ssh.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/bitrise-ssh.test.js +0 -205
- package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts +0 -27
- package/dist/src/yaml/steps/cache-pull-daemon.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cache-pull-daemon.test.js +0 -212
- package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts +0 -42
- package/dist/src/yaml/steps/cache-pull-restore-roots.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cache-pull-restore-roots.test.js +0 -209
- package/dist/src/yaml/steps/cache-push-daemon.test.d.ts +0 -27
- package/dist/src/yaml/steps/cache-push-daemon.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cache-push-daemon.test.js +0 -171
- package/dist/src/yaml/steps/cache.d.ts +0 -59
- package/dist/src/yaml/steps/cache.d.ts.map +0 -1
- package/dist/src/yaml/steps/cache.js +0 -1027
- package/dist/src/yaml/steps/capture-injection.d.ts +0 -30
- package/dist/src/yaml/steps/capture-injection.d.ts.map +0 -1
- package/dist/src/yaml/steps/capture-injection.js +0 -88
- package/dist/src/yaml/steps/capture-injection.test.d.ts +0 -2
- package/dist/src/yaml/steps/capture-injection.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/capture-injection.test.js +0 -163
- package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts +0 -42
- package/dist/src/yaml/steps/cocoapods-deployment-floor.d.ts.map +0 -1
- package/dist/src/yaml/steps/cocoapods-deployment-floor.js +0 -140
- package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts +0 -11
- package/dist/src/yaml/steps/cocoapods-deployment-floor.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cocoapods-deployment-floor.test.js +0 -176
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts +0 -22
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cocoapods-install-emits-valid-bash.test.js +0 -97
- package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts +0 -17
- package/dist/src/yaml/steps/cocoapods-locked-bundler.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cocoapods-locked-bundler.test.js +0 -282
- package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts +0 -17
- package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/cocoapods-skip-if-absent.test.js +0 -132
- package/dist/src/yaml/steps/envstore-loader.test.d.ts +0 -15
- package/dist/src/yaml/steps/envstore-loader.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/envstore-loader.test.js +0 -143
- package/dist/src/yaml/steps/fastlane.d.ts +0 -27
- package/dist/src/yaml/steps/fastlane.d.ts.map +0 -1
- package/dist/src/yaml/steps/fastlane.js +0 -79
- package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts +0 -53
- package/dist/src/yaml/steps/fidelity-aspect-guard.d.ts.map +0 -1
- package/dist/src/yaml/steps/fidelity-aspect-guard.js +0 -72
- package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts +0 -11
- package/dist/src/yaml/steps/fidelity-aspect-guard.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/fidelity-aspect-guard.test.js +0 -50
- package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts +0 -87
- package/dist/src/yaml/steps/fidelity-reference-provenance.d.ts.map +0 -1
- package/dist/src/yaml/steps/fidelity-reference-provenance.js +0 -111
- package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts +0 -22
- package/dist/src/yaml/steps/fidelity-reference-provenance.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/fidelity-reference-provenance.test.js +0 -118
- package/dist/src/yaml/steps/file.d.ts +0 -52
- package/dist/src/yaml/steps/file.d.ts.map +0 -1
- package/dist/src/yaml/steps/file.js +0 -108
- package/dist/src/yaml/steps/flutter.d.ts +0 -63
- package/dist/src/yaml/steps/flutter.d.ts.map +0 -1
- package/dist/src/yaml/steps/flutter.js +0 -215
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts +0 -16
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/git-clone-names-the-inputs-it-ignores.test.js +0 -127
- package/dist/src/yaml/steps/git-clone.d.ts +0 -32
- package/dist/src/yaml/steps/git-clone.d.ts.map +0 -1
- package/dist/src/yaml/steps/git-clone.js +0 -171
- package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts +0 -10
- package/dist/src/yaml/steps/git-fetch-retry-exec.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/git-fetch-retry-exec.test.js +0 -162
- package/dist/src/yaml/steps/git-fetch-retry.d.ts +0 -62
- package/dist/src/yaml/steps/git-fetch-retry.d.ts.map +0 -1
- package/dist/src/yaml/steps/git-fetch-retry.js +0 -155
- package/dist/src/yaml/steps/git-fetch-retry.test.d.ts +0 -5
- package/dist/src/yaml/steps/git-fetch-retry.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/git-fetch-retry.test.js +0 -86
- package/dist/src/yaml/steps/google-play-deploy.d.ts +0 -37
- package/dist/src/yaml/steps/google-play-deploy.d.ts.map +0 -1
- package/dist/src/yaml/steps/google-play-deploy.js +0 -193
- package/dist/src/yaml/steps/google-play-deploy.test.d.ts +0 -5
- package/dist/src/yaml/steps/google-play-deploy.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/google-play-deploy.test.js +0 -310
- package/dist/src/yaml/steps/gradle-memory.d.ts +0 -91
- package/dist/src/yaml/steps/gradle-memory.d.ts.map +0 -1
- package/dist/src/yaml/steps/gradle-memory.js +0 -495
- package/dist/src/yaml/steps/gradle-memory.test.d.ts +0 -2
- package/dist/src/yaml/steps/gradle-memory.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/gradle-memory.test.js +0 -344
- package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts +0 -28
- package/dist/src/yaml/steps/gradle-sdk-cmake.d.ts.map +0 -1
- package/dist/src/yaml/steps/gradle-sdk-cmake.js +0 -107
- package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts +0 -2
- package/dist/src/yaml/steps/gradle-sdk-cmake.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/gradle-sdk-cmake.test.js +0 -183
- package/dist/src/yaml/steps/index.d.ts +0 -10
- package/dist/src/yaml/steps/index.d.ts.map +0 -1
- package/dist/src/yaml/steps/index.js +0 -1461
- package/dist/src/yaml/steps/ios-deps.d.ts +0 -78
- package/dist/src/yaml/steps/ios-deps.d.ts.map +0 -1
- package/dist/src/yaml/steps/ios-deps.js +0 -279
- package/dist/src/yaml/steps/ios-deps.test.d.ts +0 -5
- package/dist/src/yaml/steps/ios-deps.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/ios-deps.test.js +0 -90
- package/dist/src/yaml/steps/ios-signing.d.ts +0 -31
- package/dist/src/yaml/steps/ios-signing.d.ts.map +0 -1
- package/dist/src/yaml/steps/ios-signing.js +0 -144
- package/dist/src/yaml/steps/ios-version.d.ts +0 -47
- package/dist/src/yaml/steps/ios-version.d.ts.map +0 -1
- package/dist/src/yaml/steps/ios-version.js +0 -151
- package/dist/src/yaml/steps/java-home-version-spec.test.d.ts +0 -10
- package/dist/src/yaml/steps/java-home-version-spec.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/java-home-version-spec.test.js +0 -58
- package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.d.ts +0 -2
- package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/kotlin-metadata-compiles-on-a-daemon.test.js +0 -139
- package/dist/src/yaml/steps/linting.d.ts +0 -47
- package/dist/src/yaml/steps/linting.d.ts.map +0 -1
- package/dist/src/yaml/steps/linting.js +0 -148
- package/dist/src/yaml/steps/phase2.test.d.ts +0 -6
- package/dist/src/yaml/steps/phase2.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/phase2.test.js +0 -197
- package/dist/src/yaml/steps/phase3.test.d.ts +0 -5
- package/dist/src/yaml/steps/phase3.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/phase3.test.js +0 -144
- package/dist/src/yaml/steps/phase4.test.d.ts +0 -5
- package/dist/src/yaml/steps/phase4.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/phase4.test.js +0 -166
- package/dist/src/yaml/steps/phase5.test.d.ts +0 -6
- package/dist/src/yaml/steps/phase5.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/phase5.test.js +0 -263
- package/dist/src/yaml/steps/registry.d.ts +0 -88
- package/dist/src/yaml/steps/registry.d.ts.map +0 -1
- package/dist/src/yaml/steps/registry.js +0 -125
- package/dist/src/yaml/steps/registry.test.d.ts +0 -5
- package/dist/src/yaml/steps/registry.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/registry.test.js +0 -235
- package/dist/src/yaml/steps/release.d.ts +0 -50
- package/dist/src/yaml/steps/release.d.ts.map +0 -1
- package/dist/src/yaml/steps/release.js +0 -154
- package/dist/src/yaml/steps/render-post-processor.d.ts +0 -100
- package/dist/src/yaml/steps/render-post-processor.d.ts.map +0 -1
- package/dist/src/yaml/steps/render-post-processor.js +0 -270
- package/dist/src/yaml/steps/render-post-processor.test.d.ts +0 -13
- package/dist/src/yaml/steps/render-post-processor.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/render-post-processor.test.js +0 -220
- package/dist/src/yaml/steps/script.d.ts +0 -23
- package/dist/src/yaml/steps/script.d.ts.map +0 -1
- package/dist/src/yaml/steps/script.js +0 -63
- package/dist/src/yaml/steps/spec-validation.test.d.ts +0 -6
- package/dist/src/yaml/steps/spec-validation.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/spec-validation.test.js +0 -130
- package/dist/src/yaml/steps/steps.test.d.ts +0 -6
- package/dist/src/yaml/steps/steps.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/steps.test.js +0 -881
- package/dist/src/yaml/steps/structural-facts.d.ts +0 -992
- package/dist/src/yaml/steps/structural-facts.d.ts.map +0 -1
- package/dist/src/yaml/steps/structural-facts.js +0 -3276
- package/dist/src/yaml/steps/structural-facts.test.d.ts +0 -12
- package/dist/src/yaml/steps/structural-facts.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/structural-facts.test.js +0 -3039
- package/dist/src/yaml/steps/test-config.d.ts +0 -5
- package/dist/src/yaml/steps/test-config.d.ts.map +0 -1
- package/dist/src/yaml/steps/test-config.js +0 -25
- package/dist/src/yaml/steps/trim-align-corpus.test.d.ts +0 -28
- package/dist/src/yaml/steps/trim-align-corpus.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/trim-align-corpus.test.js +0 -383
- package/dist/src/yaml/steps/trim-align.d.ts +0 -87
- package/dist/src/yaml/steps/trim-align.d.ts.map +0 -1
- package/dist/src/yaml/steps/trim-align.js +0 -367
- package/dist/src/yaml/steps/trim-align.test.d.ts +0 -11
- package/dist/src/yaml/steps/trim-align.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/trim-align.test.js +0 -261
- package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts +0 -66
- package/dist/src/yaml/steps/ui-fidelity-preview-android.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-preview-android.js +0 -251
- package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts +0 -13
- package/dist/src/yaml/steps/ui-fidelity-preview-android.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-preview-android.test.js +0 -302
- package/dist/src/yaml/steps/ui-fidelity-preview.d.ts +0 -134
- package/dist/src/yaml/steps/ui-fidelity-preview.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-preview.js +0 -173
- package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts +0 -11
- package/dist/src/yaml/steps/ui-fidelity-preview.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-preview.test.js +0 -210
- package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts +0 -155
- package/dist/src/yaml/steps/ui-fidelity-render-android.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-render-android.js +0 -1389
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts +0 -2
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-render-android.test.js +0 -1106
- package/dist/src/yaml/steps/ui-fidelity-render.d.ts +0 -230
- package/dist/src/yaml/steps/ui-fidelity-render.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-render.js +0 -1597
- package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts +0 -18
- package/dist/src/yaml/steps/ui-fidelity-render.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/ui-fidelity-render.test.js +0 -1871
- package/dist/src/yaml/steps/xcode-app-product.d.ts +0 -90
- package/dist/src/yaml/steps/xcode-app-product.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-app-product.js +0 -247
- package/dist/src/yaml/steps/xcode-app-product.test.d.ts +0 -2
- package/dist/src/yaml/steps/xcode-app-product.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-app-product.test.js +0 -202
- package/dist/src/yaml/steps/xcode-derived-data.test.d.ts +0 -30
- package/dist/src/yaml/steps/xcode-derived-data.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-derived-data.test.js +0 -279
- package/dist/src/yaml/steps/xcode-destination.d.ts +0 -47
- package/dist/src/yaml/steps/xcode-destination.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-destination.js +0 -202
- package/dist/src/yaml/steps/xcode-destination.test.d.ts +0 -9
- package/dist/src/yaml/steps/xcode-destination.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-destination.test.js +0 -227
- package/dist/src/yaml/steps/xcode-new.test.d.ts +0 -5
- package/dist/src/yaml/steps/xcode-new.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-new.test.js +0 -211
- package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts +0 -9
- package/dist/src/yaml/steps/xcode-resolve-retry.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-resolve-retry.test.js +0 -146
- package/dist/src/yaml/steps/xcode-simulator-archs.d.ts +0 -53
- package/dist/src/yaml/steps/xcode-simulator-archs.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-simulator-archs.js +0 -98
- package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts +0 -21
- package/dist/src/yaml/steps/xcode-simulator-archs.test.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode-simulator-archs.test.js +0 -155
- package/dist/src/yaml/steps/xcode.d.ts +0 -230
- package/dist/src/yaml/steps/xcode.d.ts.map +0 -1
- package/dist/src/yaml/steps/xcode.js +0 -1350
- package/dist/src/yaml/types.d.ts +0 -76
- package/dist/src/yaml/types.d.ts.map +0 -1
- package/dist/src/yaml/types.js +0 -5
- package/dist/src/yaml/validation-types.d.ts +0 -102
- package/dist/src/yaml/validation-types.d.ts.map +0 -1
- package/dist/src/yaml/validation-types.js +0 -8
- package/dist/src/yaml/yaml-updater.d.ts +0 -24
- package/dist/src/yaml/yaml-updater.d.ts.map +0 -1
- package/dist/src/yaml/yaml-updater.js +0 -128
|
@@ -1,1433 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
-
import prompts from "prompts";
|
|
4
|
-
import { scanAndroidProject, formatScanResult, runnableJavaVersion, javaVersionProblem, } from "./android-scanner.js";
|
|
5
|
-
import { scanIosProject, formatIosScanResult } from "./ios-scanner.js";
|
|
6
|
-
import { SecretsManager } from "../yaml/secrets-manager.js";
|
|
7
|
-
import { collectFileSecret } from "./file-secret-collector.js";
|
|
8
|
-
import { generateGitHubActionsWorkflow } from "./github-workflow.js";
|
|
9
|
-
import { detectDefaultBranch, FALLBACK_DEFAULT_BRANCH } from "../shared/git.js";
|
|
10
|
-
function variantFromName(name) {
|
|
11
|
-
const buildType = name.charAt(0).toUpperCase() + name.slice(1);
|
|
12
|
-
return { variant: name, buildType, gradleTask: `assemble${buildType}` };
|
|
13
|
-
}
|
|
14
|
-
const DEFAULT_VARIANTS = {
|
|
15
|
-
primary: variantFromName("debug"),
|
|
16
|
-
pullRequest: variantFromName("debug"),
|
|
17
|
-
release: variantFromName("release"),
|
|
18
|
-
};
|
|
19
|
-
async function promptScheme(workflowLabel, defaultScheme, detectedSchemes) {
|
|
20
|
-
const choices = detectedSchemes.map((s) => ({ title: s, value: s }));
|
|
21
|
-
choices.push({ title: "Other (enter custom scheme)", value: "__custom__" });
|
|
22
|
-
const defaultIndex = detectedSchemes.indexOf(defaultScheme);
|
|
23
|
-
const initial = defaultIndex !== -1 ? defaultIndex : 0;
|
|
24
|
-
const { choice } = await prompts({
|
|
25
|
-
type: "select",
|
|
26
|
-
name: "choice",
|
|
27
|
-
message: `Xcode scheme for ${workflowLabel}:`,
|
|
28
|
-
choices,
|
|
29
|
-
initial,
|
|
30
|
-
});
|
|
31
|
-
if (!choice)
|
|
32
|
-
return defaultScheme;
|
|
33
|
-
if (choice === "__custom__") {
|
|
34
|
-
const { name } = await prompts({
|
|
35
|
-
type: "text",
|
|
36
|
-
name: "name",
|
|
37
|
-
message: "Enter scheme name:",
|
|
38
|
-
validate: (v) => (v.trim().length > 0 ? true : "Scheme name is required"),
|
|
39
|
-
});
|
|
40
|
-
return name?.trim() || defaultScheme;
|
|
41
|
-
}
|
|
42
|
-
return choice;
|
|
43
|
-
}
|
|
44
|
-
async function promptConfiguration(workflowLabel, defaultConfig) {
|
|
45
|
-
const choices = [
|
|
46
|
-
{ title: "Debug", value: "Debug" },
|
|
47
|
-
{ title: "Release", value: "Release" },
|
|
48
|
-
{ title: "Other (enter custom configuration)", value: "__custom__" },
|
|
49
|
-
];
|
|
50
|
-
const defaultIndex = choices.findIndex((c) => c.value === defaultConfig);
|
|
51
|
-
const initial = defaultIndex !== -1 ? defaultIndex : 0;
|
|
52
|
-
const { choice } = await prompts({
|
|
53
|
-
type: "select",
|
|
54
|
-
name: "choice",
|
|
55
|
-
message: `Build configuration for ${workflowLabel}:`,
|
|
56
|
-
choices,
|
|
57
|
-
initial,
|
|
58
|
-
});
|
|
59
|
-
if (!choice)
|
|
60
|
-
return defaultConfig;
|
|
61
|
-
if (choice === "__custom__") {
|
|
62
|
-
const { name } = await prompts({
|
|
63
|
-
type: "text",
|
|
64
|
-
name: "name",
|
|
65
|
-
message: "Enter configuration name (e.g. Staging, AdHoc):",
|
|
66
|
-
validate: (v) => (v.trim().length > 0 ? true : "Configuration name is required"),
|
|
67
|
-
});
|
|
68
|
-
return name?.trim() || defaultConfig;
|
|
69
|
-
}
|
|
70
|
-
return choice;
|
|
71
|
-
}
|
|
72
|
-
async function promptDistributionMethod(workflowLabel, defaultMethod) {
|
|
73
|
-
const choices = [
|
|
74
|
-
{ title: "development — development signing, internal use", value: "development" },
|
|
75
|
-
{ title: "ad-hoc — Ad Hoc distribution (specific devices)", value: "ad-hoc" },
|
|
76
|
-
{ title: "app-store — App Store / TestFlight upload", value: "app-store" },
|
|
77
|
-
{ title: "enterprise — Enterprise in-house distribution", value: "enterprise" },
|
|
78
|
-
];
|
|
79
|
-
const defaultIndex = choices.findIndex((c) => c.value === defaultMethod);
|
|
80
|
-
const initial = defaultIndex !== -1 ? defaultIndex : 0;
|
|
81
|
-
const { choice } = await prompts({
|
|
82
|
-
type: "select",
|
|
83
|
-
name: "choice",
|
|
84
|
-
message: `Distribution method for ${workflowLabel}:`,
|
|
85
|
-
choices,
|
|
86
|
-
initial,
|
|
87
|
-
});
|
|
88
|
-
return choice || defaultMethod;
|
|
89
|
-
}
|
|
90
|
-
async function promptVariant(workflowLabel, defaultName, detectedVariants) {
|
|
91
|
-
const choices = detectedVariants.map((v) => ({ title: v, value: v }));
|
|
92
|
-
choices.push({ title: "Other (enter custom variant)", value: "__custom__" });
|
|
93
|
-
const defaultIndex = detectedVariants.indexOf(defaultName);
|
|
94
|
-
const initial = defaultIndex !== -1 ? defaultIndex : 0;
|
|
95
|
-
const { choice } = await prompts({
|
|
96
|
-
type: "select",
|
|
97
|
-
name: "choice",
|
|
98
|
-
message: `Build variant for ${workflowLabel}:`,
|
|
99
|
-
choices,
|
|
100
|
-
initial,
|
|
101
|
-
});
|
|
102
|
-
if (!choice)
|
|
103
|
-
return variantFromName(defaultName);
|
|
104
|
-
if (choice === "__custom__") {
|
|
105
|
-
const { name } = await prompts({
|
|
106
|
-
type: "text",
|
|
107
|
-
name: "name",
|
|
108
|
-
message: "Enter variant name (e.g. stagingDebug, freeRelease):",
|
|
109
|
-
validate: (v) => (v.trim().length > 0 ? true : "Variant name is required"),
|
|
110
|
-
});
|
|
111
|
-
return variantFromName(name?.trim() || defaultName);
|
|
112
|
-
}
|
|
113
|
-
return variantFromName(choice);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* The properties file the Secrets Gradle Plugin reads, out of the warnings the
|
|
117
|
-
* scanner produced for it.
|
|
118
|
-
*
|
|
119
|
-
* The two `secrets-plugin` warnings disagree about `location`, and reading the
|
|
120
|
-
* wrong one aims the `file@` step at a build file. The per-key warning carries
|
|
121
|
-
* the **properties file** there; the fallback warning carries the *build file
|
|
122
|
-
* that applies the plugin* and names the properties file only in its message.
|
|
123
|
-
*/
|
|
124
|
-
export function secretsPropertiesFileFrom(warnings) {
|
|
125
|
-
const plugin = warnings.filter((w) => w.category === "secrets-plugin");
|
|
126
|
-
if (plugin.length === 0)
|
|
127
|
-
return null;
|
|
128
|
-
const fromLocation = plugin.find((w) => w.location?.endsWith(".properties"));
|
|
129
|
-
if (fromLocation?.location)
|
|
130
|
-
return fromLocation.location;
|
|
131
|
-
for (const w of plugin) {
|
|
132
|
-
const named = /reads '([^']+)'/.exec(w.message);
|
|
133
|
-
if (named)
|
|
134
|
-
return named[1];
|
|
135
|
-
}
|
|
136
|
-
return "local.properties";
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* The product flavor a variant builds, out of the flavors the scanner found —
|
|
140
|
-
* `ciDynamicDebug` is a build of `ciDynamic`, `prodRelease` of `prod`.
|
|
141
|
-
*
|
|
142
|
-
* An Android variant name is the flavor followed by the capitalised build
|
|
143
|
-
* type, so the character after the flavor has to be an upper-case one. That
|
|
144
|
-
* check is load-bearing: without it a flavor called `pro` would claim
|
|
145
|
-
* `prodRelease`. The longest match wins, so `ciDynamic` beats `ci` on a
|
|
146
|
-
* repository that declares both.
|
|
147
|
-
*/
|
|
148
|
-
export function flavorOfVariant(variant, productFlavors) {
|
|
149
|
-
const matches = productFlavors
|
|
150
|
-
.filter((flavor) => {
|
|
151
|
-
if (flavor.length === 0 || !variant.startsWith(flavor))
|
|
152
|
-
return false;
|
|
153
|
-
const head = variant.slice(flavor.length)[0];
|
|
154
|
-
return head !== undefined && head === head.toUpperCase() && head !== head.toLowerCase();
|
|
155
|
-
})
|
|
156
|
-
.sort((a, b) => b.length - a.length);
|
|
157
|
-
return matches[0] ?? null;
|
|
158
|
-
}
|
|
159
|
-
/** Where a flavor's `google-services.json` belongs inside the app module. */
|
|
160
|
-
export function googleServicesPathFor(flavor, module = "app") {
|
|
161
|
-
return flavor
|
|
162
|
-
? `${module}/src/${flavor}/google-services.json`
|
|
163
|
-
: `${module}/google-services.json`;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* The per-workflow path and key maps for `SetupOptions`, from the variants the
|
|
167
|
-
* workflows build and the flavors the scanner found.
|
|
168
|
-
*
|
|
169
|
-
* `app/google-services.json` used to be hardcoded here. For a flavored build
|
|
170
|
-
* that is the plugin's *fallback* location, not the right one, and writing
|
|
171
|
-
* there is silently wrong rather than loudly wrong: the plugin finds a file,
|
|
172
|
-
* parses it, and fails on a package name — which reads as a Firebase
|
|
173
|
-
* misconfiguration rather than as the wrong file having been delivered. That
|
|
174
|
-
* is what `bld_8e60d90ab4e55f85d2f72459` spent 1292 Gradle tasks proving.
|
|
175
|
-
*/
|
|
176
|
-
export function googleServicesTargets(variants, productFlavors, module = "app") {
|
|
177
|
-
const paths = {};
|
|
178
|
-
for (const [workflow, config] of variantByWorkflow(variants)) {
|
|
179
|
-
const flavor = flavorOfVariant(config.variant, productFlavors);
|
|
180
|
-
paths[workflow] = googleServicesPathFor(flavor, module);
|
|
181
|
-
}
|
|
182
|
-
return paths;
|
|
183
|
-
}
|
|
184
|
-
/** The three workflows every generated pipeline has, in the order they are emitted. */
|
|
185
|
-
export const GENERATED_WORKFLOWS = ["primary", "pull-request", "release"];
|
|
186
|
-
/** The variant each workflow builds, keyed by the workflow's name in the YAML. */
|
|
187
|
-
function variantByWorkflow(variants) {
|
|
188
|
-
return [
|
|
189
|
-
["primary", variants.primary],
|
|
190
|
-
["pull-request", variants.pullRequest],
|
|
191
|
-
["release", variants.release],
|
|
192
|
-
];
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* The `workflowFlavors` map for `SetupOptions`, from the variants the
|
|
196
|
-
* workflows build and the flavors the scanner found.
|
|
197
|
-
*/
|
|
198
|
-
export function variantFlavors(variants, productFlavors) {
|
|
199
|
-
const flavors = {};
|
|
200
|
-
for (const [workflow, config] of variantByWorkflow(variants)) {
|
|
201
|
-
flavors[workflow] = flavorOfVariant(config.variant, productFlavors);
|
|
202
|
-
}
|
|
203
|
-
return flavors;
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* The `envs` slots one credential needs: one when every workflow that reads it
|
|
207
|
-
* wants the same bytes, one per workflow when they do not.
|
|
208
|
-
*
|
|
209
|
-
* What "the same bytes" means is the credential's own business — a keystore
|
|
210
|
-
* follows the target path the pipeline declares for it, `google-services.json`
|
|
211
|
-
* the flavor's source set, `local.properties` the flavor itself — so the
|
|
212
|
-
* caller passes the discriminator it already computed, one entry per workflow
|
|
213
|
-
* that reads the credential, and this decides only whether they agree. A
|
|
214
|
-
* credential only one workflow reads can never disagree, which is why the
|
|
215
|
-
* genuinely release-only ones keep one slot however flavored the repository
|
|
216
|
-
* is.
|
|
217
|
-
*
|
|
218
|
-
* **The name never changes — the block it sits in does.** An agreed credential
|
|
219
|
-
* is declared once in `app.envs`; a disagreement is declared under the plain
|
|
220
|
-
* credential name in **each workflow's own `envs`**, which is where the YAML
|
|
221
|
-
* already says which workflow something belongs to. `EnvResolver` merges
|
|
222
|
-
* `app.envs` and then the running workflow's own, so `release`'s declaration is
|
|
223
|
-
* invisible to `primary` without anything being encoded.
|
|
224
|
-
*
|
|
225
|
-
* `CIBUILD_SW__<WORKFLOW>__<VAR>` is deliberately *not* used here. That prefix
|
|
226
|
-
* is the transport encoding `ci secrets upload` carries a workflow-scoped
|
|
227
|
-
* **value** under — Invarn's secret scopes have no workflow column, so the
|
|
228
|
-
* workflow travels in the key. A pipeline has structure instead, and spending
|
|
229
|
-
* the encoding there would be a second way to say what the block already says
|
|
230
|
-
* (Alex, 2026-09-10).
|
|
231
|
-
*
|
|
232
|
-
* Every workflow gets a slot, not only the odd one out: a workflow left without
|
|
233
|
-
* one would have neither a value nor the declaration `file@1.0.0` refuses over.
|
|
234
|
-
*
|
|
235
|
-
* The `file@` step's `var_name` is the same plain name either way.
|
|
236
|
-
*/
|
|
237
|
-
export function credentialSlots(varName, discriminatorByWorkflow) {
|
|
238
|
-
const distinct = new Set(Object.values(discriminatorByWorkflow));
|
|
239
|
-
if (distinct.size <= 1)
|
|
240
|
-
return [{ slot: varName, workflow: null }];
|
|
241
|
-
return Object.keys(discriminatorByWorkflow).map((workflow) => ({
|
|
242
|
-
slot: varName,
|
|
243
|
-
workflow,
|
|
244
|
-
}));
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* One discriminator per workflow, defaulted the way the step that writes the
|
|
248
|
-
* credential defaults its own input — so a repository whose scan resolved
|
|
249
|
-
* nothing does not read as three workflows that disagree.
|
|
250
|
-
*/
|
|
251
|
-
function discriminators(map, fallback) {
|
|
252
|
-
const out = {};
|
|
253
|
-
for (const workflow of GENERATED_WORKFLOWS)
|
|
254
|
-
out[workflow] = map[workflow] ?? fallback;
|
|
255
|
-
return out;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* The groups `ci init` collects a credential's value in: one per distinct value
|
|
259
|
-
* of whatever that credential follows, in workflow order.
|
|
260
|
-
*
|
|
261
|
-
* The same discriminator `credentialSlots` splits the *declaration* by, and
|
|
262
|
-
* deliberately the same function computing it, so what `ci init` asks for and
|
|
263
|
-
* what the pipeline declares cannot drift apart. One group means one slot and
|
|
264
|
-
* one question; two groups mean one slot per workflow and a question per group.
|
|
265
|
-
*/
|
|
266
|
-
export function collectionGroups(discriminatorByWorkflow, label) {
|
|
267
|
-
const byDiscriminator = new Map();
|
|
268
|
-
for (const [workflow, value] of Object.entries(discriminatorByWorkflow)) {
|
|
269
|
-
byDiscriminator.set(value, [...(byDiscriminator.get(value) ?? []), workflow]);
|
|
270
|
-
}
|
|
271
|
-
return [...byDiscriminator].map(([discriminator, workflows]) => ({
|
|
272
|
-
label: label(discriminator, workflows),
|
|
273
|
-
workflows,
|
|
274
|
-
discriminator,
|
|
275
|
-
}));
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* The groups the Secrets Gradle Plugin's properties file is collected in: one
|
|
279
|
-
* per flavor.
|
|
280
|
-
*
|
|
281
|
-
* Unlike `google-services.json`, nothing about this credential's target says
|
|
282
|
-
* the bytes differ — every workflow writes the same file to the same path.
|
|
283
|
-
* What differs is the content: the plugin turns the file into `BuildConfig`,
|
|
284
|
-
* and a flavor is an environment. Asking once and storing one value gives
|
|
285
|
-
* `prod` the CI backend's base URL, on a green build.
|
|
286
|
-
*/
|
|
287
|
-
export function propertiesFileGroups(workflowFlavors) {
|
|
288
|
-
return collectionGroups(discriminators(workflowFlavors, ""), (flavor, workflows) => flavor ? `the ${flavor} flavor (${workflows.join(", ")})` : "every workflow");
|
|
289
|
-
}
|
|
290
|
-
export function detectSetupNeeds(warnings) {
|
|
291
|
-
const secretsPropertiesPath = secretsPropertiesFileFrom(warnings);
|
|
292
|
-
return {
|
|
293
|
-
secretsProperties: secretsPropertiesPath !== null,
|
|
294
|
-
secretsPropertiesPath: secretsPropertiesPath ?? "local.properties",
|
|
295
|
-
keystore: warnings.some((w) => w.category === "missing-file" && /keystore/i.test(w.message)),
|
|
296
|
-
keystoreProperties: warnings.some((w) => w.category === "missing-file" && /keystore\.properties/i.test(w.message)),
|
|
297
|
-
googleServices: warnings.some((w) => w.category === "firebase" ||
|
|
298
|
-
(w.category === "missing-file" && /google-services/i.test(w.message))),
|
|
299
|
-
googlePlayDeploy: false,
|
|
300
|
-
googlePlayPackageName: '',
|
|
301
|
-
artifactType: 'apk',
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* The `trigger_map` block appended to every generated pipeline.
|
|
306
|
-
*
|
|
307
|
-
* Without it the pipeline is inert on the Invarn side: `dispatchRepoEvent`
|
|
308
|
-
* skips any pipeline whose YAML declares no trigger_map, so a scaffolded repo
|
|
309
|
-
* receives pushes and builds nothing, with no error to explain it.
|
|
310
|
-
*
|
|
311
|
-
* `release` is deliberately absent — it deploys to Google Play / App Store,
|
|
312
|
-
* and a webhook must never reach it.
|
|
313
|
-
*/
|
|
314
|
-
export function triggerMapBlock(defaultBranch = FALLBACK_DEFAULT_BRANCH) {
|
|
315
|
-
const branch = defaultBranch.trim() || FALLBACK_DEFAULT_BRANCH;
|
|
316
|
-
// JSON.stringify gives a valid double-quoted YAML scalar for any branch
|
|
317
|
-
// name git will accept. Quoting is not cosmetic: a branch called `no` or
|
|
318
|
-
// `y` parses as a boolean unquoted, and one called `1.0` as a number.
|
|
319
|
-
return `
|
|
320
|
-
# Which GitHub events build which workflow. Without this block the pipeline
|
|
321
|
-
# is never triggered by a push or a pull request.
|
|
322
|
-
trigger_map:
|
|
323
|
-
- push_branch: ${JSON.stringify(branch)}
|
|
324
|
-
workflow: primary
|
|
325
|
-
- pull_request_target_branch: "*"
|
|
326
|
-
workflow: pull-request
|
|
327
|
-
`;
|
|
328
|
-
}
|
|
329
|
-
/**
|
|
330
|
-
* The JDK a generated pipeline names when the project's own constraints have
|
|
331
|
-
* no answer — nothing declared and no readable wrapper, or a declaration no
|
|
332
|
-
* installed JDK can meet. A generated pipeline has to write something; what it
|
|
333
|
-
* must not do is write a number the wrapper cannot start on and call that a
|
|
334
|
-
* choice.
|
|
335
|
-
*/
|
|
336
|
-
export const DEFAULT_PIPELINE_JAVA_VERSION = 17;
|
|
337
|
-
export function generateAndroidPipeline(javaVersion = DEFAULT_PIPELINE_JAVA_VERSION, setup = { keystore: false, keystoreProperties: false, googleServices: false, googlePlayDeploy: false, googlePlayPackageName: '', artifactType: 'apk', keystorePaths: {}, googleServicesPaths: {}, workflowFlavors: {}, secretsProperties: false, secretsPropertiesPath: 'local.properties' }, variants = DEFAULT_VARIANTS, cacheTechnology = "gradle", metaNamespace = 'cibuild.io', defaultBranch = FALLBACK_DEFAULT_BRANCH) {
|
|
338
|
-
const keystoreStepFor = (wf) => {
|
|
339
|
-
if (!setup.keystore)
|
|
340
|
-
return "";
|
|
341
|
-
const path = setup.keystorePaths[wf] ?? "keystore.jks";
|
|
342
|
-
return `
|
|
343
|
-
- file@1.0.0:
|
|
344
|
-
title: Setup Keystore
|
|
345
|
-
is_skippable: true
|
|
346
|
-
inputs:
|
|
347
|
-
target_path: ${path}
|
|
348
|
-
var_name: KEYSTORE_BASE64
|
|
349
|
-
base64_encoded: true
|
|
350
|
-
`;
|
|
351
|
-
};
|
|
352
|
-
// Add BUILD_TYPE override to workflow envs when it differs from the global default ("Debug")
|
|
353
|
-
const buildTypeEnvOverride = (v) => v.buildType !== "Debug" ? ` - BUILD_TYPE: ${v.buildType}\n` : "";
|
|
354
|
-
// Optional env var stubs (empty defaults — filled via ci secrets add).
|
|
355
|
-
//
|
|
356
|
-
// Declared and not merely used by a step: `unsetEnvKeys` — what `invarn
|
|
357
|
-
// setup` searches the developer's machine by — reads `envs`, never step
|
|
358
|
-
// inputs. An undeclared credential is never discovered and never offered for
|
|
359
|
-
// upload, however many steps name it. The same declaration is what
|
|
360
|
-
// `file@1.0.0` refuses over when nobody supplied the value.
|
|
361
|
-
//
|
|
362
|
-
// One slot per credential where every workflow that reads it wants the same
|
|
363
|
-
// bytes, and one per workflow where they do not — see `credentialSlots`. The
|
|
364
|
-
// name is the plain credential either way; a per-workflow one is declared in
|
|
365
|
-
// that workflow's own `envs` rather than under an encoded name.
|
|
366
|
-
const appEnvDeclarations = [];
|
|
367
|
-
const workflowEnvDeclarations = {};
|
|
368
|
-
const declare = (varName, discriminatorByWorkflow, comment) => {
|
|
369
|
-
for (const { slot, workflow } of credentialSlots(varName, discriminatorByWorkflow)) {
|
|
370
|
-
if (workflow === null) {
|
|
371
|
-
appEnvDeclarations.push(` - ${slot}: ""${comment(null)}\n`);
|
|
372
|
-
continue;
|
|
373
|
-
}
|
|
374
|
-
// Six spaces, not four: a workflow's `envs` sits one level deeper.
|
|
375
|
-
(workflowEnvDeclarations[workflow] ??= []).push(` - ${slot}: ""${comment(workflow)}\n`);
|
|
376
|
-
}
|
|
377
|
-
};
|
|
378
|
-
/** Every credential declaration this workflow owns, after its own variables. */
|
|
379
|
-
const workflowEnvFor = (workflow) => (workflowEnvDeclarations[workflow] ?? []).join("");
|
|
380
|
-
// Declared in the order they appear in `app.envs`, which the byte-identity
|
|
381
|
-
// test on an unflavored pipeline pins.
|
|
382
|
-
if (setup.keystore) {
|
|
383
|
-
declare("KEYSTORE_BASE64", discriminators(setup.keystorePaths, "keystore.jks"), (wf) => wf
|
|
384
|
-
? ` # Set via: ci secrets add KEYSTORE_BASE64 -w ${wf}`
|
|
385
|
-
: ` # Set via: ci secrets add KEYSTORE_BASE64 -w primary`);
|
|
386
|
-
}
|
|
387
|
-
// Release-only, and legitimately so: nothing but `release` signs a release
|
|
388
|
-
// or talks to Google Play, so there is no second workflow to disagree with.
|
|
389
|
-
if (setup.keystoreProperties) {
|
|
390
|
-
appEnvDeclarations.push(` - KEYSTORE_PROPERTIES: "" # Release signing — set via: ci secrets add KEYSTORE_PROPERTIES -w release\n`);
|
|
391
|
-
}
|
|
392
|
-
if (setup.googleServices) {
|
|
393
|
-
declare("GOOGLE_SERVICES_JSON", discriminators(setup.googleServicesPaths, "app/google-services.json"), (wf) => wf
|
|
394
|
-
? ` # Set via: ci secrets add GOOGLE_SERVICES_JSON -w ${wf}`
|
|
395
|
-
: ` # Set via: ci secrets add GOOGLE_SERVICES_JSON`);
|
|
396
|
-
}
|
|
397
|
-
if (setup.googlePlayDeploy) {
|
|
398
|
-
appEnvDeclarations.push(` - GOOGLE_PLAY_SERVICE_ACCOUNT_JSON: "" # Set via: ci secrets add GOOGLE_PLAY_SERVICE_ACCOUNT_JSON -w release\n`);
|
|
399
|
-
}
|
|
400
|
-
if (setup.secretsProperties) {
|
|
401
|
-
declare("LOCAL_PROPERTIES", discriminators(setup.workflowFlavors, ""), (wf) => wf
|
|
402
|
-
? ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES -w ${wf}`
|
|
403
|
-
: ` # Read by the Secrets Gradle Plugin — set via: ci secrets add LOCAL_PROPERTIES`);
|
|
404
|
-
}
|
|
405
|
-
const appEnv = appEnvDeclarations.join("");
|
|
406
|
-
/**
|
|
407
|
-
* The Secrets Gradle Plugin's properties file, written into every workflow
|
|
408
|
-
* that builds. Its evaluation listener runs for every variant, so a debug
|
|
409
|
-
* build fails to *configure* without it — the error names a module of the
|
|
410
|
-
* user's own project and nothing connects it to a missing credential.
|
|
411
|
-
*/
|
|
412
|
-
const secretsPropertiesStep = setup.secretsProperties
|
|
413
|
-
? `
|
|
414
|
-
- file@1.0.0:
|
|
415
|
-
title: Setup ${setup.secretsPropertiesPath}
|
|
416
|
-
is_skippable: true
|
|
417
|
-
inputs:
|
|
418
|
-
target_path: ${setup.secretsPropertiesPath}
|
|
419
|
-
var_name: LOCAL_PROPERTIES
|
|
420
|
-
`
|
|
421
|
-
: "";
|
|
422
|
-
/**
|
|
423
|
-
* Per workflow, exactly as the keystore is: the flavor a workflow builds
|
|
424
|
-
* decides where the file is written. A workflow whose variant names no
|
|
425
|
-
* flavor is unchanged — `app/google-services.json`.
|
|
426
|
-
*
|
|
427
|
-
* The `var_name` is the same in every workflow on purpose. Scoping a value
|
|
428
|
-
* to a workflow is the *secret's* job, not the step's: `CIBUILD_SW__<WF>__X`
|
|
429
|
-
* is decoded by `SecretsManager` and resolved by `EnvResolver` for the
|
|
430
|
-
* workflow being run, so one name here reads a different value there.
|
|
431
|
-
*/
|
|
432
|
-
const googleServicesStepFor = (wf) => {
|
|
433
|
-
if (!setup.googleServices)
|
|
434
|
-
return "";
|
|
435
|
-
const path = setup.googleServicesPaths[wf] ?? "app/google-services.json";
|
|
436
|
-
return `
|
|
437
|
-
- file@1.0.0:
|
|
438
|
-
is_skippable: true
|
|
439
|
-
inputs:
|
|
440
|
-
target_path: ${path}
|
|
441
|
-
var_name: GOOGLE_SERVICES_JSON
|
|
442
|
-
`;
|
|
443
|
-
};
|
|
444
|
-
const googlePlayStep = setup.googlePlayDeploy
|
|
445
|
-
? `
|
|
446
|
-
- file@1.0.0:
|
|
447
|
-
title: Setup Google Play Service Account
|
|
448
|
-
is_skippable: true
|
|
449
|
-
inputs:
|
|
450
|
-
target_path: .ci/keys/google-play-service-account.json
|
|
451
|
-
var_name: GOOGLE_PLAY_SERVICE_ACCOUNT_JSON
|
|
452
|
-
|
|
453
|
-
- google-play-deploy@1.0.0:
|
|
454
|
-
inputs:
|
|
455
|
-
service_account_json_key_path: .ci/keys/google-play-service-account.json
|
|
456
|
-
package_name: ${setup.googlePlayPackageName}
|
|
457
|
-
artifact_type: ${setup.artifactType}
|
|
458
|
-
track: internal
|
|
459
|
-
`
|
|
460
|
-
: "";
|
|
461
|
-
const keystorePropertiesStep = setup.keystoreProperties
|
|
462
|
-
? `
|
|
463
|
-
- script@1.0.0:
|
|
464
|
-
title: Setup keystore.properties
|
|
465
|
-
is_skippable: true
|
|
466
|
-
inputs:
|
|
467
|
-
content: |
|
|
468
|
-
if [ -z "\$KEYSTORE_PROPERTIES" ]; then
|
|
469
|
-
echo "KEYSTORE_PROPERTIES not set — skipping keystore.properties setup"
|
|
470
|
-
exit 0
|
|
471
|
-
fi
|
|
472
|
-
printf '%s' "\$KEYSTORE_PROPERTIES" > "\$PROJECT_LOCATION/keystore.properties"
|
|
473
|
-
echo "✓ keystore.properties created"
|
|
474
|
-
`
|
|
475
|
-
: "";
|
|
476
|
-
const { primary: pv, pullRequest: prv, release: rv } = variants;
|
|
477
|
-
// When deploying to Google Play as AAB, override the release gradle task and labels
|
|
478
|
-
const isAab = setup.googlePlayDeploy && setup.artifactType === 'aab';
|
|
479
|
-
const releaseGradleTask = isAab ? `bundle${rv.buildType}` : rv.gradleTask;
|
|
480
|
-
const releaseArtifactLabel = isAab ? 'AAB' : 'APK';
|
|
481
|
-
const releaseArtifactExt = isAab ? '*.aab' : '*.apk';
|
|
482
|
-
const stack = cacheTechnology === "kmm" ? "macos-xcode-26.4" : "linux-docker-android-22.04";
|
|
483
|
-
const platformMeta = cacheTechnology === "kmm" ? "kmm" : "android";
|
|
484
|
-
return `format_version: '1'
|
|
485
|
-
|
|
486
|
-
meta:
|
|
487
|
-
${metaNamespace}:
|
|
488
|
-
stack: ${stack}
|
|
489
|
-
machine_type: standard
|
|
490
|
-
platform: ${platformMeta}
|
|
491
|
-
|
|
492
|
-
app:
|
|
493
|
-
envs:
|
|
494
|
-
- PROJECT_LOCATION: .
|
|
495
|
-
- MODULE: app
|
|
496
|
-
- BUILD_TYPE: Debug
|
|
497
|
-
- GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.parallel=true
|
|
498
|
-
${appEnv}
|
|
499
|
-
workflows:
|
|
500
|
-
primary:
|
|
501
|
-
envs:
|
|
502
|
-
- VARIANT: ${pv.variant}
|
|
503
|
-
${buildTypeEnvOverride(pv)}${workflowEnvFor("primary")}
|
|
504
|
-
steps:
|
|
505
|
-
${keystoreStepFor("primary")}${googleServicesStepFor("primary")}${secretsPropertiesStep}
|
|
506
|
-
- set-java-version@1.0.0:
|
|
507
|
-
inputs:
|
|
508
|
-
java_version: '${javaVersion}'
|
|
509
|
-
|
|
510
|
-
- cache-pull@1.0.0:
|
|
511
|
-
inputs:
|
|
512
|
-
technology: ${cacheTechnology}
|
|
513
|
-
|
|
514
|
-
- android-lint@1.0.0:
|
|
515
|
-
is_skippable: true
|
|
516
|
-
inputs:
|
|
517
|
-
project_location: \$PROJECT_LOCATION
|
|
518
|
-
module: \$MODULE
|
|
519
|
-
variant: \$VARIANT
|
|
520
|
-
|
|
521
|
-
- gradle-build@1.0.0:
|
|
522
|
-
title: Build ${pv.buildType} APK
|
|
523
|
-
inputs:
|
|
524
|
-
project_location: \$PROJECT_LOCATION
|
|
525
|
-
build_type: ${pv.buildType}
|
|
526
|
-
gradle_task: ${pv.gradleTask}
|
|
527
|
-
gradle_options: --stacktrace
|
|
528
|
-
|
|
529
|
-
- android-unit-test@1.0.0:
|
|
530
|
-
is_skippable: true
|
|
531
|
-
inputs:
|
|
532
|
-
project_location: \$PROJECT_LOCATION
|
|
533
|
-
module: \$MODULE
|
|
534
|
-
variant: \$VARIANT
|
|
535
|
-
|
|
536
|
-
- cache-push@1.0.0:
|
|
537
|
-
inputs:
|
|
538
|
-
technology: ${cacheTechnology}
|
|
539
|
-
|
|
540
|
-
pull-request:
|
|
541
|
-
envs:
|
|
542
|
-
- VARIANT: ${prv.variant}
|
|
543
|
-
${buildTypeEnvOverride(prv)}${workflowEnvFor("pull-request")}
|
|
544
|
-
steps:
|
|
545
|
-
${keystoreStepFor("pull-request")}${googleServicesStepFor("pull-request")}${secretsPropertiesStep}
|
|
546
|
-
- set-java-version@1.0.0:
|
|
547
|
-
inputs:
|
|
548
|
-
java_version: '${javaVersion}'
|
|
549
|
-
|
|
550
|
-
- cache-pull@1.0.0:
|
|
551
|
-
inputs:
|
|
552
|
-
technology: ${cacheTechnology}
|
|
553
|
-
|
|
554
|
-
- android-lint@1.0.0:
|
|
555
|
-
is_skippable: true
|
|
556
|
-
inputs:
|
|
557
|
-
project_location: \$PROJECT_LOCATION
|
|
558
|
-
module: \$MODULE
|
|
559
|
-
variant: \$VARIANT
|
|
560
|
-
|
|
561
|
-
- android-unit-test@1.0.0:
|
|
562
|
-
is_skippable: true
|
|
563
|
-
inputs:
|
|
564
|
-
project_location: \$PROJECT_LOCATION
|
|
565
|
-
module: \$MODULE
|
|
566
|
-
variant: \$VARIANT
|
|
567
|
-
|
|
568
|
-
- gradle-build@1.0.0:
|
|
569
|
-
title: Build PR APK
|
|
570
|
-
inputs:
|
|
571
|
-
project_location: \$PROJECT_LOCATION
|
|
572
|
-
build_type: ${prv.buildType}
|
|
573
|
-
gradle_task: ${prv.gradleTask}
|
|
574
|
-
|
|
575
|
-
- cache-push@1.0.0:
|
|
576
|
-
inputs:
|
|
577
|
-
technology: ${cacheTechnology}
|
|
578
|
-
|
|
579
|
-
release:
|
|
580
|
-
envs:
|
|
581
|
-
- VARIANT: ${rv.variant}
|
|
582
|
-
${buildTypeEnvOverride(rv)}${workflowEnvFor("release")}
|
|
583
|
-
steps:
|
|
584
|
-
${keystoreStepFor("release")}${keystorePropertiesStep}${googleServicesStepFor("release")}${secretsPropertiesStep}
|
|
585
|
-
- set-java-version@1.0.0:
|
|
586
|
-
inputs:
|
|
587
|
-
java_version: '${javaVersion}'
|
|
588
|
-
|
|
589
|
-
- gradle-build@1.0.0:
|
|
590
|
-
title: Build ${rv.buildType} ${releaseArtifactLabel}
|
|
591
|
-
inputs:
|
|
592
|
-
project_location: \$PROJECT_LOCATION
|
|
593
|
-
build_type: ${rv.buildType}
|
|
594
|
-
gradle_task: ${releaseGradleTask}
|
|
595
|
-
gradle_options: --stacktrace
|
|
596
|
-
|
|
597
|
-
- android-unit-test@1.0.0:
|
|
598
|
-
is_skippable: true
|
|
599
|
-
inputs:
|
|
600
|
-
project_location: \$PROJECT_LOCATION
|
|
601
|
-
module: \$MODULE
|
|
602
|
-
variant: \$VARIANT
|
|
603
|
-
${googlePlayStep}
|
|
604
|
-
- script@1.0.0:
|
|
605
|
-
title: Build Summary
|
|
606
|
-
inputs:
|
|
607
|
-
content: |
|
|
608
|
-
echo "Build Complete"
|
|
609
|
-
if [ -d ".ci/artifacts" ]; then
|
|
610
|
-
find .ci/artifacts -name "${releaseArtifactExt}" | while read -r artifact; do
|
|
611
|
-
echo "${releaseArtifactLabel}: \$artifact"
|
|
612
|
-
done
|
|
613
|
-
fi
|
|
614
|
-
${triggerMapBlock(defaultBranch)}`;
|
|
615
|
-
}
|
|
616
|
-
export function generateIosPipeline(projectPath, setup, variants, metaNamespace = 'cibuild.io', defaultBranch = FALLBACK_DEFAULT_BRANCH) {
|
|
617
|
-
const { primary: pv, pullRequest: prv, release: rv } = variants;
|
|
618
|
-
// Global scheme is the primary scheme (used as default across workflows)
|
|
619
|
-
const globalScheme = pv.scheme;
|
|
620
|
-
// Only emit per-workflow scheme env if it differs from global
|
|
621
|
-
const schemeEnvFor = (scheme) => scheme !== globalScheme ? ` - SCHEME: ${scheme}\n` : "";
|
|
622
|
-
// Configuration override env (global default is Debug)
|
|
623
|
-
const configEnvFor = (cfg) => cfg !== "Debug" ? ` - CONFIGURATION: ${cfg}\n` : "";
|
|
624
|
-
// Optional env var stubs
|
|
625
|
-
const certEnv = setup.codeSigning
|
|
626
|
-
? ` - CERTIFICATE_P12: "" # Set via: ci secrets add CERTIFICATE_P12 -w release\n` +
|
|
627
|
-
` - CERTIFICATE_PASSWORD: "" # Set via: ci secrets add CERTIFICATE_PASSWORD -w release\n` +
|
|
628
|
-
` - PROVISIONING_PROFILE: "" # Set via: ci secrets add PROVISIONING_PROFILE -w release\n`
|
|
629
|
-
: "";
|
|
630
|
-
// CocoaPods install step
|
|
631
|
-
const podInstallStep = setup.cocoaPods
|
|
632
|
-
? `
|
|
633
|
-
- script@1.0.0:
|
|
634
|
-
title: Install CocoaPods
|
|
635
|
-
inputs:
|
|
636
|
-
content: |
|
|
637
|
-
pod install --repo-update
|
|
638
|
-
`
|
|
639
|
-
: "";
|
|
640
|
-
const podInstallStepPR = setup.cocoaPods
|
|
641
|
-
? `
|
|
642
|
-
- script@1.0.0:
|
|
643
|
-
title: Install CocoaPods
|
|
644
|
-
inputs:
|
|
645
|
-
content: |
|
|
646
|
-
pod install
|
|
647
|
-
`
|
|
648
|
-
: "";
|
|
649
|
-
// Code signing setup step (release only)
|
|
650
|
-
const codeSigningStep = setup.codeSigning
|
|
651
|
-
? `
|
|
652
|
-
- script@1.0.0:
|
|
653
|
-
title: Setup Code Signing
|
|
654
|
-
is_skippable: true
|
|
655
|
-
inputs:
|
|
656
|
-
content: |
|
|
657
|
-
if [ -z "\$CERTIFICATE_P12" ]; then
|
|
658
|
-
echo "CERTIFICATE_P12 not set — skipping code signing setup"
|
|
659
|
-
exit 0
|
|
660
|
-
fi
|
|
661
|
-
echo "\$CERTIFICATE_P12" | base64 --decode > /tmp/certificate.p12
|
|
662
|
-
security create-keychain -p "" ci-build.keychain
|
|
663
|
-
security import /tmp/certificate.p12 -k ci-build.keychain -P "\$CERTIFICATE_PASSWORD" -A -T /usr/bin/codesign
|
|
664
|
-
security list-keychains -s ci-build.keychain
|
|
665
|
-
security default-keychain -s ci-build.keychain
|
|
666
|
-
security unlock-keychain -p "" ci-build.keychain
|
|
667
|
-
security set-keychain-settings ci-build.keychain
|
|
668
|
-
if [ -n "\$PROVISIONING_PROFILE" ]; then
|
|
669
|
-
mkdir -p ~/Library/MobileDevice/Provisioning\\ Profiles
|
|
670
|
-
echo "\$PROVISIONING_PROFILE" | base64 --decode > ~/Library/MobileDevice/Provisioning\\ Profiles/profile.mobileprovision
|
|
671
|
-
echo "✓ Provisioning profile installed"
|
|
672
|
-
fi
|
|
673
|
-
rm -f /tmp/certificate.p12
|
|
674
|
-
echo "✓ Code signing setup complete"
|
|
675
|
-
`
|
|
676
|
-
: "";
|
|
677
|
-
// App Store Connect deploy step (release only)
|
|
678
|
-
const appStoreDeployStep = setup.appStoreDeploy
|
|
679
|
-
? `
|
|
680
|
-
- app-store-deploy@1.0.0:
|
|
681
|
-
inputs:
|
|
682
|
-
skip_metadata: 'yes'
|
|
683
|
-
skip_screenshots: 'yes'
|
|
684
|
-
submit_for_review: 'no'
|
|
685
|
-
`
|
|
686
|
-
: "";
|
|
687
|
-
// Cache technology depends on whether CocoaPods is used
|
|
688
|
-
const iosCacheTechnology = setup.cocoaPods ? "cocoapods" : "spm";
|
|
689
|
-
return `format_version: '1'
|
|
690
|
-
|
|
691
|
-
meta:
|
|
692
|
-
${metaNamespace}:
|
|
693
|
-
stack: macos-xcode-26.4
|
|
694
|
-
machine_type: performance
|
|
695
|
-
platform: ios
|
|
696
|
-
|
|
697
|
-
app:
|
|
698
|
-
envs:
|
|
699
|
-
- PROJECT_PATH: ${projectPath}
|
|
700
|
-
- SCHEME: ${globalScheme}
|
|
701
|
-
- CONFIGURATION: Debug
|
|
702
|
-
${certEnv}
|
|
703
|
-
workflows:
|
|
704
|
-
primary:
|
|
705
|
-
envs:
|
|
706
|
-
${schemeEnvFor(pv.scheme)}${configEnvFor(pv.configuration)}
|
|
707
|
-
steps:
|
|
708
|
-
- cache-pull@1.0.0:
|
|
709
|
-
inputs:
|
|
710
|
-
technology: ${iosCacheTechnology}
|
|
711
|
-
${podInstallStep}
|
|
712
|
-
- xcode-archive@1.0.0:
|
|
713
|
-
inputs:
|
|
714
|
-
project_path: \$PROJECT_PATH
|
|
715
|
-
scheme: \$SCHEME
|
|
716
|
-
configuration: \$CONFIGURATION
|
|
717
|
-
distribution_method: ${pv.distributionMethod}
|
|
718
|
-
output_dir: .ci/artifacts
|
|
719
|
-
|
|
720
|
-
- xcode-test@1.0.0:
|
|
721
|
-
is_skippable: true
|
|
722
|
-
inputs:
|
|
723
|
-
project_path: \$PROJECT_PATH
|
|
724
|
-
scheme: \$SCHEME
|
|
725
|
-
destination: platform=iOS Simulator,OS=latest
|
|
726
|
-
is_code_coverage_enabled: true
|
|
727
|
-
|
|
728
|
-
- cache-push@1.0.0:
|
|
729
|
-
inputs:
|
|
730
|
-
technology: ${iosCacheTechnology}
|
|
731
|
-
|
|
732
|
-
pull-request:
|
|
733
|
-
envs:
|
|
734
|
-
${schemeEnvFor(prv.scheme)}${configEnvFor(prv.configuration)}
|
|
735
|
-
steps:
|
|
736
|
-
- cache-pull@1.0.0:
|
|
737
|
-
inputs:
|
|
738
|
-
technology: ${iosCacheTechnology}
|
|
739
|
-
${podInstallStepPR}
|
|
740
|
-
- xcode-test@1.0.0:
|
|
741
|
-
is_skippable: true
|
|
742
|
-
inputs:
|
|
743
|
-
project_path: \$PROJECT_PATH
|
|
744
|
-
scheme: \$SCHEME
|
|
745
|
-
destination: platform=iOS Simulator,OS=latest
|
|
746
|
-
|
|
747
|
-
- xcodebuild@1.0.0:
|
|
748
|
-
inputs:
|
|
749
|
-
project_path: \$PROJECT_PATH
|
|
750
|
-
scheme: \$SCHEME
|
|
751
|
-
configuration: \$CONFIGURATION
|
|
752
|
-
destination: generic/platform=iOS Simulator
|
|
753
|
-
|
|
754
|
-
- cache-push@1.0.0:
|
|
755
|
-
inputs:
|
|
756
|
-
technology: ${iosCacheTechnology}
|
|
757
|
-
|
|
758
|
-
release:
|
|
759
|
-
envs:
|
|
760
|
-
- CONFIGURATION: Release
|
|
761
|
-
${schemeEnvFor(rv.scheme)}
|
|
762
|
-
steps:
|
|
763
|
-
${codeSigningStep}
|
|
764
|
-
- cache-pull@1.0.0:
|
|
765
|
-
inputs:
|
|
766
|
-
technology: ${iosCacheTechnology}
|
|
767
|
-
${podInstallStep}
|
|
768
|
-
- xcode-archive@1.0.0:
|
|
769
|
-
inputs:
|
|
770
|
-
project_path: \$PROJECT_PATH
|
|
771
|
-
scheme: \$SCHEME
|
|
772
|
-
configuration: \$CONFIGURATION
|
|
773
|
-
distribution_method: ${rv.distributionMethod}
|
|
774
|
-
perform_clean_action: 'yes'
|
|
775
|
-
automatic_code_signing: '${setup.codeSigning || setup.appStoreDeploy ? 'on' : 'off'}'
|
|
776
|
-
compile_bitcode: 'no'
|
|
777
|
-
upload_bitcode: 'no'
|
|
778
|
-
output_dir: .ci/artifacts
|
|
779
|
-
${appStoreDeployStep}
|
|
780
|
-
- script@1.0.0:
|
|
781
|
-
title: Build Summary
|
|
782
|
-
inputs:
|
|
783
|
-
content: |
|
|
784
|
-
echo "Build Complete"
|
|
785
|
-
if [ -n "\$CIBUILD_IPA_PATH" ]; then
|
|
786
|
-
echo "IPA: \$CIBUILD_IPA_PATH"
|
|
787
|
-
fi
|
|
788
|
-
if [ -n "\$CIBUILD_XCARCHIVE_PATH" ]; then
|
|
789
|
-
echo "Archive: \$CIBUILD_XCARCHIVE_PATH"
|
|
790
|
-
fi
|
|
791
|
-
${triggerMapBlock(defaultBranch)}`;
|
|
792
|
-
}
|
|
793
|
-
/**
|
|
794
|
-
* Prints a hint listing env vars / Gradle properties detected during scanning
|
|
795
|
-
* that haven't been handled by the setup flow (keystore, google-services, etc.).
|
|
796
|
-
* Prompts the user to add them as secrets before running the pipeline.
|
|
797
|
-
*/
|
|
798
|
-
function showMissingSecretsHint(scanResult, setupOptions) {
|
|
799
|
-
// Build a set of var names already configured by the setup flow
|
|
800
|
-
const alreadyHandled = new Set();
|
|
801
|
-
if (setupOptions.keystore)
|
|
802
|
-
alreadyHandled.add("KEYSTORE_BASE64");
|
|
803
|
-
if (setupOptions.keystoreProperties)
|
|
804
|
-
alreadyHandled.add("KEYSTORE_PROPERTIES");
|
|
805
|
-
if (setupOptions.googleServices)
|
|
806
|
-
alreadyHandled.add("GOOGLE_SERVICES_JSON");
|
|
807
|
-
// Only show actionable warnings that have a "ci secrets add VAR" hint
|
|
808
|
-
const ACTIONABLE = new Set(["build-config", "env-var", "signing-config", "gradle-property", "secrets-plugin"]);
|
|
809
|
-
const remaining = scanResult.warnings.filter((w) => {
|
|
810
|
-
if (!ACTIONABLE.has(w.category) || w.severity !== "warning")
|
|
811
|
-
return false;
|
|
812
|
-
const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1];
|
|
813
|
-
return varName && !alreadyHandled.has(varName);
|
|
814
|
-
});
|
|
815
|
-
if (remaining.length === 0)
|
|
816
|
-
return;
|
|
817
|
-
const LABELS = {
|
|
818
|
-
"build-config": "BuildConfig env vars",
|
|
819
|
-
"env-var": "Environment variables",
|
|
820
|
-
"signing-config": "Signing credentials",
|
|
821
|
-
"gradle-property": "Gradle properties",
|
|
822
|
-
"secrets-plugin": "Google Secrets Gradle Plugin (local.properties keys)",
|
|
823
|
-
};
|
|
824
|
-
// Group by category — secrets-plugin keys are grouped separately and shown with a file hint
|
|
825
|
-
const byCategory = new Map();
|
|
826
|
-
for (const w of remaining) {
|
|
827
|
-
if (!byCategory.has(w.category))
|
|
828
|
-
byCategory.set(w.category, []);
|
|
829
|
-
byCategory.get(w.category).push(w);
|
|
830
|
-
}
|
|
831
|
-
console.log("\n──────────────────────────────────────────────────");
|
|
832
|
-
console.log("⚠ Additional secrets detected in your Gradle files");
|
|
833
|
-
console.log("──────────────────────────────────────────────────");
|
|
834
|
-
console.log(" These variables were found but not yet configured.\n");
|
|
835
|
-
for (const [cat, items] of byCategory) {
|
|
836
|
-
console.log(` ${LABELS[cat] ?? cat}:`);
|
|
837
|
-
if (cat === "secrets-plugin") {
|
|
838
|
-
// These all go into a single properties file — show as a block with a file-step hint
|
|
839
|
-
const propertiesFile = items[0]?.location ?? "local.properties";
|
|
840
|
-
console.log(` → These keys are read from '${propertiesFile}' by the Secrets Gradle Plugin.`);
|
|
841
|
-
console.log(` → The file is gitignored — inject it as a file step before building.\n`);
|
|
842
|
-
console.log(` Suggested step (add via: ci edit → Add step → file):`);
|
|
843
|
-
console.log(` target_path: ${propertiesFile}`);
|
|
844
|
-
console.log(` var_name: <SECRET_NAME_HOLDING_FILE_CONTENT>\n`);
|
|
845
|
-
console.log(` Keys needed in '${propertiesFile}':`);
|
|
846
|
-
for (const w of items) {
|
|
847
|
-
const key = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
|
|
848
|
-
console.log(` ${key}`);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
else {
|
|
852
|
-
for (const w of items) {
|
|
853
|
-
const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
|
|
854
|
-
const loc = w.location ? ` (${w.location})` : "";
|
|
855
|
-
console.log(` ci secrets add ${varName}${loc}`);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
console.log("");
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
async function handleIosBuildCommand(cwd, options = {}) {
|
|
862
|
-
// 1. Scan the project
|
|
863
|
-
console.log("\n🔍 Scanning iOS project for potential unknowns...\n");
|
|
864
|
-
const iosScanResult = await scanIosProject(cwd);
|
|
865
|
-
console.log(formatIosScanResult(iosScanResult));
|
|
866
|
-
// 2. Determine available schemes
|
|
867
|
-
const detectedSchemes = iosScanResult.detectedSchemes.length > 0
|
|
868
|
-
? iosScanResult.detectedSchemes
|
|
869
|
-
: ["MyApp"];
|
|
870
|
-
if (iosScanResult.detectedSchemes.length > 1) {
|
|
871
|
-
console.log(`ℹ Detected schemes: ${iosScanResult.detectedSchemes.join(", ")}\n`);
|
|
872
|
-
}
|
|
873
|
-
// 3. Prompt for scheme + configuration + distribution method per workflow
|
|
874
|
-
const ni = options.nonInteractive;
|
|
875
|
-
if (!ni)
|
|
876
|
-
console.log("⚙ Configure build schemes and configurations\n");
|
|
877
|
-
const primaryScheme = ni ? detectedSchemes[0] : await promptScheme("primary", detectedSchemes[0], detectedSchemes);
|
|
878
|
-
const primaryConfig = ni ? "Debug" : await promptConfiguration("primary", "Debug");
|
|
879
|
-
const primaryMethod = ni ? "development" : await promptDistributionMethod("primary", "development");
|
|
880
|
-
const pullRequestScheme = ni ? detectedSchemes[0] : await promptScheme("pull-request", primaryScheme, detectedSchemes);
|
|
881
|
-
const pullRequestConfig = ni ? "Debug" : await promptConfiguration("pull-request", "Debug");
|
|
882
|
-
const pullRequestMethod = ni ? "development" : await promptDistributionMethod("pull-request", "development");
|
|
883
|
-
const releaseScheme = ni ? detectedSchemes[0] : await promptScheme("release", primaryScheme, detectedSchemes);
|
|
884
|
-
const releaseConfig = ni ? "Release" : await promptConfiguration("release", "Release");
|
|
885
|
-
const releaseMethod = ni ? "app-store" : await promptDistributionMethod("release", "app-store");
|
|
886
|
-
const iosVariants = {
|
|
887
|
-
primary: { scheme: primaryScheme, configuration: primaryConfig, distributionMethod: primaryMethod },
|
|
888
|
-
pullRequest: { scheme: pullRequestScheme, configuration: pullRequestConfig, distributionMethod: pullRequestMethod },
|
|
889
|
-
release: { scheme: releaseScheme, configuration: releaseConfig, distributionMethod: releaseMethod },
|
|
890
|
-
};
|
|
891
|
-
// 4. Setup options
|
|
892
|
-
const iosSetupOptions = {
|
|
893
|
-
cocoaPods: false,
|
|
894
|
-
codeSigning: false,
|
|
895
|
-
appStoreDeploy: false,
|
|
896
|
-
bundleId: "",
|
|
897
|
-
};
|
|
898
|
-
const secretsManager = new SecretsManager();
|
|
899
|
-
// CocoaPods
|
|
900
|
-
if (iosScanResult.hasCocoaPods) {
|
|
901
|
-
if (ni) {
|
|
902
|
-
iosSetupOptions.cocoaPods = true;
|
|
903
|
-
}
|
|
904
|
-
else {
|
|
905
|
-
const { create } = await prompts({
|
|
906
|
-
type: "confirm",
|
|
907
|
-
name: "create",
|
|
908
|
-
message: "Generate a pod install step for CocoaPods dependencies?",
|
|
909
|
-
initial: true,
|
|
910
|
-
});
|
|
911
|
-
iosSetupOptions.cocoaPods = !!create;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
// Code signing
|
|
915
|
-
if (iosScanResult.hasSigningConfig) {
|
|
916
|
-
if (ni) {
|
|
917
|
-
iosSetupOptions.codeSigning = true;
|
|
918
|
-
}
|
|
919
|
-
else {
|
|
920
|
-
const { create } = await prompts({
|
|
921
|
-
type: "confirm",
|
|
922
|
-
name: "create",
|
|
923
|
-
message: "Generate a code signing setup step for the release workflow?",
|
|
924
|
-
initial: true,
|
|
925
|
-
});
|
|
926
|
-
iosSetupOptions.codeSigning = !!create;
|
|
927
|
-
}
|
|
928
|
-
if (iosSetupOptions.codeSigning) {
|
|
929
|
-
await collectFileSecret({
|
|
930
|
-
displayName: "certificate (.p12)",
|
|
931
|
-
secretName: "CERTIFICATE_P12",
|
|
932
|
-
fileMatch: (name) => name.endsWith(".p12"),
|
|
933
|
-
readAs: "base64",
|
|
934
|
-
workflowGroups: [{ label: "release", workflows: ["release"] }],
|
|
935
|
-
}, cwd, secretsManager, ni);
|
|
936
|
-
if (ni) {
|
|
937
|
-
console.log(" Add later: ci secrets add CERTIFICATE_PASSWORD -w release");
|
|
938
|
-
}
|
|
939
|
-
else {
|
|
940
|
-
const { password } = await prompts({
|
|
941
|
-
type: "password",
|
|
942
|
-
name: "password",
|
|
943
|
-
message: "Certificate password (leave blank to add later):",
|
|
944
|
-
});
|
|
945
|
-
if (password?.trim()) {
|
|
946
|
-
secretsManager.storeSecret("CERTIFICATE_PASSWORD", password.trim(), "release");
|
|
947
|
-
console.log(" ✅ CERTIFICATE_PASSWORD stored for workflow: release");
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
console.log(" Add later: ci secrets add CERTIFICATE_PASSWORD -w release");
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
await collectFileSecret({
|
|
954
|
-
displayName: "provisioning profile (.mobileprovision)",
|
|
955
|
-
secretName: "PROVISIONING_PROFILE",
|
|
956
|
-
fileMatch: (name) => name.endsWith(".mobileprovision"),
|
|
957
|
-
readAs: "base64",
|
|
958
|
-
workflowGroups: [{ label: "release", workflows: ["release"] }],
|
|
959
|
-
}, cwd, secretsManager, ni);
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
// App Store Connect deploy — skip in non-interactive mode (requires credentials)
|
|
963
|
-
if (!ni) {
|
|
964
|
-
const { deployToAppStore } = await prompts({
|
|
965
|
-
type: "confirm",
|
|
966
|
-
name: "deployToAppStore",
|
|
967
|
-
message: "Add App Store Connect / TestFlight upload step to release workflow?",
|
|
968
|
-
initial: false,
|
|
969
|
-
});
|
|
970
|
-
iosSetupOptions.appStoreDeploy = !!deployToAppStore;
|
|
971
|
-
if (iosSetupOptions.appStoreDeploy) {
|
|
972
|
-
await collectFileSecret({
|
|
973
|
-
displayName: "App Store Connect API key (.p8)",
|
|
974
|
-
secretName: "APPLE_API_KEY_PATH",
|
|
975
|
-
fileMatch: (name) => name.endsWith(".p8"),
|
|
976
|
-
readAs: "base64",
|
|
977
|
-
workflowGroups: [{ label: "release", workflows: ["release"] }],
|
|
978
|
-
}, cwd, secretsManager);
|
|
979
|
-
const { keyId } = await prompts({
|
|
980
|
-
type: "text",
|
|
981
|
-
name: "keyId",
|
|
982
|
-
message: "Apple API Key ID (leave blank to add later):",
|
|
983
|
-
});
|
|
984
|
-
if (keyId?.trim()) {
|
|
985
|
-
secretsManager.storeSecret("APPLE_API_KEY_ID", keyId.trim(), "release");
|
|
986
|
-
console.log(" ✅ APPLE_API_KEY_ID stored for workflow: release");
|
|
987
|
-
}
|
|
988
|
-
else {
|
|
989
|
-
console.log(" Add later: ci secrets add APPLE_API_KEY_ID -w release");
|
|
990
|
-
}
|
|
991
|
-
const { issuerId } = await prompts({
|
|
992
|
-
type: "text",
|
|
993
|
-
name: "issuerId",
|
|
994
|
-
message: "Apple API Issuer ID (leave blank to add later):",
|
|
995
|
-
});
|
|
996
|
-
if (issuerId?.trim()) {
|
|
997
|
-
secretsManager.storeSecret("APPLE_API_ISSUER_ID", issuerId.trim(), "release");
|
|
998
|
-
console.log(" ✅ APPLE_API_ISSUER_ID stored for workflow: release");
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
console.log(" Add later: ci secrets add APPLE_API_ISSUER_ID -w release");
|
|
1002
|
-
}
|
|
1003
|
-
const detectedTeamId = iosScanResult.developmentTeam;
|
|
1004
|
-
let teamIdToStore = detectedTeamId;
|
|
1005
|
-
if (detectedTeamId) {
|
|
1006
|
-
console.log(` ℹ Detected Apple Team ID from project: ${detectedTeamId}`);
|
|
1007
|
-
secretsManager.storeSecret("APPLE_TEAM_ID", detectedTeamId, "release");
|
|
1008
|
-
console.log(" ✅ APPLE_TEAM_ID stored for workflow: release");
|
|
1009
|
-
}
|
|
1010
|
-
else {
|
|
1011
|
-
const { teamId } = await prompts({
|
|
1012
|
-
type: "text",
|
|
1013
|
-
name: "teamId",
|
|
1014
|
-
message: "Apple Team ID (leave blank to add later):",
|
|
1015
|
-
});
|
|
1016
|
-
teamIdToStore = teamId?.trim() || "";
|
|
1017
|
-
if (teamIdToStore) {
|
|
1018
|
-
secretsManager.storeSecret("APPLE_TEAM_ID", teamIdToStore, "release");
|
|
1019
|
-
console.log(" ✅ APPLE_TEAM_ID stored for workflow: release");
|
|
1020
|
-
}
|
|
1021
|
-
else {
|
|
1022
|
-
console.log(" Add later: ci secrets add APPLE_TEAM_ID -w release");
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
// 5. Ensure .ci/pipelines/ exists
|
|
1028
|
-
const pipelinesDir = resolve(cwd, ".ci", "pipelines");
|
|
1029
|
-
if (!existsSync(pipelinesDir)) {
|
|
1030
|
-
if (options.createPipelinesDir) {
|
|
1031
|
-
mkdirSync(pipelinesDir, { recursive: true });
|
|
1032
|
-
console.log("✅ Created .ci/pipelines/");
|
|
1033
|
-
}
|
|
1034
|
-
else {
|
|
1035
|
-
console.error("Project not initialised. Run 'ci init' first.");
|
|
1036
|
-
process.exit(1);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
// 6. Check if cibuild.yml already exists
|
|
1040
|
-
const outputPath = resolve(pipelinesDir, "cibuild.yml");
|
|
1041
|
-
if (existsSync(outputPath) && !ni) {
|
|
1042
|
-
const { overwrite } = await prompts({
|
|
1043
|
-
type: "confirm",
|
|
1044
|
-
name: "overwrite",
|
|
1045
|
-
message: ".ci/pipelines/cibuild.yml already exists. Overwrite?",
|
|
1046
|
-
initial: false,
|
|
1047
|
-
});
|
|
1048
|
-
if (!overwrite) {
|
|
1049
|
-
console.log("\nCancelled. Existing pipeline was not modified.");
|
|
1050
|
-
process.exit(0);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
// 7. Generate and write the pipeline
|
|
1054
|
-
const yaml = generateIosPipeline(iosScanResult.projectPath, iosSetupOptions, iosVariants, options.metaNamespace, detectDefaultBranch());
|
|
1055
|
-
writeFileSync(outputPath, yaml, "utf-8");
|
|
1056
|
-
console.log("\n✅ Generated .ci/pipelines/cibuild.yml");
|
|
1057
|
-
console.log(" Platform: iOS");
|
|
1058
|
-
console.log(` Workflows: primary (${iosVariants.primary.scheme}/${iosVariants.primary.configuration}), pull-request (${iosVariants.pullRequest.scheme}/${iosVariants.pullRequest.configuration}), release (${iosVariants.release.scheme}/${iosVariants.release.configuration})`);
|
|
1059
|
-
if (iosSetupOptions.cocoaPods || iosSetupOptions.codeSigning || iosSetupOptions.appStoreDeploy) {
|
|
1060
|
-
console.log(" Setup steps included:");
|
|
1061
|
-
if (iosSetupOptions.cocoaPods)
|
|
1062
|
-
console.log(" • Install CocoaPods (pod install)");
|
|
1063
|
-
if (iosSetupOptions.codeSigning)
|
|
1064
|
-
console.log(" • Setup Code Signing (CERTIFICATE_P12, PROVISIONING_PROFILE) — release only");
|
|
1065
|
-
if (iosSetupOptions.appStoreDeploy)
|
|
1066
|
-
console.log(" • App Store Connect / TestFlight upload (app-store-deploy) — release only");
|
|
1067
|
-
}
|
|
1068
|
-
// Generate GitHub Actions workflow
|
|
1069
|
-
let githubWorkflowGenerated = false;
|
|
1070
|
-
if (options.createPipelinesDir) {
|
|
1071
|
-
githubWorkflowGenerated = generateGitHubActionsWorkflow({ platform: "ios", cwd });
|
|
1072
|
-
}
|
|
1073
|
-
// Show env var secrets hint
|
|
1074
|
-
const envVarWarnings = iosScanResult.warnings.filter((w) => w.category === "env-var" && w.severity === "warning");
|
|
1075
|
-
if (envVarWarnings.length > 0) {
|
|
1076
|
-
console.log("\n──────────────────────────────────────────────────");
|
|
1077
|
-
console.log("⚠ Additional secrets detected in your xcconfig files");
|
|
1078
|
-
console.log("──────────────────────────────────────────────────");
|
|
1079
|
-
console.log(" These variables were found but not yet configured.\n");
|
|
1080
|
-
console.log(" Environment variables:");
|
|
1081
|
-
for (const w of envVarWarnings) {
|
|
1082
|
-
const varName = w.hint?.match(/ci secrets add (\S+)/)?.[1] ?? w.message;
|
|
1083
|
-
const loc = w.location ? ` (${w.location})` : "";
|
|
1084
|
-
console.log(` ci secrets add ${varName}${loc}`);
|
|
1085
|
-
}
|
|
1086
|
-
console.log("");
|
|
1087
|
-
}
|
|
1088
|
-
const hasSecrets = existsSync(resolve(cwd, ".cibuild-secrets.json"));
|
|
1089
|
-
const hasWorkflow = existsSync(resolve(cwd, ".github", "workflows", "ci.yml"));
|
|
1090
|
-
console.log("\nNext steps:");
|
|
1091
|
-
console.log(" ci run .ci/pipelines/cibuild.yml -w primary # Run locally");
|
|
1092
|
-
if (hasWorkflow) {
|
|
1093
|
-
if (hasSecrets) {
|
|
1094
|
-
console.log(" ci secrets upload # Upload secrets to GitHub");
|
|
1095
|
-
}
|
|
1096
|
-
console.log(" git add . && git push # Push to GitHub - CI runs automatically");
|
|
1097
|
-
}
|
|
1098
|
-
console.log("");
|
|
1099
|
-
}
|
|
1100
|
-
export async function handleBuildCommand(detectMobileProjectRoot, options = {}) {
|
|
1101
|
-
const cwd = process.cwd();
|
|
1102
|
-
// 1. Detect project type
|
|
1103
|
-
const projectType = detectMobileProjectRoot(cwd);
|
|
1104
|
-
if (projectType === "ios") {
|
|
1105
|
-
await handleIosBuildCommand(cwd, options);
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
if (projectType === "kmm" || projectType === "android") {
|
|
1109
|
-
await handleAndroidBuildCommand(cwd, options, projectType === "kmm" ? "kmm" : "gradle");
|
|
1110
|
-
return;
|
|
1111
|
-
}
|
|
1112
|
-
console.error("Error: Not a mobile project root.");
|
|
1113
|
-
console.error("'ci build' must be run from the root folder of an Android, iOS, or KMM project.");
|
|
1114
|
-
console.error("\nAndroid/KMM projects must contain one of:");
|
|
1115
|
-
console.error(" build.gradle, build.gradle.kts, settings.gradle, settings.gradle.kts, gradlew");
|
|
1116
|
-
console.error("\nKMM projects additionally need: iosApp/, shared/, or composeApp/ directory");
|
|
1117
|
-
console.error("\niOS projects must contain one of:");
|
|
1118
|
-
console.error(" *.xcodeproj, *.xcworkspace, Podfile");
|
|
1119
|
-
process.exit(1);
|
|
1120
|
-
}
|
|
1121
|
-
async function handleAndroidBuildCommand(cwd, options, cacheTechnology) {
|
|
1122
|
-
const platformLabel = cacheTechnology === "kmm" ? "KMM (Kotlin Multiplatform)" : "Android";
|
|
1123
|
-
// 2. Scan the project for potential unknowns
|
|
1124
|
-
console.log(`\n🔍 Scanning ${platformLabel} project for potential unknowns...\n`);
|
|
1125
|
-
const scanResult = await scanAndroidProject(cwd);
|
|
1126
|
-
console.log(formatScanResult(scanResult));
|
|
1127
|
-
// 3. Prompt for build variants per workflow
|
|
1128
|
-
const ni = options.nonInteractive;
|
|
1129
|
-
const { variants: detectedVariants, productFlavors } = scanResult.buildVariants;
|
|
1130
|
-
if (productFlavors.length > 0) {
|
|
1131
|
-
console.log(`ℹ Detected product flavors: ${productFlavors.join(", ")}`);
|
|
1132
|
-
console.log(` Available variants: ${detectedVariants.join(", ")}\n`);
|
|
1133
|
-
}
|
|
1134
|
-
let variants;
|
|
1135
|
-
if (ni) {
|
|
1136
|
-
variants = DEFAULT_VARIANTS;
|
|
1137
|
-
}
|
|
1138
|
-
else {
|
|
1139
|
-
console.log("⚙ Configure build variants\n");
|
|
1140
|
-
const primaryVariant = await promptVariant("primary", "debug", detectedVariants);
|
|
1141
|
-
const pullRequestVariant = await promptVariant("pull-request", "debug", detectedVariants);
|
|
1142
|
-
const releaseVariant = await promptVariant("release", "release", detectedVariants);
|
|
1143
|
-
variants = {
|
|
1144
|
-
primary: primaryVariant,
|
|
1145
|
-
pullRequest: pullRequestVariant,
|
|
1146
|
-
release: releaseVariant,
|
|
1147
|
-
};
|
|
1148
|
-
}
|
|
1149
|
-
// 4. Prompt to generate setup steps for detected file issues
|
|
1150
|
-
const setupNeeds = detectSetupNeeds(scanResult.warnings);
|
|
1151
|
-
// Which flavor each workflow builds decides where its google-services.json
|
|
1152
|
-
// is written, and — with no path to give it away — whether the properties
|
|
1153
|
-
// file can be one value or has to be one per workflow. Both maps are
|
|
1154
|
-
// derived from the variants chosen just above.
|
|
1155
|
-
const googleServicesPaths = googleServicesTargets(variants, productFlavors);
|
|
1156
|
-
const workflowFlavors = variantFlavors(variants, productFlavors);
|
|
1157
|
-
const setupOptions = { keystore: false, keystoreProperties: false, googleServices: false, googlePlayDeploy: false, googlePlayPackageName: '', artifactType: 'apk', keystorePaths: {}, googleServicesPaths, workflowFlavors, secretsProperties: false, secretsPropertiesPath: setupNeeds.secretsPropertiesPath };
|
|
1158
|
-
const secretsManager = new SecretsManager();
|
|
1159
|
-
const keystoreWorkflowGroups = [
|
|
1160
|
-
{ label: "debug builds (primary, pull-request)", workflows: ["primary", "pull-request"] },
|
|
1161
|
-
{ label: "release", workflows: ["release"] },
|
|
1162
|
-
];
|
|
1163
|
-
if (setupNeeds.keystore) {
|
|
1164
|
-
if (ni) {
|
|
1165
|
-
setupOptions.keystore = true;
|
|
1166
|
-
}
|
|
1167
|
-
else {
|
|
1168
|
-
const { create } = await prompts({
|
|
1169
|
-
type: "confirm",
|
|
1170
|
-
name: "create",
|
|
1171
|
-
message: "Generate a setup script step for the keystore?",
|
|
1172
|
-
initial: true,
|
|
1173
|
-
});
|
|
1174
|
-
setupOptions.keystore = !!create;
|
|
1175
|
-
}
|
|
1176
|
-
if (setupOptions.keystore) {
|
|
1177
|
-
setupOptions.keystorePaths = await collectFileSecret({
|
|
1178
|
-
displayName: "keystore",
|
|
1179
|
-
secretName: "KEYSTORE_BASE64",
|
|
1180
|
-
fileMatch: (name) => name.endsWith(".jks") || name.endsWith(".keystore"),
|
|
1181
|
-
readAs: "base64",
|
|
1182
|
-
workflowGroups: keystoreWorkflowGroups,
|
|
1183
|
-
promptTargetPath: true,
|
|
1184
|
-
}, cwd, secretsManager, ni);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
if (setupNeeds.keystoreProperties) {
|
|
1188
|
-
if (ni) {
|
|
1189
|
-
setupOptions.keystoreProperties = true;
|
|
1190
|
-
}
|
|
1191
|
-
else {
|
|
1192
|
-
const { create } = await prompts({
|
|
1193
|
-
type: "confirm",
|
|
1194
|
-
name: "create",
|
|
1195
|
-
message: "Generate a setup script step for keystore.properties (release signing)?",
|
|
1196
|
-
initial: true,
|
|
1197
|
-
});
|
|
1198
|
-
setupOptions.keystoreProperties = !!create;
|
|
1199
|
-
}
|
|
1200
|
-
if (setupOptions.keystoreProperties) {
|
|
1201
|
-
await collectFileSecret({
|
|
1202
|
-
displayName: "keystore.properties",
|
|
1203
|
-
secretName: "KEYSTORE_PROPERTIES",
|
|
1204
|
-
fileMatch: (name) => name === "keystore.properties",
|
|
1205
|
-
readAs: "text",
|
|
1206
|
-
workflowGroups: [
|
|
1207
|
-
{ label: "release", workflows: ["release"] },
|
|
1208
|
-
],
|
|
1209
|
-
}, cwd, secretsManager, ni);
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
// Resolve missing keystore target paths for all configured workflows.
|
|
1213
|
-
// For each workflow with no file-selected path: extract storeFile from any
|
|
1214
|
-
// KEYSTORE_PROPERTIES secret, then fall back to Gradle-detected paths.
|
|
1215
|
-
if (setupOptions.keystore) {
|
|
1216
|
-
const gradleKeystorePaths = scanResult.warnings
|
|
1217
|
-
.filter((w) => w.category === "missing-file" && w.message.startsWith("Keystore file not found:"))
|
|
1218
|
-
.map((w) => w.message.replace("Keystore file not found: ", ""));
|
|
1219
|
-
for (const wf of keystoreWorkflowGroups.flatMap((g) => g.workflows)) {
|
|
1220
|
-
if (setupOptions.keystorePaths[wf])
|
|
1221
|
-
continue;
|
|
1222
|
-
const kpContent = secretsManager.getSecret("KEYSTORE_PROPERTIES", wf);
|
|
1223
|
-
if (kpContent) {
|
|
1224
|
-
const line = kpContent.split("\n").find((l) => l.trim().startsWith("storeFile="));
|
|
1225
|
-
if (line) {
|
|
1226
|
-
setupOptions.keystorePaths[wf] = line.split("=").slice(1).join("=").trim();
|
|
1227
|
-
continue;
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
if (gradleKeystorePaths.length > 0) {
|
|
1231
|
-
setupOptions.keystorePaths[wf] = gradleKeystorePaths[0];
|
|
1232
|
-
}
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
if (setupNeeds.googleServices) {
|
|
1236
|
-
if (ni) {
|
|
1237
|
-
setupOptions.googleServices = true;
|
|
1238
|
-
}
|
|
1239
|
-
else {
|
|
1240
|
-
const { create } = await prompts({
|
|
1241
|
-
type: "confirm",
|
|
1242
|
-
name: "create",
|
|
1243
|
-
message: "Generate a setup script step for google-services.json?",
|
|
1244
|
-
initial: true,
|
|
1245
|
-
});
|
|
1246
|
-
setupOptions.googleServices = !!create;
|
|
1247
|
-
}
|
|
1248
|
-
if (setupOptions.googleServices) {
|
|
1249
|
-
// One collection per distinct target path. Workflows building the same
|
|
1250
|
-
// flavor write to the same place and are collected together; workflows
|
|
1251
|
-
// building different flavors need different files, so each is asked
|
|
1252
|
-
// separately and pinned to its own source set — which is what lets a
|
|
1253
|
-
// flavored repository auto-select rather than guess.
|
|
1254
|
-
//
|
|
1255
|
-
// The secret name is the same every time, and the *value* is stored per
|
|
1256
|
-
// workflow. That is what `SecretsManager` has always done, what
|
|
1257
|
-
// `ci secrets upload` encodes as `CIBUILD_SW__<WF>__<VAR>`, and what
|
|
1258
|
-
// `EnvResolver` resolves for the workflow being run.
|
|
1259
|
-
const byTarget = collectionGroups(discriminators(setupOptions.googleServicesPaths, "app/google-services.json"), (_path, workflows) => workflows.join(", "));
|
|
1260
|
-
for (const group of byTarget) {
|
|
1261
|
-
const flavorDir = /\/src\/([^/]+)\//.exec(group.discriminator)?.[1] ?? null;
|
|
1262
|
-
await collectFileSecret({
|
|
1263
|
-
displayName: flavorDir
|
|
1264
|
-
? `google-services.json for the ${flavorDir} flavor`
|
|
1265
|
-
: "google-services.json",
|
|
1266
|
-
secretName: "GOOGLE_SERVICES_JSON",
|
|
1267
|
-
fileMatch: (name) => name === "google-services.json",
|
|
1268
|
-
// A flavored target takes the copy from that flavor's source set. An
|
|
1269
|
-
// unflavored one takes anything that is *not* a flavor's copy, so
|
|
1270
|
-
// the module root wins rather than an arbitrary source set.
|
|
1271
|
-
pathMatch: flavorDir
|
|
1272
|
-
? (rel) => rel.includes(`/src/${flavorDir}/`) || rel.startsWith(`src/${flavorDir}/`)
|
|
1273
|
-
: (rel) => !/(?:^|\/)src\/[^/]+\//.test(rel),
|
|
1274
|
-
readAs: "text",
|
|
1275
|
-
// One group per call, not all of them: each group narrows the search
|
|
1276
|
-
// to its own flavor's source set, which is what lets a flavored
|
|
1277
|
-
// repository auto-select rather than ask.
|
|
1278
|
-
workflowGroups: [group],
|
|
1279
|
-
}, cwd, secretsManager, ni);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
if (setupNeeds.secretsProperties) {
|
|
1284
|
-
const propertiesFile = setupNeeds.secretsPropertiesPath;
|
|
1285
|
-
if (ni) {
|
|
1286
|
-
setupOptions.secretsProperties = true;
|
|
1287
|
-
}
|
|
1288
|
-
else {
|
|
1289
|
-
const { create } = await prompts({
|
|
1290
|
-
type: "confirm",
|
|
1291
|
-
name: "create",
|
|
1292
|
-
message: `Generate a setup step for '${propertiesFile}' (read by the Secrets Gradle Plugin)?`,
|
|
1293
|
-
initial: true,
|
|
1294
|
-
});
|
|
1295
|
-
setupOptions.secretsProperties = !!create;
|
|
1296
|
-
}
|
|
1297
|
-
if (setupOptions.secretsProperties) {
|
|
1298
|
-
// Every workflow, not just release: the plugin's evaluation listener
|
|
1299
|
-
// runs for every variant, so a debug build cannot configure without it.
|
|
1300
|
-
//
|
|
1301
|
-
// And one group per flavor, because the plugin turns this file into
|
|
1302
|
-
// BuildConfig and a flavor is an environment. Every group in one call,
|
|
1303
|
-
// unlike google-services.json above: there is no per-flavor path
|
|
1304
|
-
// convention for a properties file to narrow the search by, so the
|
|
1305
|
-
// groups differ only in which answer they take.
|
|
1306
|
-
await collectFileSecret({
|
|
1307
|
-
displayName: propertiesFile,
|
|
1308
|
-
secretName: "LOCAL_PROPERTIES",
|
|
1309
|
-
fileMatch: (name) => name === propertiesFile.split("/").pop(),
|
|
1310
|
-
readAs: "text",
|
|
1311
|
-
workflowGroups: propertiesFileGroups(setupOptions.workflowFlavors),
|
|
1312
|
-
}, cwd, secretsManager, ni);
|
|
1313
|
-
}
|
|
1314
|
-
}
|
|
1315
|
-
// 4b. Prompt for Google Play deployment setup — skip in non-interactive mode (requires package name + credentials)
|
|
1316
|
-
if (!ni) {
|
|
1317
|
-
const { setupGooglePlay } = await prompts({
|
|
1318
|
-
type: "confirm",
|
|
1319
|
-
name: "setupGooglePlay",
|
|
1320
|
-
message: "Add a Google Play deploy step to the release workflow?",
|
|
1321
|
-
initial: false,
|
|
1322
|
-
});
|
|
1323
|
-
if (setupGooglePlay) {
|
|
1324
|
-
const { packageName } = await prompts({
|
|
1325
|
-
type: "text",
|
|
1326
|
-
name: "packageName",
|
|
1327
|
-
message: "Android package name (e.g. com.example.app):",
|
|
1328
|
-
validate: (v) => (v.trim().length > 0 ? true : "Package name is required"),
|
|
1329
|
-
});
|
|
1330
|
-
setupOptions.googlePlayDeploy = true;
|
|
1331
|
-
setupOptions.googlePlayPackageName = packageName?.trim() || '';
|
|
1332
|
-
const { artifactType } = await prompts({
|
|
1333
|
-
type: "select",
|
|
1334
|
-
name: "artifactType",
|
|
1335
|
-
message: "Artifact format for Google Play upload:",
|
|
1336
|
-
choices: [
|
|
1337
|
-
{ title: "AAB — Android App Bundle (recommended by Google Play)", value: "aab" },
|
|
1338
|
-
{ title: "APK — traditional APK format", value: "apk" },
|
|
1339
|
-
],
|
|
1340
|
-
initial: 0,
|
|
1341
|
-
});
|
|
1342
|
-
setupOptions.artifactType = artifactType || 'aab';
|
|
1343
|
-
await collectFileSecret({
|
|
1344
|
-
displayName: "Google Play service account JSON",
|
|
1345
|
-
secretName: "GOOGLE_PLAY_SERVICE_ACCOUNT_JSON",
|
|
1346
|
-
fileMatch: (name) => name.endsWith(".json"),
|
|
1347
|
-
readAs: "text",
|
|
1348
|
-
workflowGroups: [
|
|
1349
|
-
{ label: "release", workflows: ["release"] },
|
|
1350
|
-
],
|
|
1351
|
-
}, cwd, secretsManager);
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
// 5. Ensure .ci/pipelines/ exists
|
|
1355
|
-
const pipelinesDir = resolve(cwd, ".ci", "pipelines");
|
|
1356
|
-
if (!existsSync(pipelinesDir)) {
|
|
1357
|
-
if (options.createPipelinesDir) {
|
|
1358
|
-
mkdirSync(pipelinesDir, { recursive: true });
|
|
1359
|
-
console.log("✅ Created .ci/pipelines/");
|
|
1360
|
-
}
|
|
1361
|
-
else {
|
|
1362
|
-
console.error("Project not initialised. Run 'ci init' first.");
|
|
1363
|
-
process.exit(1);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
// 6. Check if cibuild.yml already exists
|
|
1367
|
-
const outputPath = resolve(pipelinesDir, "cibuild.yml");
|
|
1368
|
-
if (existsSync(outputPath) && !ni) {
|
|
1369
|
-
const { overwrite } = await prompts({
|
|
1370
|
-
type: "confirm",
|
|
1371
|
-
name: "overwrite",
|
|
1372
|
-
message: ".ci/pipelines/cibuild.yml already exists. Overwrite?",
|
|
1373
|
-
initial: false,
|
|
1374
|
-
});
|
|
1375
|
-
if (!overwrite) {
|
|
1376
|
-
console.log("\nCancelled. Existing pipeline was not modified.");
|
|
1377
|
-
process.exit(0);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
// 7. Generate and write the pipeline
|
|
1381
|
-
//
|
|
1382
|
-
// The chooser abstains rather than falling back, so `undefined` here means
|
|
1383
|
-
// "no installed JDK meets this project's constraints" — not "17 is fine".
|
|
1384
|
-
// The default is what gets written in that case, and the warning below is
|
|
1385
|
-
// what says so; it is never silent when the answer is not what was asked
|
|
1386
|
-
// for, which is the whole reason the two are resolved together.
|
|
1387
|
-
const javaVersion = runnableJavaVersion(scanResult.detectedJavaVersion, scanResult.detectedGradleVersion);
|
|
1388
|
-
const javaProblem = javaVersionProblem(scanResult.detectedJavaVersion, scanResult.detectedGradleVersion, scanResult.detectedJavaVersionSource);
|
|
1389
|
-
if (javaProblem)
|
|
1390
|
-
console.log(`\n⚠️ ${javaProblem}`);
|
|
1391
|
-
const yaml = generateAndroidPipeline(javaVersion ?? DEFAULT_PIPELINE_JAVA_VERSION, setupOptions, variants, cacheTechnology, options.metaNamespace, detectDefaultBranch());
|
|
1392
|
-
writeFileSync(outputPath, yaml, "utf-8");
|
|
1393
|
-
console.log("\n✅ Generated .ci/pipelines/cibuild.yml");
|
|
1394
|
-
console.log(` Platform: ${platformLabel}`);
|
|
1395
|
-
console.log(` Workflows: primary (${variants.primary.variant}), pull-request (${variants.pullRequest.variant}), release (${variants.release.variant})`);
|
|
1396
|
-
if (setupOptions.keystore || setupOptions.keystoreProperties || setupOptions.googleServices || setupOptions.googlePlayDeploy) {
|
|
1397
|
-
console.log(" Setup steps included:");
|
|
1398
|
-
if (setupOptions.keystore)
|
|
1399
|
-
console.log(" • Setup Keystore (KEYSTORE_BASE64)");
|
|
1400
|
-
if (setupOptions.keystoreProperties)
|
|
1401
|
-
console.log(" • Setup keystore.properties (KEYSTORE_PROPERTIES) — release only");
|
|
1402
|
-
if (setupOptions.googleServices) {
|
|
1403
|
-
const seen = new Set();
|
|
1404
|
-
for (const wf of GENERATED_WORKFLOWS) {
|
|
1405
|
-
const path = setupOptions.googleServicesPaths[wf];
|
|
1406
|
-
if (!path || seen.has(path))
|
|
1407
|
-
continue;
|
|
1408
|
-
seen.add(path);
|
|
1409
|
-
console.log(` • Setup ${path} (GOOGLE_SERVICES_JSON)`);
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
if (setupOptions.googlePlayDeploy)
|
|
1413
|
-
console.log(` • Google Play deploy (${setupOptions.googlePlayPackageName}) — release only`);
|
|
1414
|
-
}
|
|
1415
|
-
showMissingSecretsHint(scanResult, setupOptions);
|
|
1416
|
-
// Generate GitHub Actions workflow
|
|
1417
|
-
let githubWorkflowGenerated = false;
|
|
1418
|
-
if (options.createPipelinesDir) {
|
|
1419
|
-
githubWorkflowGenerated = generateGitHubActionsWorkflow({ platform: "android", cwd });
|
|
1420
|
-
}
|
|
1421
|
-
const hasSecrets = existsSync(resolve(cwd, ".cibuild-secrets.json"));
|
|
1422
|
-
const hasWorkflow = existsSync(resolve(cwd, ".github", "workflows", "ci.yml"));
|
|
1423
|
-
console.log("\nNext steps:");
|
|
1424
|
-
console.log(" ci run .ci/pipelines/cibuild.yml -w primary # Run locally");
|
|
1425
|
-
if (hasWorkflow) {
|
|
1426
|
-
if (hasSecrets) {
|
|
1427
|
-
console.log(" ci secrets upload # Upload secrets to GitHub");
|
|
1428
|
-
}
|
|
1429
|
-
console.log(" git add . && git push # Push to GitHub - CI runs automatically");
|
|
1430
|
-
}
|
|
1431
|
-
console.log("");
|
|
1432
|
-
}
|
|
1433
|
-
//# sourceMappingURL=build.js.map
|