@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,1027 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cache step implementations (cache-pull and cache-push)
|
|
3
|
-
*/
|
|
4
|
-
import { BaseStepExecutor } from './base.js';
|
|
5
|
-
/**
|
|
6
|
-
* Files that together define a Gradle/KMM build graph. Hashing these catches
|
|
7
|
-
* module additions/removals, plugin changes, and version-catalog bumps — things
|
|
8
|
-
* that invalidate reusability of the `~/.gradle/caches` contents across branches.
|
|
9
|
-
* Source file edits do NOT affect this hash; Gradle's task cache inside the
|
|
10
|
-
* tarball handles source-level reuse.
|
|
11
|
-
*/
|
|
12
|
-
const GRADLE_FINGERPRINT = [
|
|
13
|
-
'settings.gradle',
|
|
14
|
-
'settings.gradle.kts',
|
|
15
|
-
'build.gradle',
|
|
16
|
-
'build.gradle.kts',
|
|
17
|
-
'libs.versions.toml',
|
|
18
|
-
'gradle-wrapper.properties',
|
|
19
|
-
];
|
|
20
|
-
export const CACHE_PRESETS = {
|
|
21
|
-
ios: { lockfile: 'Podfile.lock', keyPrefix: 'pods', paths: ['Pods'], fallback: 'spm' },
|
|
22
|
-
cocoapods: { lockfile: 'Podfile.lock', keyPrefix: 'pods', paths: ['Pods'] },
|
|
23
|
-
carthage: { lockfile: 'Cartfile.resolved', keyPrefix: 'carthage', paths: ['Carthage'] },
|
|
24
|
-
spm: { lockfile: 'Package.resolved', keyPrefix: 'spm', paths: ['~/Library/Developer/Xcode/DerivedData/*/SourcePackages'] },
|
|
25
|
-
gradle: { fingerprint: GRADLE_FINGERPRINT, keyPrefix: 'gradle', paths: ['~/.gradle/caches', '~/.gradle/wrapper/dists'] },
|
|
26
|
-
kmm: { fingerprint: GRADLE_FINGERPRINT, keyPrefix: 'kmm', paths: ['~/.gradle/caches', '~/.gradle/wrapper/dists', '~/.konan'] },
|
|
27
|
-
npm: { lockfile: 'package-lock.json', keyPrefix: 'npm', paths: ['node_modules'] },
|
|
28
|
-
yarn: { lockfile: 'yarn.lock', keyPrefix: 'yarn', paths: ['node_modules'] },
|
|
29
|
-
dart: { lockfile: 'pubspec.lock', keyPrefix: 'dart', paths: ['.dart_tool', '.pub-cache'] },
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Emits bash that derives a stable short project id from the git remote URL
|
|
33
|
-
* (with embedded credentials stripped, so token rotations don't churn the key).
|
|
34
|
-
* Sets $__ci_proj to an 8-char hex. Falls back to "no-remote" if no git remote.
|
|
35
|
-
*/
|
|
36
|
-
function emitProjectScopeCommands() {
|
|
37
|
-
return [
|
|
38
|
-
'__ci_git_remote=$(git config --get remote.origin.url 2>/dev/null | sed -E "s#(://)[^@/]+@#\\1#" || echo "no-remote")',
|
|
39
|
-
// `shasum` is Perl-provided and is not guaranteed on a slim Linux image;
|
|
40
|
-
// `sha256sum` is coreutils and is absent from macOS. Both print
|
|
41
|
-
// `<hex> -`, so either yields the same 8-char project id — which is what
|
|
42
|
-
// keeps this byte-identical to `deriveProjectId` in the runner.
|
|
43
|
-
'__ci_sha256() { if command -v shasum >/dev/null 2>&1; then shasum -a 256; else sha256sum; fi; }',
|
|
44
|
-
'__ci_proj=$(printf "%s" "${__ci_git_remote:-no-remote}" | __ci_sha256 | cut -c1-8)',
|
|
45
|
-
];
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Emits bash that discovers fingerprint filenames across the repo, sorts them,
|
|
49
|
-
* and hashes their concatenated names+contents into CACHE_KEY.
|
|
50
|
-
* Key format: <keyPrefix>-<projectId>-<fingerprint>, so tarballs are scoped
|
|
51
|
-
* per-repo and fallback globbing can't cross-contaminate between projects.
|
|
52
|
-
* Used by presets (Gradle, KMM) where no single lockfile captures the build graph.
|
|
53
|
-
*/
|
|
54
|
-
function emitFingerprintKeyCommands(preset) {
|
|
55
|
-
const fp = preset.fingerprint ?? [];
|
|
56
|
-
const escape = (s) => s.replace(/'/g, "'\\''");
|
|
57
|
-
const nameArgs = fp
|
|
58
|
-
.map((n, i) => `${i === 0 ? '' : '-o '}-name '${escape(n)}'`)
|
|
59
|
-
.join(' ');
|
|
60
|
-
return [
|
|
61
|
-
`__ci_fp_files=$(find . -type f \\( ${nameArgs} \\) \\`,
|
|
62
|
-
` -not -path '*/build/*' \\`,
|
|
63
|
-
` -not -path '*/.gradle/*' \\`,
|
|
64
|
-
` -not -path '*/node_modules/*' \\`,
|
|
65
|
-
` -not -path '*/DerivedData/*' \\`,
|
|
66
|
-
` 2>/dev/null | LC_ALL=C sort)`,
|
|
67
|
-
`if [ -n "$__ci_fp_files" ]; then`,
|
|
68
|
-
` CHECKSUM=$(echo "$__ci_fp_files" | xargs shasum -a 256 2>/dev/null | shasum -a 256 | cut -c1-16)`,
|
|
69
|
-
` CACHE_KEY="${escape(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`,
|
|
70
|
-
`else`,
|
|
71
|
-
` echo "Warning: no fingerprint files found, using fallback cache key"`,
|
|
72
|
-
` CACHE_KEY="${escape(preset.keyPrefix)}-\${__ci_proj}-no-fingerprint"`,
|
|
73
|
-
`fi`,
|
|
74
|
-
];
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Resolves a preset with its fallback chain into a flat list of
|
|
78
|
-
* {lockfile, keyPrefix, paths} entries to try in order at runtime.
|
|
79
|
-
*/
|
|
80
|
-
function resolvePresetChain(technology) {
|
|
81
|
-
const chain = [];
|
|
82
|
-
let current = technology;
|
|
83
|
-
const visited = new Set();
|
|
84
|
-
while (current && !visited.has(current)) {
|
|
85
|
-
visited.add(current);
|
|
86
|
-
const preset = CACHE_PRESETS[current];
|
|
87
|
-
if (!preset)
|
|
88
|
-
break;
|
|
89
|
-
chain.push(preset);
|
|
90
|
-
current = preset.fallback;
|
|
91
|
-
}
|
|
92
|
-
return chain;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Resolves the `identifier:pattern` shorthand a `cache_paths` entry may use
|
|
96
|
-
* (`derived_data:MyApp-*`, `builds:nightly`) into a real path. Anything else is
|
|
97
|
-
* returned unchanged.
|
|
98
|
-
*
|
|
99
|
-
* Shared because the two sides used to disagree: `cache-push` resolved these
|
|
100
|
-
* and `cache-pull` did not, so the classification below would have put a
|
|
101
|
-
* `derived_data:` entry in the project-relative class on the way out and the
|
|
102
|
-
* absolute class on the way in.
|
|
103
|
-
*/
|
|
104
|
-
function resolveCachePathIdentifier(path, config) {
|
|
105
|
-
if (!path.includes(':'))
|
|
106
|
-
return path;
|
|
107
|
-
const [identifier, pattern] = path.split(':', 2);
|
|
108
|
-
switch (identifier) {
|
|
109
|
-
case 'derived_data':
|
|
110
|
-
return `${config.paths.derivedDataDir}/${pattern}`;
|
|
111
|
-
case 'builds':
|
|
112
|
-
return `${config.paths.buildsDir}/${pattern}`;
|
|
113
|
-
default:
|
|
114
|
-
return path;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Splits `cache_paths` into the two classes that decide where a member has to
|
|
119
|
-
* be extracted to.
|
|
120
|
-
*
|
|
121
|
-
* `cache-push` archives whatever `PATHS_TO_CACHE` holds. A `~`- or `/`-rooted
|
|
122
|
-
* entry is expanded to an absolute path and tar stores it with the leading `/`
|
|
123
|
-
* stripped (`/home/builder/.gradle/caches` -> `home/builder/.gradle/caches`),
|
|
124
|
-
* which round-trips through `-C /`. A project-relative entry (`Pods`,
|
|
125
|
-
* `.gradle`, `node_modules`) is stored as-is, relative to the step's working
|
|
126
|
-
* directory — and `-C /` then tries to create it at the root of the
|
|
127
|
-
* filesystem, which the build user cannot do.
|
|
128
|
-
*
|
|
129
|
-
* So the class is decided by the declared form, not by anything in the tarball,
|
|
130
|
-
* and both steps can compute it from the same input.
|
|
131
|
-
*/
|
|
132
|
-
function classifyCachePaths(paths, config) {
|
|
133
|
-
const absolute = [];
|
|
134
|
-
const relative = [];
|
|
135
|
-
for (const raw of paths) {
|
|
136
|
-
const path = resolveCachePathIdentifier(raw, config);
|
|
137
|
-
if (path.startsWith('~') || path.startsWith('/')) {
|
|
138
|
-
absolute.push(path);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
relative.push(path);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return { absolute, relative };
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Emits `__ci_cache_extract`, which every restore path pipes its tar stream
|
|
148
|
-
* into, plus the two member lists it reads.
|
|
149
|
-
*
|
|
150
|
-
* **The defect it closes.** Every restore used one `tar -xf - -C /`. For a
|
|
151
|
-
* `cache_paths` that mixes the two classes — which every Gradle/Android
|
|
152
|
-
* pipeline does, including the one the README ships — the absolute half landed
|
|
153
|
-
* (that *was* the warmth) and the relative half could not, so tar exited
|
|
154
|
-
* non-zero, the compound condition went false, and a build that had just been
|
|
155
|
-
* restored reported `CACHE_SOURCE=cold`. Worse, a pipeline whose paths are
|
|
156
|
-
* *entirely* project-relative (`Pods`, `Podfile.lock`, `node_modules`) had
|
|
157
|
-
* every member fail: those caches were written on every build and had never
|
|
158
|
-
* restored anything. Measured on GNU tar 1.35 and bsdtar 3.5.3 alike.
|
|
159
|
-
*
|
|
160
|
-
* On the filesystem transport the same failure was not a mis-report at all —
|
|
161
|
-
* the extraction is a bare command there, so under `set -e -o pipefail` it
|
|
162
|
-
* ended the step.
|
|
163
|
-
*
|
|
164
|
-
* **Cost.** A restore with no project-relative paths keeps today's single
|
|
165
|
-
* streaming pass and stages nothing, so the shape that already worked pays
|
|
166
|
-
* nothing. The mixed and all-relative shapes stage the tar and read it twice,
|
|
167
|
-
* which is the price of extracting two roots from one stream.
|
|
168
|
-
*
|
|
169
|
-
* Written for bash 3.2 — macOS ships it, and the generated script says
|
|
170
|
-
* `#!/bin/bash`.
|
|
171
|
-
*/
|
|
172
|
-
function emitCacheExtractHelper(paths, config, indent = '') {
|
|
173
|
-
const { absolute, relative } = classifyCachePaths(paths, config);
|
|
174
|
-
const lit = (s) => `'${s.replace(/'/g, "'\\''")}'`;
|
|
175
|
-
const out = [
|
|
176
|
-
// Captured before anything can cd: project-relative members are recorded
|
|
177
|
-
// against the step's working directory, so that is where they go back.
|
|
178
|
-
`${indent}__ci_cache_root="$PWD"`,
|
|
179
|
-
`${indent}__ci_cache_abs=()`,
|
|
180
|
-
`${indent}__ci_cache_rel=()`,
|
|
181
|
-
];
|
|
182
|
-
for (const p of absolute) {
|
|
183
|
-
// Expanded at runtime — `~` means ${CIBUILD_USER_HOME:-$HOME}, which the
|
|
184
|
-
// generator cannot know. The leading `/` is stripped to match how tar
|
|
185
|
-
// recorded the member.
|
|
186
|
-
out.push(`${indent}__ci_p=${lit(p)}`);
|
|
187
|
-
out.push(`${indent}__ci_p="\${__ci_p/#~/\${CIBUILD_USER_HOME:-$HOME}}"`);
|
|
188
|
-
out.push(`${indent}__ci_cache_abs+=("\${__ci_p#/}")`);
|
|
189
|
-
}
|
|
190
|
-
for (const p of relative) {
|
|
191
|
-
out.push(`${indent}__ci_cache_rel+=(${lit(p)})`);
|
|
192
|
-
}
|
|
193
|
-
out.push(`${indent}__ci_cache_extract() {`,
|
|
194
|
-
// Nothing project-relative: the archive is entirely absolute-derived and
|
|
195
|
-
// one streaming pass is both correct and cheapest.
|
|
196
|
-
`${indent} if [ \${#__ci_cache_rel[@]} -eq 0 ]; then`, `${indent} tar -xf - -C /`, `${indent} return`, `${indent} fi`, `${indent} local __t="\${TMPDIR:-/tmp}/cibuild-cache-extract.$$.tar"`, `${indent} local __rc=0 __top __c __is_rel __p`, `${indent} local __abs_ops __rel_ops`, `${indent} cat > "$__t" || { rm -f "$__t"; return 1; }`,
|
|
197
|
-
// The split is decided on the archive's own top-level names, not by
|
|
198
|
-
// `--exclude`. tar's exclude patterns are unanchored: `--exclude=.gradle`
|
|
199
|
-
// also matches `home/builder/.gradle`, so excluding the project-relative
|
|
200
|
-
// half from the pass that targets `/` silently dropped the absolute half
|
|
201
|
-
// too whenever the two shared a component name — which the canonical
|
|
202
|
-
// Gradle shape (`~/.gradle/caches` plus `.gradle`) always does.
|
|
203
|
-
`${indent} __top=$(tar -tf "$__t" 2>/dev/null | sed -e 's|^\\./||' -e 's|/.*||' | grep -v '^$' | sort -u) || __rc=$?`, `${indent} if [ "$__rc" -ne 0 ]; then rm -f "$__t"; return "$__rc"; fi`, `${indent} __abs_ops=()`, `${indent} __rel_ops=()`,
|
|
204
|
-
// Read rather than word-split: a cached path may contain spaces
|
|
205
|
-
// (`derived_data:My App-*`). Process substitution keeps the loop in this
|
|
206
|
-
// shell, so the arrays it fills survive it.
|
|
207
|
-
`${indent} while IFS= read -r __c; do`, `${indent} __is_rel=0`, `${indent} for __p in "\${__ci_cache_rel[@]}"; do`, `${indent} case "$__p" in "$__c"|"$__c"/*) __is_rel=1 ;; esac`, `${indent} done`,
|
|
208
|
-
// A name claimed by both classes resolves to `/`, which is what it did
|
|
209
|
-
// before this existed. It needs a project directory named after a
|
|
210
|
-
// filesystem root while also caching an absolute path under it.
|
|
211
|
-
`${indent} if [ "$__is_rel" -eq 1 ]; then __rel_ops+=("$__c"); else __abs_ops+=("$__c"); fi`, `${indent} done < <(printf '%s\\n' "$__top")`, `${indent} if [ \${#__abs_ops[@]} -gt 0 ]; then`, `${indent} tar -xf "$__t" -C / "\${__abs_ops[@]}" || __rc=$?`, `${indent} fi`, `${indent} if [ "$__rc" -eq 0 ] && [ \${#__rel_ops[@]} -gt 0 ]; then`, `${indent} tar -xf "$__t" -C "$__ci_cache_root" "\${__rel_ops[@]}" || __rc=$?`, `${indent} fi`, `${indent} rm -f "$__t"`, `${indent} return "$__rc"`, `${indent}}`);
|
|
212
|
-
return out;
|
|
213
|
-
}
|
|
214
|
-
/** The token `%{http_code}` is reported under; see `daemonCurl`. */
|
|
215
|
-
const DAEMON_STATUS_TOKEN = 'ci_http_status';
|
|
216
|
-
/**
|
|
217
|
-
* Emits the `curl` invocation the cache steps talk to the daemon with. The
|
|
218
|
-
* bearer token is per-build and only present on the Invarn runner, so it is
|
|
219
|
-
* expanded conditionally and the same script works without one.
|
|
220
|
-
*
|
|
221
|
-
* Every request also reports the HTTP status it got, on **stderr** — the
|
|
222
|
-
* response body is the tarball and goes to stdout, so a status written there
|
|
223
|
-
* would corrupt it. `%{stderr}` is curl 7.63+ (2018); an older curl prints the
|
|
224
|
-
* token literally instead of a number, which the reader below tolerates.
|
|
225
|
-
*
|
|
226
|
-
* `-f`, not `--fail-with-body`: on the pull side the error body would be piped
|
|
227
|
-
* straight into `zstd`. The status is what distinguishes the answers anyway,
|
|
228
|
-
* and it now arrives without the body.
|
|
229
|
-
*/
|
|
230
|
-
function daemonCurl(args) {
|
|
231
|
-
return `curl -fsS -w '%{stderr}${DAEMON_STATUS_TOKEN}=%{http_code}\\n' \${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"} ${args}`;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Emitted once inside a daemon pull branch, before the first fetch: a file for
|
|
235
|
-
* curl's stderr, and the reader that turns it into one line — or into nothing
|
|
236
|
-
* at all when the daemon simply had no such entry.
|
|
237
|
-
*
|
|
238
|
-
* `{ curl | zstd | tar; } 2>/dev/null` used to swallow curl's message
|
|
239
|
-
* entirely, so a 401, a 400, a 503, a 500 and a daemon that was not listening
|
|
240
|
-
* all printed the same `No cache found for key: …` as an empty cache. That is
|
|
241
|
-
* the same class as the push-side defect: the instrument reported the normal
|
|
242
|
-
* case for every abnormal one.
|
|
243
|
-
*
|
|
244
|
-
* **A miss stays quiet and a failure gets loud.** A cold cache is the normal
|
|
245
|
-
* case and must not start printing errors; the point is only that a broken
|
|
246
|
-
* daemon stops looking like one.
|
|
247
|
-
*/
|
|
248
|
-
function emitDaemonPullDiagnostics(indent = '') {
|
|
249
|
-
return [
|
|
250
|
-
`${indent}__ci_cache_diag="\${TMPDIR:-/tmp}/cibuild-cache-pull.$$.diag"`,
|
|
251
|
-
`${indent}__ci_cache_daemon_said() {`,
|
|
252
|
-
`${indent} local __said=''`,
|
|
253
|
-
`${indent} if [ -s "$__ci_cache_diag" ]; then`,
|
|
254
|
-
`${indent} __said=$(tr '\\n' ' ' < "$__ci_cache_diag")`,
|
|
255
|
-
`${indent} fi`,
|
|
256
|
-
`${indent} case "$__said" in`,
|
|
257
|
-
// 404 is the daemon's answer for `cache_miss` and `no_cache_for_scope` —
|
|
258
|
-
// an empty cache, which is normal. The second pattern reads curl's own
|
|
259
|
-
// English for a curl too old to honour `%{stderr}`, so neither reader
|
|
260
|
-
// failing alone can turn a cold cache noisy.
|
|
261
|
-
`${indent} *${DAEMON_STATUS_TOKEN}=404*|*'error: 404'*) __said='' ;;`,
|
|
262
|
-
`${indent} esac`,
|
|
263
|
-
// Always exits 0. A non-zero return would be the assignment's status at
|
|
264
|
-
// every call site, and under errexit that ends the step on a cache miss.
|
|
265
|
-
`${indent} printf '%s' "\${__said:0:300}"`,
|
|
266
|
-
`${indent}}`,
|
|
267
|
-
];
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* The `else` arm of a daemon fetch: says why it did not restore, unless the
|
|
271
|
-
* daemon simply had no such entry. Never fatal — a build that cannot read a
|
|
272
|
-
* cache still builds, it just builds cold.
|
|
273
|
-
*/
|
|
274
|
-
function emitDaemonPullFailureDiagnosis(indent, what) {
|
|
275
|
-
return [
|
|
276
|
-
`${indent}__ci_cache_why=$(__ci_cache_daemon_said)`,
|
|
277
|
-
`${indent}if [ -n "$__ci_cache_why" ]; then`,
|
|
278
|
-
`${indent} echo "Warning: cache-pull could not restore ${what} from the daemon — $__ci_cache_why"`,
|
|
279
|
-
`${indent}fi`,
|
|
280
|
-
];
|
|
281
|
-
}
|
|
282
|
-
/**
|
|
283
|
-
* The same request, keeping the response body when the daemon refuses.
|
|
284
|
-
*
|
|
285
|
-
* `-f` throws the body away, so a rejection reached the log as an exit code
|
|
286
|
-
* and nothing else. The daemon distinguishes `invalid_cache_key` (400), a
|
|
287
|
-
* retryable `cache_busy` (503) and an auth failure (401); all three arrived
|
|
288
|
-
* as the same `curl: (22)` line under the same warning, which is how a
|
|
289
|
-
* one-line bug stayed open behind it. Push only — on the pull side the body
|
|
290
|
-
* would be piped into `zstd`.
|
|
291
|
-
*
|
|
292
|
-
* `--fail-with-body` is curl 7.76+ (2021); every runner image is well past
|
|
293
|
-
* it. An older curl rejects the option and the step still degrades to
|
|
294
|
-
* today's behaviour, with the reason in the message.
|
|
295
|
-
*/
|
|
296
|
-
function daemonCurlKeepingErrorBody(args) {
|
|
297
|
-
return `curl --fail-with-body -sS \${CIBUILD_CACHE_TOKEN:+-H "Authorization: Bearer $CIBUILD_CACHE_TOKEN"} ${args}`;
|
|
298
|
-
}
|
|
299
|
-
/**
|
|
300
|
-
* The daemon half of `cache-push`, shared by both step forms.
|
|
301
|
-
*
|
|
302
|
-
* Shared because it did not used to be. The two forms carried byte-identical
|
|
303
|
-
* copies of this block, and the explicit-key one reached it without ever
|
|
304
|
-
* assigning the `CACHE_KEY` it interpolates — the preset form assigned it,
|
|
305
|
-
* the other only assigned `CACHE_FILE`. There is no `set -u`, so the PUT went
|
|
306
|
-
* to `<daemon>/cache/.tar.zst`, the daemon answered 400, and the step
|
|
307
|
-
* reported success. Every curated Invarn template uses the explicit-key form,
|
|
308
|
-
* so nothing was ever stored by any of them. One definition cannot diverge
|
|
309
|
-
* from itself.
|
|
310
|
-
*
|
|
311
|
-
* The failure message names the stage that failed, which took a second fix.
|
|
312
|
-
* `$( … 2>&1 )` captured curl's stdout, and `--fail-with-body` writes the
|
|
313
|
-
* daemon's response body there on success as well as on refusal — so when the
|
|
314
|
-
* *middle* stage died (no `zstd` on the image), pipefail took the else branch
|
|
315
|
-
* and printed the daemon's **success** body as the reason the upload failed:
|
|
316
|
-
*
|
|
317
|
-
* Warning: failed to upload cache to the daemon — {"ok":true}
|
|
318
|
-
*
|
|
319
|
-
* No stream separates those two cases, because the body is on stdout either
|
|
320
|
-
* way; only the stage's exit status does. Hence `PIPESTATUS` rather than the
|
|
321
|
-
* pull side's diag-file split — the file is still here, but it is the daemon's
|
|
322
|
-
* answer, and it is quoted only when curl is what refused. A rejection's
|
|
323
|
-
* reason still reaches the log, which `cache-wire-test.sh` asserts.
|
|
324
|
-
*/
|
|
325
|
-
function emitDaemonPushCommands() {
|
|
326
|
-
return [
|
|
327
|
-
// curl's own stdout and stderr, kept out of the pipeline's status. Mirrors
|
|
328
|
-
// the pull side's `$__ci_cache_diag`.
|
|
329
|
-
' __ci_push_out="${TMPDIR:-/tmp}/cibuild-cache-push.$$.out"',
|
|
330
|
-
` if tar -cf - "\${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 | ${daemonCurlKeepingErrorBody('-T - "$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst"')} >"$__ci_push_out" 2>&1; then`,
|
|
331
|
-
' echo "Cache created successfully"',
|
|
332
|
-
' else',
|
|
333
|
-
// Read first, before anything else in this branch: PIPESTATUS holds the
|
|
334
|
-
// last pipeline's statuses and the next command replaces it.
|
|
335
|
-
' __ci_push_rc=("${PIPESTATUS[@]}")',
|
|
336
|
-
// Never fatal: a build that produced good output should not be failed by
|
|
337
|
-
// a cache upload, and the next build simply misses. Saying *why* costs
|
|
338
|
-
// nothing and is the difference between a warning and a diagnosis.
|
|
339
|
-
' __ci_push_msg=""',
|
|
340
|
-
' if [ "${__ci_push_rc[2]}" != 0 ]; then',
|
|
341
|
-
// The daemon refused, so the daemon's own answer is the reason —
|
|
342
|
-
// `invalid_cache_key`, `cache_busy`, `empty_body`, an auth failure.
|
|
343
|
-
// `[ -s ]` guards the substitution: an assignment whose command
|
|
344
|
-
// substitution fails is a non-zero statement, and under errexit that ends
|
|
345
|
-
// the step.
|
|
346
|
-
' if [ -s "$__ci_push_out" ]; then',
|
|
347
|
-
" __ci_push_msg=$(tr '\\n' ' ' < \"$__ci_push_out\")",
|
|
348
|
-
' fi',
|
|
349
|
-
' elif [ "${__ci_push_rc[1]}" = 127 ]; then',
|
|
350
|
-
// curl was happy — it uploaded whatever reached it, which was nothing.
|
|
351
|
-
// 127 is the shell failing to find the command, and zstd itself never
|
|
352
|
-
// exits 127, so this is unambiguous and worth saying outright.
|
|
353
|
-
' __ci_push_msg="zstd is not installed on this image, so nothing was uploaded"',
|
|
354
|
-
' else',
|
|
355
|
-
' __ci_push_msg="nothing was uploaded — tar exited ${__ci_push_rc[0]}, zstd exited ${__ci_push_rc[1]}"',
|
|
356
|
-
' fi',
|
|
357
|
-
' __ci_push_msg=${__ci_push_msg:0:300}',
|
|
358
|
-
' if [ -n "$__ci_push_msg" ]; then',
|
|
359
|
-
' echo "Warning: failed to upload cache to the daemon — $__ci_push_msg"',
|
|
360
|
-
' else',
|
|
361
|
-
' echo "Warning: failed to upload cache to the daemon"',
|
|
362
|
-
' fi',
|
|
363
|
-
' fi',
|
|
364
|
-
' rm -f "$__ci_push_out"',
|
|
365
|
-
];
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Restores a cache through the daemon rather than a shared directory.
|
|
369
|
-
*
|
|
370
|
-
* Two requests, mirroring the directory-based flow it replaces: the exact key
|
|
371
|
-
* first, then — on a miss — the scope's newest tarball. The daemon resolves
|
|
372
|
-
* each from its own disk or a tailnet peer, so a hit here covers what used to
|
|
373
|
-
* be separate "local" and "peer" branches; which one it was is recorded in the
|
|
374
|
-
* daemon's own host-side log, where the fleet-wide view already lives.
|
|
375
|
-
*
|
|
376
|
-
* The 30-day age cap is preserved: the daemon dates the fallback with
|
|
377
|
-
* `mtimeMs`, standing in for the `stat` the guest can no longer perform.
|
|
378
|
-
*/
|
|
379
|
-
function emitDaemonPullCommands() {
|
|
380
|
-
return [
|
|
381
|
-
'echo "Cache daemon: $CIBUILD_CACHE_DAEMON"',
|
|
382
|
-
...emitDaemonPullDiagnostics(),
|
|
383
|
-
// Exact key. zstd's and tar's stderr is silenced — a miss is the normal
|
|
384
|
-
// case and they both editorialise about it; pipefail still carries the
|
|
385
|
-
// exit status. curl's own stderr is kept, in the file the diagnostics
|
|
386
|
-
// above read: it is the only thing that knows *why*.
|
|
387
|
-
`if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`,
|
|
388
|
-
' echo "Cache found (daemon), extracting..."',
|
|
389
|
-
' echo "CACHE_SOURCE=daemon CACHE_KEY=$CACHE_KEY"',
|
|
390
|
-
'else',
|
|
391
|
-
// Said before the fallback is attempted, so the reason names the request
|
|
392
|
-
// it belongs to rather than whichever request happened to fail last.
|
|
393
|
-
...emitDaemonPullFailureDiagnosis(' ', '$CACHE_KEY'),
|
|
394
|
-
// Scope fallback: the newest tarball for this <keyPrefix>-<projectId>.
|
|
395
|
-
// Gradle/Xcode re-hash their inputs on top of the warm directory, hitting
|
|
396
|
-
// the unchanged work and rebuilding only what actually moved.
|
|
397
|
-
' __ci_fb_scope="${CACHE_KEY%-*}"',
|
|
398
|
-
` __ci_fb_json=$(${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/scope/$__ci_fb_scope/latest"')} 2>/dev/null || true)`,
|
|
399
|
-
// Parse without a JSON tool: the guest image is not guaranteed to ship jq.
|
|
400
|
-
` __ci_fb_key=$(printf '%s' "$__ci_fb_json" | sed -n 's/.*"key"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
|
|
401
|
-
` __ci_fb_mtime=$(printf '%s' "$__ci_fb_json" | sed -n 's/.*"mtimeMs"[[:space:]]*:[[:space:]]*\\([0-9]*\\).*/\\1/p')`,
|
|
402
|
-
' if [ -n "$__ci_fb_key" ] && [ -n "$__ci_fb_mtime" ]; then',
|
|
403
|
-
' __ci_fb_age_days=$(( ($(date +%s) - __ci_fb_mtime / 1000) / 86400 ))',
|
|
404
|
-
// Age cap: a fallback older than 30d likely carries obsolete toolchain
|
|
405
|
-
// installs or cache entries from a pre-wrapper-bump world. Go cold rather
|
|
406
|
-
// than seed from ancient state. Exact-key hits are unaffected.
|
|
407
|
-
' if [ "$__ci_fb_age_days" -le 30 ]; then',
|
|
408
|
-
' echo "Cache fallback (daemon): using prior tarball $__ci_fb_key (${__ci_fb_age_days}d old)"',
|
|
409
|
-
` if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$__ci_fb_key.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`,
|
|
410
|
-
' echo "CACHE_SOURCE=fallback_daemon CACHE_KEY=$CACHE_KEY FALLBACK_KEY=$__ci_fb_key"',
|
|
411
|
-
' else',
|
|
412
|
-
' echo "Cache fallback $__ci_fb_key could not be fetched — going cold"',
|
|
413
|
-
...emitDaemonPullFailureDiagnosis(' ', '$__ci_fb_key'),
|
|
414
|
-
' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"',
|
|
415
|
-
' fi',
|
|
416
|
-
' else',
|
|
417
|
-
' echo "Cache fallback candidate $__ci_fb_key is ${__ci_fb_age_days}d old, exceeds 30d cap — going cold"',
|
|
418
|
-
' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY FALLBACK_REJECTED=$__ci_fb_key"',
|
|
419
|
-
' fi',
|
|
420
|
-
' else',
|
|
421
|
-
' echo "No cache found for key: $CACHE_KEY"',
|
|
422
|
-
' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"',
|
|
423
|
-
' fi',
|
|
424
|
-
'fi',
|
|
425
|
-
'rm -f "$__ci_cache_diag"',
|
|
426
|
-
];
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* Cache pull step executor
|
|
430
|
-
* Restores cached files from cache directory
|
|
431
|
-
*/
|
|
432
|
-
export class CachePullStepExecutor extends BaseStepExecutor {
|
|
433
|
-
async execute(inputs, env, config) {
|
|
434
|
-
const stepName = 'cache-pull';
|
|
435
|
-
if (config.local) {
|
|
436
|
-
const script = this.createBashScriptFromCommands(['echo "⏭️ Cache pull skipped (local mode)"'], stepName);
|
|
437
|
-
return this.createScriptStep(script, stepName);
|
|
438
|
-
}
|
|
439
|
-
// Check for technology preset
|
|
440
|
-
const technology = this.getInput(inputs, 'technology', '').trim();
|
|
441
|
-
if (technology) {
|
|
442
|
-
return this.executeWithPreset(technology, inputs, config);
|
|
443
|
-
}
|
|
444
|
-
// Get cache key - optional (skip if not provided)
|
|
445
|
-
const cacheKey = this.getInput(inputs, 'cache_key', '');
|
|
446
|
-
// Skip if no cache key provided
|
|
447
|
-
if (!cacheKey || cacheKey.trim() === '') {
|
|
448
|
-
const commands = [];
|
|
449
|
-
commands.push('# Cache Pull - Skipped');
|
|
450
|
-
commands.push('echo "⏭️ Cache pull skipped: no cache_key configured"');
|
|
451
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
452
|
-
return this.createScriptStep(script, stepName);
|
|
453
|
-
}
|
|
454
|
-
// Get cache paths - defaults to empty array (can be string or array)
|
|
455
|
-
const cachePathsInput = this.getInput(inputs, 'cache_paths', []);
|
|
456
|
-
// Normalize to array (handle single string input)
|
|
457
|
-
const cachePaths = Array.isArray(cachePathsInput)
|
|
458
|
-
? cachePathsInput
|
|
459
|
-
: (typeof cachePathsInput === 'string' && cachePathsInput.trim() !== '')
|
|
460
|
-
? [cachePathsInput]
|
|
461
|
-
: [];
|
|
462
|
-
// Get debug mode flag
|
|
463
|
-
const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
|
|
464
|
-
const commands = [];
|
|
465
|
-
commands.push('# Cache Pull');
|
|
466
|
-
// Create cache directory if it doesn't exist
|
|
467
|
-
commands.push('');
|
|
468
|
-
commands.push('# Ensure cache directory exists');
|
|
469
|
-
commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
|
|
470
|
-
// Only the filesystem transport needs the directory to exist. Over the
|
|
471
|
-
// daemon the cache lives on the host, and on a runner this path is no
|
|
472
|
-
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
473
|
-
// whole step under errexit.
|
|
474
|
-
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
475
|
-
// Project scope: an explicit `cache_key` is author-written and carries no
|
|
476
|
-
// repo identity, so two projects that happen to write the same key (e.g.
|
|
477
|
-
// `gradle-{{checksum "gradle/wrapper/gradle-wrapper.properties"}}`, which
|
|
478
|
-
// depends only on the Gradle version) would share one tarball on a shared
|
|
479
|
-
// runner. Prefixing the project id keeps each repo's cache its own.
|
|
480
|
-
//
|
|
481
|
-
// The id goes at the FRONT, deliberately. The preset path spells its key
|
|
482
|
-
// `<keyPrefix>-<projectId>-<checksum>`, and that middle-segment shape is
|
|
483
|
-
// load-bearing elsewhere: it is how a scope is recovered from a tarball
|
|
484
|
-
// filename. An explicit key is author-written and must stay outside the
|
|
485
|
-
// scope model, so it must not be able to imitate that shape by accident.
|
|
486
|
-
commands.push('');
|
|
487
|
-
commands.push(...emitProjectScopeCommands());
|
|
488
|
-
// Generate cache file name based on cache key
|
|
489
|
-
commands.push('');
|
|
490
|
-
commands.push('# Generate cache file path');
|
|
491
|
-
commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
|
|
492
|
-
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
493
|
-
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
494
|
-
if (isDebugMode) {
|
|
495
|
-
commands.push('echo "Debug mode enabled"');
|
|
496
|
-
commands.push('echo "Cache file: $CACHE_FILE"');
|
|
497
|
-
}
|
|
498
|
-
// Peer cache directory
|
|
499
|
-
const peerCacheDir = config.paths.peerCacheDir || '';
|
|
500
|
-
if (peerCacheDir) {
|
|
501
|
-
commands.push('');
|
|
502
|
-
commands.push(`PEER_CACHE_DIR="${this.escapeBash(peerCacheDir)}"`);
|
|
503
|
-
commands.push('PEER_CACHE_FILE="$PEER_CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
504
|
-
}
|
|
505
|
-
// Where each class of member is restored to. Emitted before every
|
|
506
|
-
// transport, because all of them extract.
|
|
507
|
-
commands.push('');
|
|
508
|
-
commands.push('# Restore roots');
|
|
509
|
-
commands.push(...emitCacheExtractHelper(cachePaths, config));
|
|
510
|
-
// Check if cache file exists
|
|
511
|
-
commands.push('');
|
|
512
|
-
commands.push('# Check if cache exists');
|
|
513
|
-
// Transport split — see the preset path. An explicit-key step has no scope
|
|
514
|
-
// to fall back to, so the daemon side is the exact key or nothing.
|
|
515
|
-
commands.push('if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
|
|
516
|
-
commands.push(...emitDaemonPullDiagnostics(' '));
|
|
517
|
-
commands.push(` if { ${daemonCurl('"$CIBUILD_CACHE_DAEMON/cache/$CACHE_KEY.tar.zst" 2>"$__ci_cache_diag"')} | zstd -dc | __ci_cache_extract; } 2>/dev/null; then`);
|
|
518
|
-
commands.push(' echo "Cache found (daemon), extracting..."');
|
|
519
|
-
commands.push(' echo "CACHE_SOURCE=daemon"');
|
|
520
|
-
commands.push(' else');
|
|
521
|
-
commands.push(...emitDaemonPullFailureDiagnosis(' ', '$CACHE_KEY'));
|
|
522
|
-
commands.push(' echo "No cache found for key: $CACHE_KEY"');
|
|
523
|
-
commands.push(' echo "CACHE_SOURCE=cold"');
|
|
524
|
-
commands.push(' fi');
|
|
525
|
-
commands.push(' rm -f "$__ci_cache_diag"');
|
|
526
|
-
commands.push('else');
|
|
527
|
-
commands.push('if [ -f "$CACHE_FILE" ]; then');
|
|
528
|
-
commands.push(' echo "Cache found (local), extracting..."');
|
|
529
|
-
if (isDebugMode) {
|
|
530
|
-
commands.push(' echo "Cache file size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
531
|
-
}
|
|
532
|
-
commands.push(' zstd -dc "$CACHE_FILE" | __ci_cache_extract');
|
|
533
|
-
commands.push(' echo "CACHE_SOURCE=local"');
|
|
534
|
-
// Peer fallback
|
|
535
|
-
if (peerCacheDir) {
|
|
536
|
-
commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
|
|
537
|
-
commands.push(' echo "Cache found (peer), extracting..."');
|
|
538
|
-
commands.push(' tee "$CACHE_FILE.tmp" < "$PEER_CACHE_FILE" | zstd -dc | __ci_cache_extract');
|
|
539
|
-
commands.push(' mv "$CACHE_FILE.tmp" "$CACHE_FILE"');
|
|
540
|
-
commands.push(' echo "CACHE_SOURCE=peer"');
|
|
541
|
-
}
|
|
542
|
-
commands.push('else');
|
|
543
|
-
commands.push(' echo "No cache found for key: $CACHE_KEY"');
|
|
544
|
-
commands.push(' echo "CACHE_SOURCE=cold"');
|
|
545
|
-
commands.push('fi');
|
|
546
|
-
// Closes the daemon/filesystem transport split.
|
|
547
|
-
commands.push('fi');
|
|
548
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
549
|
-
return this.createScriptStep(script, stepName);
|
|
550
|
-
}
|
|
551
|
-
async executeWithPreset(technology, inputs, config) {
|
|
552
|
-
const stepName = 'cache-pull';
|
|
553
|
-
const chain = resolvePresetChain(technology);
|
|
554
|
-
if (chain.length === 0) {
|
|
555
|
-
const supported = Object.keys(CACHE_PRESETS).join(', ');
|
|
556
|
-
throw new Error(`Unknown cache technology '${technology}'. Supported: ${supported}`);
|
|
557
|
-
}
|
|
558
|
-
const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
|
|
559
|
-
const commands = [];
|
|
560
|
-
commands.push(`# Cache Pull (${technology})`);
|
|
561
|
-
commands.push(`echo "Restoring ${technology} cache..."`);
|
|
562
|
-
commands.push('');
|
|
563
|
-
// Compute cache key from lockfile checksum at runtime
|
|
564
|
-
// If technology has a fallback chain, try each lockfile in order
|
|
565
|
-
commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
|
|
566
|
-
// Only the filesystem transport needs the directory to exist. Over the
|
|
567
|
-
// daemon the cache lives on the host, and on a runner this path is no
|
|
568
|
-
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
569
|
-
// whole step under errexit.
|
|
570
|
-
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
571
|
-
commands.push('');
|
|
572
|
-
// Helper: resolve lockfile path — checks exact path first, then searches
|
|
573
|
-
// recursively (handles SPM Package.resolved inside .xcodeproj/.xcworkspace)
|
|
574
|
-
commands.push('__ci_find_lockfile() {');
|
|
575
|
-
commands.push(' if [ -f "$1" ]; then echo "$1"; return; fi');
|
|
576
|
-
commands.push(' find . -name "$(basename "$1")" -not -path "*/DerivedData/*" -not -path "*/.build/*" 2>/dev/null | head -1');
|
|
577
|
-
commands.push('}');
|
|
578
|
-
commands.push('');
|
|
579
|
-
// Project scope: include a stable short hash of the git remote in every
|
|
580
|
-
// cache key so different repos on the same runner don't share tarballs
|
|
581
|
-
// (prevents fallback cross-contamination across projects).
|
|
582
|
-
commands.push(...emitProjectScopeCommands());
|
|
583
|
-
commands.push('');
|
|
584
|
-
if (chain.length === 1 && chain[0].fingerprint && chain[0].fingerprint.length > 0) {
|
|
585
|
-
commands.push(...emitFingerprintKeyCommands(chain[0]));
|
|
586
|
-
}
|
|
587
|
-
else if (chain.length === 1) {
|
|
588
|
-
const preset = chain[0];
|
|
589
|
-
commands.push(`LOCKFILE=$(__ci_find_lockfile "${this.escapeBash(preset.lockfile)}")`);
|
|
590
|
-
commands.push('if [ -n "$LOCKFILE" ] && [ -f "$LOCKFILE" ]; then');
|
|
591
|
-
commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
|
|
592
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
|
|
593
|
-
commands.push('else');
|
|
594
|
-
commands.push(` echo "Warning: ${this.escapeBash(preset.lockfile)} not found, using fallback cache key"`);
|
|
595
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-no-lockfile"`);
|
|
596
|
-
commands.push('fi');
|
|
597
|
-
}
|
|
598
|
-
else {
|
|
599
|
-
// Fallback chain: probe every lockfile up front, then branch on the
|
|
600
|
-
// results. The probes must precede the if/elif — emitting a probe
|
|
601
|
-
// inside a prior preset's `then` body (the old shape) meant the
|
|
602
|
-
// second lockfile was only checked when the FIRST one was found, so
|
|
603
|
-
// an SPM project with no Podfile.lock never reached the Package.resolved
|
|
604
|
-
// branch and fell through to `<first>-no-lockfile`.
|
|
605
|
-
for (let i = 0; i < chain.length; i++) {
|
|
606
|
-
commands.push(`__ci_lockfile_${i}=$(__ci_find_lockfile "${this.escapeBash(chain[i].lockfile)}")`);
|
|
607
|
-
}
|
|
608
|
-
for (let i = 0; i < chain.length; i++) {
|
|
609
|
-
const preset = chain[i];
|
|
610
|
-
const cond = i === 0 ? 'if' : 'elif';
|
|
611
|
-
commands.push(`${cond} [ -n "$__ci_lockfile_${i}" ] && [ -f "$__ci_lockfile_${i}" ]; then`);
|
|
612
|
-
commands.push(` LOCKFILE="$__ci_lockfile_${i}"`);
|
|
613
|
-
commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
|
|
614
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
|
|
615
|
-
if (isDebugMode) {
|
|
616
|
-
commands.push(` echo "Detected: ${this.escapeBash(preset.keyPrefix)} (lockfile: $LOCKFILE)"`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
commands.push('else');
|
|
620
|
-
commands.push(` echo "Warning: no lockfile found, using fallback cache key"`);
|
|
621
|
-
commands.push(` CACHE_KEY="${this.escapeBash(chain[0].keyPrefix)}-\${__ci_proj}-no-lockfile"`);
|
|
622
|
-
commands.push('fi');
|
|
623
|
-
}
|
|
624
|
-
commands.push('');
|
|
625
|
-
if (isDebugMode) {
|
|
626
|
-
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
627
|
-
}
|
|
628
|
-
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
629
|
-
commands.push('');
|
|
630
|
-
// Where each class of member is restored to. Every path below extracts, so
|
|
631
|
-
// this is emitted once, above the transport split. `allPaths` is the same
|
|
632
|
-
// union cache-push archives from, which is what keeps the two sides
|
|
633
|
-
// agreeing about which class a path is in.
|
|
634
|
-
commands.push('# Restore roots');
|
|
635
|
-
commands.push(...emitCacheExtractHelper([...new Set(chain.flatMap(p => p.paths))], config));
|
|
636
|
-
commands.push('');
|
|
637
|
-
// Transport split. On a runner the cache lives on the host and is reached
|
|
638
|
-
// over HTTP; standalone, it is a directory on this machine. The daemon URL
|
|
639
|
-
// is the switch, so the same generated script serves both.
|
|
640
|
-
commands.push('if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
|
|
641
|
-
commands.push(...emitDaemonPullCommands());
|
|
642
|
-
commands.push('else');
|
|
643
|
-
// Peer cache directory — set from config, empty string disables peer lookup
|
|
644
|
-
const peerCacheDir = config.paths.peerCacheDir || '';
|
|
645
|
-
if (peerCacheDir) {
|
|
646
|
-
commands.push(`PEER_CACHE_DIR="${this.escapeBash(peerCacheDir)}"`);
|
|
647
|
-
commands.push('PEER_CACHE_FILE="$PEER_CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
648
|
-
commands.push('');
|
|
649
|
-
}
|
|
650
|
-
// 1. Local hit
|
|
651
|
-
commands.push('if [ -f "$CACHE_FILE" ]; then');
|
|
652
|
-
commands.push(' echo "Cache found (local), extracting..."');
|
|
653
|
-
if (isDebugMode) {
|
|
654
|
-
commands.push(' echo "Cache file size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
655
|
-
}
|
|
656
|
-
commands.push(' zstd -dc "$CACHE_FILE" | __ci_cache_extract');
|
|
657
|
-
// LRU bump: lift this tarball to the top of `ls -t` so retention's age
|
|
658
|
-
// cap counts last-used time, not last-written. Without this, a tarball
|
|
659
|
-
// hit daily would still be reaped on its 31st day from creation.
|
|
660
|
-
commands.push(' touch "$CACHE_FILE"');
|
|
661
|
-
commands.push(' echo "CACHE_SOURCE=local CACHE_KEY=$CACHE_KEY"');
|
|
662
|
-
// 2. Peer hit — tee to warm local cache while extracting in one pass
|
|
663
|
-
if (peerCacheDir) {
|
|
664
|
-
commands.push('elif [ -f "$PEER_CACHE_FILE" ] 2>/dev/null; then');
|
|
665
|
-
commands.push(' echo "Cache found (peer), extracting..."');
|
|
666
|
-
if (isDebugMode) {
|
|
667
|
-
commands.push(' echo "Peer cache file size: $(du -h "$PEER_CACHE_FILE" | cut -f1)"');
|
|
668
|
-
}
|
|
669
|
-
// Single NFS read: tee forks the stream to local cache file AND decompression
|
|
670
|
-
commands.push(' tee "$CACHE_FILE.tmp" < "$PEER_CACHE_FILE" | zstd -dc | __ci_cache_extract');
|
|
671
|
-
commands.push(' mv "$CACHE_FILE.tmp" "$CACHE_FILE"');
|
|
672
|
-
commands.push(' echo "CACHE_SOURCE=peer CACHE_KEY=$CACHE_KEY"');
|
|
673
|
-
}
|
|
674
|
-
// 3. Fallback — no exact-key tarball, try the newest prior tarball scoped
|
|
675
|
-
// to the same <keyPrefix>-<projectId> (so fallback stays within this repo
|
|
676
|
-
// and doesn't pick up a different KMM project's cache). Gradle will
|
|
677
|
-
// re-hash task inputs on top of the warm ~/.gradle/caches directory,
|
|
678
|
-
// hitting the unchanged tasks and recompiling only what actually changed.
|
|
679
|
-
// Deliberately does NOT warm $CACHE_FILE — that way cache-push still runs
|
|
680
|
-
// and creates a fresh tarball under the new key for future exact hits.
|
|
681
|
-
commands.push('else');
|
|
682
|
-
commands.push(' __ci_fb_scope="${CACHE_KEY%-*}"');
|
|
683
|
-
// `|| true` defuses pipefail+errexit when ls finds no matches: bare
|
|
684
|
-
// assignment would abort the whole cache-pull step silently on an empty
|
|
685
|
-
// cache dir, flipping any cold miss into a non-zero exit.
|
|
686
|
-
commands.push(' __ci_fb=$(ls -t "$CACHE_DIR"/"$__ci_fb_scope"-*.tar.zst 2>/dev/null | head -1 || true)');
|
|
687
|
-
if (peerCacheDir) {
|
|
688
|
-
commands.push(' if [ -z "$__ci_fb" ] || [ ! -f "$__ci_fb" ]; then');
|
|
689
|
-
commands.push(' __ci_fb=$(ls -t "$PEER_CACHE_DIR"/"$__ci_fb_scope"-*.tar.zst 2>/dev/null | head -1 || true)');
|
|
690
|
-
commands.push(' __ci_fb_src=peer');
|
|
691
|
-
commands.push(' else');
|
|
692
|
-
commands.push(' __ci_fb_src=local');
|
|
693
|
-
commands.push(' fi');
|
|
694
|
-
}
|
|
695
|
-
else {
|
|
696
|
-
commands.push(' __ci_fb_src=local');
|
|
697
|
-
}
|
|
698
|
-
commands.push(' if [ -n "$__ci_fb" ] && [ -f "$__ci_fb" ]; then');
|
|
699
|
-
commands.push(' __ci_fb_key=$(basename "$__ci_fb" .tar.zst)');
|
|
700
|
-
// Age cap: a fallback older than 30d likely carries obsolete .konan
|
|
701
|
-
// installs or Gradle entries from a pre-wrapper-bump world. Go cold
|
|
702
|
-
// rather than seed from ancient state. Exact-key hits are unaffected.
|
|
703
|
-
commands.push(' __ci_fb_age_days=$(( ($(date +%s) - $(stat -f %m "$__ci_fb")) / 86400 ))');
|
|
704
|
-
commands.push(' if [ "$__ci_fb_age_days" -le 30 ]; then');
|
|
705
|
-
commands.push(' echo "Cache fallback ($__ci_fb_src): using prior tarball $__ci_fb_key (${__ci_fb_age_days}d old)"');
|
|
706
|
-
if (isDebugMode) {
|
|
707
|
-
commands.push(' echo "Fallback size: $(du -h "$__ci_fb" | cut -f1)"');
|
|
708
|
-
}
|
|
709
|
-
commands.push(' zstd -dc "$__ci_fb" | __ci_cache_extract');
|
|
710
|
-
// LRU bump for the fallback tarball — we restored from it, so it earns
|
|
711
|
-
// its place at the top of the retention sort even if the exact-key
|
|
712
|
-
// tarball is the one that gets created in this run's cache-push.
|
|
713
|
-
commands.push(' touch "$__ci_fb"');
|
|
714
|
-
commands.push(' echo "CACHE_SOURCE=fallback_$__ci_fb_src CACHE_KEY=$CACHE_KEY FALLBACK_KEY=$__ci_fb_key"');
|
|
715
|
-
commands.push(' else');
|
|
716
|
-
commands.push(' echo "Cache fallback candidate $__ci_fb_key is ${__ci_fb_age_days}d old, exceeds 30d cap — going cold"');
|
|
717
|
-
commands.push(' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY FALLBACK_REJECTED=$__ci_fb_key"');
|
|
718
|
-
commands.push(' fi');
|
|
719
|
-
commands.push(' else');
|
|
720
|
-
// 4. Cold miss — no exact key, no fallback candidate
|
|
721
|
-
commands.push(' echo "No cache found for key: $CACHE_KEY"');
|
|
722
|
-
commands.push(' echo "CACHE_SOURCE=cold CACHE_KEY=$CACHE_KEY"');
|
|
723
|
-
commands.push(' fi');
|
|
724
|
-
commands.push('fi');
|
|
725
|
-
// Closes the daemon/filesystem transport split.
|
|
726
|
-
commands.push('fi');
|
|
727
|
-
if (isDebugMode) {
|
|
728
|
-
commands.push('echo "Restored paths:"');
|
|
729
|
-
const allPaths = [...new Set(chain.flatMap(p => p.paths))];
|
|
730
|
-
for (const p of allPaths) {
|
|
731
|
-
commands.push(`EXPANDED="${this.escapeBash(p)}"`);
|
|
732
|
-
commands.push('EXPANDED="${EXPANDED/#~/${CIBUILD_USER_HOME:-$HOME}}"');
|
|
733
|
-
commands.push('if [ -e "$EXPANDED" ]; then');
|
|
734
|
-
commands.push(' echo " ✓ $EXPANDED"');
|
|
735
|
-
commands.push('else');
|
|
736
|
-
commands.push(' echo " ✗ $EXPANDED (not found)"');
|
|
737
|
-
commands.push('fi');
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
741
|
-
return this.createScriptStep(script, stepName);
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Cache push step executor
|
|
746
|
-
* Saves files to cache directory
|
|
747
|
-
*/
|
|
748
|
-
export class CachePushStepExecutor extends BaseStepExecutor {
|
|
749
|
-
async execute(inputs, env, config) {
|
|
750
|
-
const stepName = 'cache-push';
|
|
751
|
-
if (config.local) {
|
|
752
|
-
const script = this.createBashScriptFromCommands(['echo "⏭️ Cache push skipped (local mode)"'], stepName);
|
|
753
|
-
return this.createScriptStep(script, stepName);
|
|
754
|
-
}
|
|
755
|
-
// Check for technology preset
|
|
756
|
-
const technology = this.getInput(inputs, 'technology', '').trim();
|
|
757
|
-
if (technology) {
|
|
758
|
-
return this.executeWithPreset(technology, inputs, config);
|
|
759
|
-
}
|
|
760
|
-
// Get cache key - optional (skip if not provided)
|
|
761
|
-
const cacheKey = this.getInput(inputs, 'cache_key', '');
|
|
762
|
-
// Skip if no cache key provided
|
|
763
|
-
if (!cacheKey || cacheKey.trim() === '') {
|
|
764
|
-
const commands = [];
|
|
765
|
-
commands.push('# Cache Push - Skipped');
|
|
766
|
-
commands.push('echo "⏭️ Cache push skipped: no cache_key configured"');
|
|
767
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
768
|
-
return this.createScriptStep(script, stepName);
|
|
769
|
-
}
|
|
770
|
-
// Get cache paths - required (can be string or array)
|
|
771
|
-
const cachePathsInput = this.getInput(inputs, 'cache_paths', []);
|
|
772
|
-
// Normalize to array (handle single string input)
|
|
773
|
-
const cachePaths = Array.isArray(cachePathsInput)
|
|
774
|
-
? cachePathsInput
|
|
775
|
-
: (typeof cachePathsInput === 'string' && cachePathsInput.trim() !== '')
|
|
776
|
-
? [cachePathsInput]
|
|
777
|
-
: [];
|
|
778
|
-
if (cachePaths.length === 0) {
|
|
779
|
-
throw new Error(`Missing or empty 'cache_paths' for step '${stepName}'`);
|
|
780
|
-
}
|
|
781
|
-
// Get debug mode flag
|
|
782
|
-
const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
|
|
783
|
-
// Get ignore check flag
|
|
784
|
-
const ignoreCheckOnPaths = this.getInput(inputs, 'ignore_check_on_paths', false);
|
|
785
|
-
const commands = [];
|
|
786
|
-
commands.push('# Cache Push');
|
|
787
|
-
// Create cache directory if it doesn't exist
|
|
788
|
-
commands.push('');
|
|
789
|
-
commands.push('# Ensure cache directory exists');
|
|
790
|
-
commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
|
|
791
|
-
// Only the filesystem transport needs the directory to exist. Over the
|
|
792
|
-
// daemon the cache lives on the host, and on a runner this path is no
|
|
793
|
-
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
794
|
-
// whole step under errexit.
|
|
795
|
-
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
796
|
-
// Project scope — must match the pull side exactly, or a push writes a
|
|
797
|
-
// tarball no pull can ever find. See the note there for why an explicit
|
|
798
|
-
// key needs the repo identity prefixed.
|
|
799
|
-
commands.push('');
|
|
800
|
-
commands.push(...emitProjectScopeCommands());
|
|
801
|
-
// Generate cache file name based on cache key
|
|
802
|
-
commands.push('');
|
|
803
|
-
commands.push('# Generate cache file path');
|
|
804
|
-
// CACHE_KEY first, CACHE_FILE derived from it — exactly as the pull side
|
|
805
|
-
// does. This step used to set only CACHE_FILE while the daemon URL below
|
|
806
|
-
// interpolated `$CACHE_KEY`; see emitDaemonPushCommands.
|
|
807
|
-
commands.push(`CACHE_KEY="\${__ci_proj}-${this.escapeBash(cacheKey)}"`);
|
|
808
|
-
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
809
|
-
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
810
|
-
if (isDebugMode) {
|
|
811
|
-
commands.push('echo "Debug mode enabled"');
|
|
812
|
-
commands.push('echo "Cache file: $CACHE_FILE"');
|
|
813
|
-
}
|
|
814
|
-
// Check which paths exist
|
|
815
|
-
commands.push('');
|
|
816
|
-
commands.push('# Check which paths exist');
|
|
817
|
-
commands.push('PATHS_TO_CACHE=()');
|
|
818
|
-
for (const path of cachePaths) {
|
|
819
|
-
// Resolve path identifiers (e.g., "derived_data:pattern" -> "~/Library/.../DerivedData/pattern").
|
|
820
|
-
// Shared with cache-pull's member classification: if only one side
|
|
821
|
-
// resolved these, the two would disagree about which restore root the
|
|
822
|
-
// path belongs to.
|
|
823
|
-
const resolvedPath = resolveCachePathIdentifier(path, config);
|
|
824
|
-
// Expand tilde by using parameter expansion
|
|
825
|
-
commands.push(`EXPANDED_PATH="${this.escapeBash(resolvedPath)}"`);
|
|
826
|
-
commands.push('EXPANDED_PATH="${EXPANDED_PATH/#~/${CIBUILD_USER_HOME:-$HOME}}"');
|
|
827
|
-
commands.push('');
|
|
828
|
-
commands.push('# Expand glob patterns and check each match');
|
|
829
|
-
commands.push('FOUND_MATCH=false');
|
|
830
|
-
commands.push('shopt -s nullglob');
|
|
831
|
-
commands.push('for path_match in $EXPANDED_PATH; do');
|
|
832
|
-
commands.push(' if [ -e "$path_match" ]; then');
|
|
833
|
-
commands.push(' PATHS_TO_CACHE+=("$path_match")');
|
|
834
|
-
commands.push(' FOUND_MATCH=true');
|
|
835
|
-
if (isDebugMode) {
|
|
836
|
-
commands.push(' echo "Found: $path_match"');
|
|
837
|
-
}
|
|
838
|
-
commands.push(' fi');
|
|
839
|
-
commands.push('done');
|
|
840
|
-
commands.push('shopt -u nullglob');
|
|
841
|
-
commands.push('');
|
|
842
|
-
commands.push('if [ "$FOUND_MATCH" = false ]; then');
|
|
843
|
-
if (isDebugMode) {
|
|
844
|
-
commands.push(` echo "Not found: $EXPANDED_PATH"`);
|
|
845
|
-
}
|
|
846
|
-
if (!ignoreCheckOnPaths) {
|
|
847
|
-
commands.push(` echo "Warning: Path does not exist: $EXPANDED_PATH"`);
|
|
848
|
-
}
|
|
849
|
-
commands.push('fi');
|
|
850
|
-
}
|
|
851
|
-
// Create cache if we have paths
|
|
852
|
-
commands.push('');
|
|
853
|
-
commands.push('# Skip if cache already exists for this key');
|
|
854
|
-
// Over the daemon there is no local file to test, so the "already up to
|
|
855
|
-
// date" shortcut can't apply — the daemon's PUT is an idempotent overwrite
|
|
856
|
-
// under the same key, which gets the same outcome.
|
|
857
|
-
commands.push('if [ -z "$CIBUILD_CACHE_DAEMON" ] && [ -f "$CACHE_FILE" ]; then');
|
|
858
|
-
commands.push(' echo "Cache already up to date for key: $CACHE_KEY"');
|
|
859
|
-
commands.push('elif [ ${#PATHS_TO_CACHE[@]} -gt 0 ]; then');
|
|
860
|
-
commands.push(' echo "Caching ${#PATHS_TO_CACHE[@]} path(s)..."');
|
|
861
|
-
if (isDebugMode) {
|
|
862
|
-
commands.push(' echo "Paths to cache:"');
|
|
863
|
-
commands.push(' printf " %s\\n" "${PATHS_TO_CACHE[@]}"');
|
|
864
|
-
}
|
|
865
|
-
// Transport split — see the preset path.
|
|
866
|
-
commands.push(' if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
|
|
867
|
-
commands.push(...emitDaemonPushCommands());
|
|
868
|
-
commands.push(' else');
|
|
869
|
-
// Atomic write into a per-process-unique temp (CBR-12). The "runs serialized
|
|
870
|
-
// per runner" assumption behind a fixed "$CACHE_FILE.tmp" is false with two VM
|
|
871
|
-
// slots and collides with the host cache daemon mutating this VirtioFS-shared
|
|
872
|
-
// dir; a unique name (PID + $RANDOM) removes the clash. The atomic mv is kept,
|
|
873
|
-
// and a failed compress/mv removes its own temp (no orphan) and stays non-fatal.
|
|
874
|
-
commands.push(' CACHE_TMP="$CACHE_FILE.$$.$RANDOM.tmp"');
|
|
875
|
-
commands.push(' tar -cf - "${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 > "$CACHE_TMP" && mv "$CACHE_TMP" "$CACHE_FILE" || rm -f "$CACHE_TMP"');
|
|
876
|
-
commands.push(' if [ -f "$CACHE_FILE" ]; then');
|
|
877
|
-
commands.push(' echo "Cache created successfully"');
|
|
878
|
-
if (isDebugMode) {
|
|
879
|
-
commands.push(' echo "Cache size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
880
|
-
}
|
|
881
|
-
commands.push(' else');
|
|
882
|
-
commands.push(' echo "Warning: Failed to create cache file"');
|
|
883
|
-
commands.push(' fi');
|
|
884
|
-
// Closes the daemon/filesystem transport split.
|
|
885
|
-
commands.push(' fi');
|
|
886
|
-
commands.push('else');
|
|
887
|
-
commands.push(' echo "No paths to cache"');
|
|
888
|
-
commands.push('fi');
|
|
889
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
890
|
-
return this.createScriptStep(script, stepName);
|
|
891
|
-
}
|
|
892
|
-
async executeWithPreset(technology, inputs, config) {
|
|
893
|
-
const stepName = 'cache-push';
|
|
894
|
-
const chain = resolvePresetChain(technology);
|
|
895
|
-
if (chain.length === 0) {
|
|
896
|
-
const supported = Object.keys(CACHE_PRESETS).join(', ');
|
|
897
|
-
throw new Error(`Unknown cache technology '${technology}'. Supported: ${supported}`);
|
|
898
|
-
}
|
|
899
|
-
const isDebugMode = this.getInput(inputs, 'is_debug_mode', false);
|
|
900
|
-
const commands = [];
|
|
901
|
-
commands.push(`# Cache Push (${technology})`);
|
|
902
|
-
commands.push(`echo "Saving ${technology} cache..."`);
|
|
903
|
-
commands.push('');
|
|
904
|
-
// Compute cache key from lockfile checksum at runtime
|
|
905
|
-
commands.push(`CACHE_DIR="${this.escapeBash(config.paths.cacheDir)}"`);
|
|
906
|
-
// Only the filesystem transport needs the directory to exist. Over the
|
|
907
|
-
// daemon the cache lives on the host, and on a runner this path is no
|
|
908
|
-
// longer mounted into the VM — an unconditional mkdir would abort the
|
|
909
|
-
// whole step under errexit.
|
|
910
|
-
commands.push('[ -n "$CIBUILD_CACHE_DAEMON" ] || mkdir -p "$CACHE_DIR"');
|
|
911
|
-
commands.push('');
|
|
912
|
-
commands.push('__ci_find_lockfile() {');
|
|
913
|
-
commands.push(' if [ -f "$1" ]; then echo "$1"; return; fi');
|
|
914
|
-
commands.push(' find . -name "$(basename "$1")" -not -path "*/DerivedData/*" -not -path "*/.build/*" 2>/dev/null | head -1');
|
|
915
|
-
commands.push('}');
|
|
916
|
-
commands.push('');
|
|
917
|
-
// Project scope: include a stable short hash of the git remote in every
|
|
918
|
-
// cache key so different repos on the same runner don't share tarballs.
|
|
919
|
-
commands.push(...emitProjectScopeCommands());
|
|
920
|
-
commands.push('');
|
|
921
|
-
if (chain.length === 1 && chain[0].fingerprint && chain[0].fingerprint.length > 0) {
|
|
922
|
-
commands.push(...emitFingerprintKeyCommands(chain[0]));
|
|
923
|
-
}
|
|
924
|
-
else if (chain.length === 1) {
|
|
925
|
-
const preset = chain[0];
|
|
926
|
-
commands.push(`LOCKFILE=$(__ci_find_lockfile "${this.escapeBash(preset.lockfile)}")`);
|
|
927
|
-
commands.push('if [ -n "$LOCKFILE" ] && [ -f "$LOCKFILE" ]; then');
|
|
928
|
-
commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
|
|
929
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
|
|
930
|
-
commands.push('else');
|
|
931
|
-
commands.push(` echo "Warning: ${this.escapeBash(preset.lockfile)} not found, using fallback cache key"`);
|
|
932
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-no-lockfile"`);
|
|
933
|
-
commands.push('fi');
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
// Fallback chain: probe every lockfile up front, then branch — same
|
|
937
|
-
// fix as cache-pull. A probe nested in a prior preset's `then` body
|
|
938
|
-
// only ran when the first lockfile was found, so push wrote the
|
|
939
|
-
// `<first>-no-lockfile` key for SPM projects, mismatching what a
|
|
940
|
-
// correct pull would look for.
|
|
941
|
-
for (let i = 0; i < chain.length; i++) {
|
|
942
|
-
commands.push(`__ci_lockfile_${i}=$(__ci_find_lockfile "${this.escapeBash(chain[i].lockfile)}")`);
|
|
943
|
-
}
|
|
944
|
-
for (let i = 0; i < chain.length; i++) {
|
|
945
|
-
const preset = chain[i];
|
|
946
|
-
const cond = i === 0 ? 'if' : 'elif';
|
|
947
|
-
commands.push(`${cond} [ -n "$__ci_lockfile_${i}" ] && [ -f "$__ci_lockfile_${i}" ]; then`);
|
|
948
|
-
commands.push(` LOCKFILE="$__ci_lockfile_${i}"`);
|
|
949
|
-
commands.push(` CHECKSUM=$(shasum -a 256 "$LOCKFILE" | cut -d ' ' -f1 | head -c 16)`);
|
|
950
|
-
commands.push(` CACHE_KEY="${this.escapeBash(preset.keyPrefix)}-\${__ci_proj}-\${CHECKSUM}"`);
|
|
951
|
-
}
|
|
952
|
-
commands.push('else');
|
|
953
|
-
commands.push(` echo "Warning: no lockfile found, using fallback cache key"`);
|
|
954
|
-
commands.push(` CACHE_KEY="${this.escapeBash(chain[0].keyPrefix)}-\${__ci_proj}-no-lockfile"`);
|
|
955
|
-
commands.push('fi');
|
|
956
|
-
}
|
|
957
|
-
commands.push('');
|
|
958
|
-
if (isDebugMode) {
|
|
959
|
-
commands.push('echo "Cache key: $CACHE_KEY"');
|
|
960
|
-
}
|
|
961
|
-
commands.push('CACHE_FILE="$CACHE_DIR/$CACHE_KEY.tar.zst"');
|
|
962
|
-
commands.push('');
|
|
963
|
-
// Collect paths to cache — use all paths from all presets in the chain,
|
|
964
|
-
// only archiving the ones that actually exist on disk
|
|
965
|
-
commands.push('# Collect paths to cache');
|
|
966
|
-
commands.push('PATHS_TO_CACHE=()');
|
|
967
|
-
const allPaths = [...new Set(chain.flatMap(p => p.paths))];
|
|
968
|
-
for (const p of allPaths) {
|
|
969
|
-
commands.push(`EXPANDED="${this.escapeBash(p)}"`);
|
|
970
|
-
commands.push('EXPANDED="${EXPANDED/#~/${CIBUILD_USER_HOME:-$HOME}}"');
|
|
971
|
-
commands.push('if [ -e "$EXPANDED" ]; then');
|
|
972
|
-
commands.push(' PATHS_TO_CACHE+=("$EXPANDED")');
|
|
973
|
-
if (isDebugMode) {
|
|
974
|
-
commands.push(' echo "Found: $EXPANDED"');
|
|
975
|
-
}
|
|
976
|
-
commands.push('else');
|
|
977
|
-
commands.push(` echo "Warning: Path does not exist: $EXPANDED"`);
|
|
978
|
-
commands.push('fi');
|
|
979
|
-
}
|
|
980
|
-
commands.push('');
|
|
981
|
-
// Always re-archive on success: ~/.gradle/caches and friends are
|
|
982
|
-
// write-through accumulators — same fingerprint key does NOT imply
|
|
983
|
-
// unchanged contents (Gradle's build-cache-1 keeps appending task
|
|
984
|
-
// outputs as sources change). Skipping here would freeze the tarball
|
|
985
|
-
// at first-mint and let it drift further from current state on every
|
|
986
|
-
// build. Atomic .tmp + mv overwrites any prior tarball under this key.
|
|
987
|
-
commands.push('if [ ${#PATHS_TO_CACHE[@]} -gt 0 ]; then');
|
|
988
|
-
commands.push(' echo "Caching ${#PATHS_TO_CACHE[@]} path(s)..."');
|
|
989
|
-
// Transport split, mirroring cache-pull. Over the daemon the tarball is
|
|
990
|
-
// streamed straight out as the PUT body — nothing is staged on the guest,
|
|
991
|
-
// so there is no temp file to collide or orphan, and the daemon does the
|
|
992
|
-
// atomic temp+rename on the host side where the cache actually lives.
|
|
993
|
-
commands.push(' if [ -n "$CIBUILD_CACHE_DAEMON" ]; then');
|
|
994
|
-
commands.push(...emitDaemonPushCommands());
|
|
995
|
-
commands.push(' else');
|
|
996
|
-
// Per-process-unique temp (CBR-12). The old fixed "$CACHE_FILE.tmp" assumed
|
|
997
|
-
// a single serialized writer — false with two VM slots, and it collides with
|
|
998
|
-
// the host cache daemon mutating this VirtioFS-shared dir, which (a) made the
|
|
999
|
-
// guest's own .tmp transiently vanish at `mv` (silent clobber, masked by
|
|
1000
|
-
// `|| true`) and (b) dropped VirtioFS off its async fast path. A unique name
|
|
1001
|
-
// removes the collision; the atomic mv into place is preserved, and a failed
|
|
1002
|
-
// compress/mv removes its own temp (no orphan) while staying non-fatal.
|
|
1003
|
-
commands.push(' CACHE_TMP="$CACHE_FILE.$$.$RANDOM.tmp"');
|
|
1004
|
-
commands.push(' tar -cf - "${PATHS_TO_CACHE[@]}" 2>/dev/null | zstd -3 > "$CACHE_TMP" && mv "$CACHE_TMP" "$CACHE_FILE" || rm -f "$CACHE_TMP"');
|
|
1005
|
-
commands.push(' if [ -f "$CACHE_FILE" ]; then');
|
|
1006
|
-
commands.push(' echo "Cache created successfully"');
|
|
1007
|
-
if (isDebugMode) {
|
|
1008
|
-
commands.push(' echo "Cache size: $(du -h "$CACHE_FILE" | cut -f1)"');
|
|
1009
|
-
}
|
|
1010
|
-
// Retention is no longer applied here (ADR 0002 / CBR-6). It moved into the
|
|
1011
|
-
// long-lived peer cache daemon, which owns ~/cache and — unlike the guest —
|
|
1012
|
-
// knows (via the manager) which scopes this runner OWNS and must never evict.
|
|
1013
|
-
// A guest-side prune would have to run blind to ownership and could reap the
|
|
1014
|
-
// fleet's single durable copy, so the daemon owns the whole policy now.
|
|
1015
|
-
commands.push(' else');
|
|
1016
|
-
commands.push(' echo "Warning: Failed to create cache file"');
|
|
1017
|
-
commands.push(' fi');
|
|
1018
|
-
// Closes the daemon/filesystem transport split.
|
|
1019
|
-
commands.push(' fi');
|
|
1020
|
-
commands.push('else');
|
|
1021
|
-
commands.push(' echo "No paths to cache"');
|
|
1022
|
-
commands.push('fi');
|
|
1023
|
-
const script = this.createBashScriptFromCommands(commands, stepName);
|
|
1024
|
-
return this.createScriptStep(script, stepName);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
//# sourceMappingURL=cache.js.map
|