checkly 9.5.0 → 10.0.0-rc.1
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/ai-context/context.js +1 -0
- package/dist/ai-context/context.js.map +1 -1
- package/dist/ai-context/onboarding-boilerplate/checkly-config-template.ts +2 -0
- package/dist/ai-context/public-skills/checkly/SKILL.md +18 -5
- package/dist/ai-context/skills-command/references/communicate.md +6 -3
- package/dist/ai-context/skills-command/references/configure-api-checks.md +1 -9
- package/dist/ai-context/skills-command/references/configure-browser-checks.md +1 -8
- package/dist/ai-context/skills-command/references/configure-check-groups.md +1 -0
- package/dist/ai-context/skills-command/references/configure-dns-monitors.md +1 -9
- package/dist/ai-context/skills-command/references/configure-grpc-monitors.md +24 -0
- package/dist/ai-context/skills-command/references/configure-heartbeat-monitors.md +1 -9
- package/dist/ai-context/skills-command/references/configure-icmp-monitors.md +1 -9
- package/dist/ai-context/skills-command/references/configure-multistep-checks.md +1 -9
- package/dist/ai-context/skills-command/references/configure-playwright-checks.md +1 -1
- package/dist/ai-context/skills-command/references/configure-supporting-constructs.md +6 -2
- package/dist/ai-context/skills-command/references/configure-tcp-monitors.md +1 -10
- package/dist/ai-context/skills-command/references/configure-url-monitors.md +1 -10
- package/dist/ai-context/skills-command/references/configure.md +16 -5
- package/dist/ai-context/skills-command/references/initialize.md +11 -3
- package/dist/ai-context/skills-command/references/investigate-checks.md +1 -1
- package/dist/auth/api-key.d.ts +24 -0
- package/dist/auth/api-key.js +104 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/device-flow.d.ts +57 -0
- package/dist/auth/device-flow.js +221 -0
- package/dist/auth/device-flow.js.map +1 -0
- package/dist/auth/index.d.ts +6 -6
- package/dist/auth/index.js +64 -64
- package/dist/auth/index.js.map +1 -1
- package/dist/commands/api.js +43 -10
- package/dist/commands/api.js.map +1 -1
- package/dist/commands/authCommand.d.ts +2 -0
- package/dist/commands/authCommand.js +77 -7
- package/dist/commands/authCommand.js.map +1 -1
- package/dist/commands/debug/parse-project.d.ts +1 -0
- package/dist/commands/debug/parse-project.js +5 -2
- package/dist/commands/debug/parse-project.js.map +1 -1
- package/dist/commands/deploy.d.ts +7 -1
- package/dist/commands/deploy.js +627 -271
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/import/plan.js +18 -4
- package/dist/commands/import/plan.js.map +1 -1
- package/dist/commands/init.js +29 -13
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.d.ts +18 -2
- package/dist/commands/login.js +681 -48
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/logout.js +7 -1
- package/dist/commands/logout.js.map +1 -1
- package/dist/commands/pw-test.js +3 -2
- package/dist/commands/pw-test.js.map +1 -1
- package/dist/commands/switch.js +23 -10
- package/dist/commands/switch.js.map +1 -1
- package/dist/commands/test.js +16 -17
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/trigger.js +4 -3
- package/dist/commands/trigger.js.map +1 -1
- package/dist/commands/validate.js +1 -0
- package/dist/commands/validate.js.map +1 -1
- package/dist/constants.d.ts +1 -2
- package/dist/constants.js +0 -1
- package/dist/constants.js.map +1 -1
- package/dist/constructs/alert-channel-codegen.d.ts +6 -1
- package/dist/constructs/alert-channel-codegen.js +12 -6
- package/dist/constructs/alert-channel-codegen.js.map +1 -1
- package/dist/constructs/alert-escalation-policy-codegen.d.ts +7 -0
- package/dist/constructs/alert-escalation-policy-codegen.js +25 -8
- package/dist/constructs/alert-escalation-policy-codegen.js.map +1 -1
- package/dist/constructs/api-assertion.d.ts +0 -2
- package/dist/constructs/api-assertion.js +0 -4
- package/dist/constructs/api-assertion.js.map +1 -1
- package/dist/constructs/api-check-codegen.js +1 -1
- package/dist/constructs/api-check-codegen.js.map +1 -1
- package/dist/constructs/api-check.d.ts +4 -22
- package/dist/constructs/api-check.js +15 -17
- package/dist/constructs/api-check.js.map +1 -1
- package/dist/constructs/api-request-codegen.js +10 -8
- package/dist/constructs/api-request-codegen.js.map +1 -1
- package/dist/constructs/browser-check-bundle.d.ts +19 -13
- package/dist/constructs/browser-check-bundle.js +5 -1
- package/dist/constructs/browser-check-bundle.js.map +1 -1
- package/dist/constructs/browser-check.d.ts +1 -4
- package/dist/constructs/browser-check.js +1 -1
- package/dist/constructs/browser-check.js.map +1 -1
- package/dist/constructs/check-codegen.d.ts +39 -14
- package/dist/constructs/check-codegen.js +101 -23
- package/dist/constructs/check-codegen.js.map +1 -1
- package/dist/constructs/check-group-codegen.js +23 -9
- package/dist/constructs/check-group-codegen.js.map +1 -1
- package/dist/constructs/check-group-v1.d.ts +19 -14
- package/dist/constructs/check-group-v1.js +39 -14
- package/dist/constructs/check-group-v1.js.map +1 -1
- package/dist/constructs/check-group-v2.d.ts +0 -20
- package/dist/constructs/check-group-v2.js +0 -4
- package/dist/constructs/check-group-v2.js.map +1 -1
- package/dist/constructs/check.d.ts +45 -32
- package/dist/constructs/check.js +78 -17
- package/dist/constructs/check.js.map +1 -1
- package/dist/constructs/construct-codegen.d.ts +1 -2
- package/dist/constructs/construct-diagnostics.d.ts +3 -0
- package/dist/constructs/construct-diagnostics.js +11 -0
- package/dist/constructs/construct-diagnostics.js.map +1 -1
- package/dist/constructs/construct.d.ts +25 -4
- package/dist/constructs/construct.js +41 -5
- package/dist/constructs/construct.js.map +1 -1
- package/dist/constructs/dns-request-codegen.js +1 -1
- package/dist/constructs/dns-request-codegen.js.map +1 -1
- package/dist/constructs/email-alert-channel-codegen.js +1 -1
- package/dist/constructs/email-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/frequency-codegen.js +10 -2
- package/dist/constructs/frequency-codegen.js.map +1 -1
- package/dist/constructs/frequency.d.ts +8 -3
- package/dist/constructs/frequency.js +7 -2
- package/dist/constructs/frequency.js.map +1 -1
- package/dist/constructs/grpc-monitor.js +19 -0
- package/dist/constructs/grpc-monitor.js.map +1 -1
- package/dist/constructs/grpc-request-codegen.js +13 -4
- package/dist/constructs/grpc-request-codegen.js.map +1 -1
- package/dist/constructs/grpc-request.d.ts +23 -0
- package/dist/constructs/heartbeat-monitor.d.ts +2 -1
- package/dist/constructs/heartbeat-monitor.js +2 -2
- package/dist/constructs/heartbeat-monitor.js.map +1 -1
- package/dist/constructs/icmp-request-codegen.js +1 -1
- package/dist/constructs/icmp-request-codegen.js.map +1 -1
- package/dist/constructs/incidentio-alert-channel-codegen.d.ts +1 -1
- package/dist/constructs/incidentio-alert-channel-codegen.js +14 -8
- package/dist/constructs/incidentio-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/index.d.ts +1 -1
- package/dist/constructs/index.js +1 -1
- package/dist/constructs/index.js.map +1 -1
- package/dist/constructs/internal/assertion-validation.js +1 -2
- package/dist/constructs/internal/assertion-validation.js.map +1 -1
- package/dist/constructs/internal/codegen/context.d.ts +73 -6
- package/dist/constructs/internal/codegen/context.js +78 -6
- package/dist/constructs/internal/codegen/context.js.map +1 -1
- package/dist/constructs/internal/codegen/index.d.ts +3 -1
- package/dist/constructs/internal/codegen/index.js +2 -1
- package/dist/constructs/internal/codegen/index.js.map +1 -1
- package/dist/constructs/internal/codegen/render.d.ts +68 -0
- package/dist/constructs/internal/codegen/render.js +75 -0
- package/dist/constructs/internal/codegen/render.js.map +1 -0
- package/dist/constructs/internal/common-diagnostics.d.ts +1 -2
- package/dist/constructs/internal/common-diagnostics.js +6 -14
- package/dist/constructs/internal/common-diagnostics.js.map +1 -1
- package/dist/constructs/internal/declaring-file.d.ts +39 -0
- package/dist/constructs/internal/declaring-file.js +139 -0
- package/dist/constructs/internal/declaring-file.js.map +1 -0
- package/dist/constructs/internal/retry-strategy-defaults.d.ts +13 -0
- package/dist/constructs/internal/retry-strategy-defaults.js +14 -0
- package/dist/constructs/internal/retry-strategy-defaults.js.map +1 -0
- package/dist/constructs/key-value-pair-codegen.d.ts +6 -1
- package/dist/constructs/key-value-pair-codegen.js +24 -3
- package/dist/constructs/key-value-pair-codegen.js.map +1 -1
- package/dist/constructs/maintenance-window-codegen.d.ts +7 -0
- package/dist/constructs/maintenance-window-codegen.js +36 -19
- package/dist/constructs/maintenance-window-codegen.js.map +1 -1
- package/dist/constructs/maintenance-window.d.ts +35 -3
- package/dist/constructs/maintenance-window.js +35 -1
- package/dist/constructs/maintenance-window.js.map +1 -1
- package/dist/constructs/monitor.d.ts +1 -1
- package/dist/constructs/msteams-alert-channel-codegen.d.ts +1 -1
- package/dist/constructs/msteams-alert-channel-codegen.js +8 -18
- package/dist/constructs/msteams-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/opsgenie-alert-channel-codegen.js +1 -1
- package/dist/constructs/opsgenie-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/pagerduty-alert-channel-codegen.js +1 -1
- package/dist/constructs/pagerduty-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/phone-call-alert-channel-codegen.js +1 -1
- package/dist/constructs/phone-call-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/playwright-check-bundle.d.ts +7 -1
- package/dist/constructs/playwright-check-bundle.js +3 -0
- package/dist/constructs/playwright-check-bundle.js.map +1 -1
- package/dist/constructs/playwright-check-codegen.d.ts +85 -0
- package/dist/constructs/playwright-check-codegen.js +212 -0
- package/dist/constructs/playwright-check-codegen.js.map +1 -0
- package/dist/constructs/playwright-check.d.ts +14 -24
- package/dist/constructs/playwright-check.js +16 -52
- package/dist/constructs/playwright-check.js.map +1 -1
- package/dist/constructs/retry-strategy-codegen.d.ts +7 -1
- package/dist/constructs/retry-strategy-codegen.js +41 -32
- package/dist/constructs/retry-strategy-codegen.js.map +1 -1
- package/dist/constructs/retry-strategy.d.ts +0 -4
- package/dist/constructs/retry-strategy.js +5 -8
- package/dist/constructs/retry-strategy.js.map +1 -1
- package/dist/constructs/session.d.ts +26 -1
- package/dist/constructs/session.js +33 -2
- package/dist/constructs/session.js.map +1 -1
- package/dist/constructs/slack-alert-channel-codegen.js +1 -1
- package/dist/constructs/slack-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/slack-app-alert-channel-codegen.js +1 -1
- package/dist/constructs/slack-app-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/sms-alert-channel-codegen.js +1 -1
- package/dist/constructs/sms-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/ssl-request-codegen.js +4 -3
- package/dist/constructs/ssl-request-codegen.js.map +1 -1
- package/dist/constructs/status-page-v3-automation-rule-codegen.js +5 -4
- package/dist/constructs/status-page-v3-automation-rule-codegen.js.map +1 -1
- package/dist/constructs/status-page-v3-codegen.js +3 -2
- package/dist/constructs/status-page-v3-codegen.js.map +1 -1
- package/dist/constructs/status-page-v3-component-codegen.js +8 -7
- package/dist/constructs/status-page-v3-component-codegen.js.map +1 -1
- package/dist/constructs/tcp-monitor-codegen.js +3 -2
- package/dist/constructs/tcp-monitor-codegen.js.map +1 -1
- package/dist/constructs/tcp-monitor.d.ts +4 -3
- package/dist/constructs/tcp-monitor.js +3 -2
- package/dist/constructs/tcp-monitor.js.map +1 -1
- package/dist/constructs/telegram-alert-channel-codegen.d.ts +1 -1
- package/dist/constructs/telegram-alert-channel-codegen.js +14 -8
- package/dist/constructs/telegram-alert-channel-codegen.js.map +1 -1
- package/dist/constructs/time-zone.d.ts +24 -0
- package/dist/constructs/time-zone.js +455 -0
- package/dist/constructs/time-zone.js.map +1 -0
- package/dist/constructs/traceroute-request-codegen.js +2 -2
- package/dist/constructs/traceroute-request-codegen.js.map +1 -1
- package/dist/constructs/url-monitor-codegen.js +1 -1
- package/dist/constructs/url-monitor-codegen.js.map +1 -1
- package/dist/constructs/url-monitor.d.ts +3 -2
- package/dist/constructs/url-monitor.js +3 -0
- package/dist/constructs/url-monitor.js.map +1 -1
- package/dist/constructs/url-request-codegen.js +4 -3
- package/dist/constructs/url-request-codegen.js.map +1 -1
- package/dist/constructs/webhook-alert-channel-codegen.js +3 -3
- package/dist/constructs/webhook-alert-channel-codegen.js.map +1 -1
- package/dist/formatters/check-result-detail.js +0 -185
- package/dist/formatters/check-result-detail.js.map +1 -1
- package/dist/helpers/activate-account.d.ts +14 -0
- package/dist/helpers/activate-account.js +25 -0
- package/dist/helpers/activate-account.js.map +1 -0
- package/dist/helpers/cli-mode.d.ts +18 -0
- package/dist/helpers/cli-mode.js +41 -2
- package/dist/helpers/cli-mode.js.map +1 -1
- package/dist/helpers/command-preview.d.ts +44 -0
- package/dist/helpers/command-preview.js +29 -9
- package/dist/helpers/command-preview.js.map +1 -1
- package/dist/helpers/flags.d.ts +10 -0
- package/dist/helpers/flags.js +17 -0
- package/dist/helpers/flags.js.map +1 -1
- package/dist/helpers/onboarding/boilerplate.js +13 -1
- package/dist/helpers/onboarding/boilerplate.js.map +1 -1
- package/dist/helpers/onboarding/index.d.ts +1 -1
- package/dist/helpers/onboarding/index.js +1 -1
- package/dist/helpers/onboarding/index.js.map +1 -1
- package/dist/helpers/onboarding/messages.d.ts +6 -0
- package/dist/helpers/onboarding/messages.js +14 -0
- package/dist/helpers/onboarding/messages.js.map +1 -1
- package/dist/helpers/onboarding/skill-install.js +2 -2
- package/dist/helpers/onboarding/skill-install.js.map +1 -1
- package/dist/helpers/wrap.js +1 -1
- package/dist/helpers/wrap.js.map +1 -1
- package/dist/reporters/abstract-list.js +11 -2
- package/dist/reporters/abstract-list.js.map +1 -1
- package/dist/reporters/github.js +3 -2
- package/dist/reporters/github.js.map +1 -1
- package/dist/reporters/util.d.ts +1 -0
- package/dist/reporters/util.js +5 -58
- package/dist/reporters/util.js.map +1 -1
- package/dist/rest/analytics.js +0 -4
- package/dist/rest/analytics.js.map +1 -1
- package/dist/rest/api.d.ts +8 -1
- package/dist/rest/api.js +21 -3
- package/dist/rest/api.js.map +1 -1
- package/dist/rest/check-results.d.ts +1 -50
- package/dist/rest/check-results.js.map +1 -1
- package/dist/rest/checkly-storage.d.ts +6 -1
- package/dist/rest/checkly-storage.js +13 -2
- package/dist/rest/checkly-storage.js.map +1 -1
- package/dist/rest/errors.d.ts +13 -0
- package/dist/rest/errors.js +5 -1
- package/dist/rest/errors.js.map +1 -1
- package/dist/rest/projects.d.ts +254 -4
- package/dist/rest/projects.js +184 -12
- package/dist/rest/projects.js.map +1 -1
- package/dist/services/abstract-check-runner.d.ts +11 -0
- package/dist/services/abstract-check-runner.js +98 -46
- package/dist/services/abstract-check-runner.js.map +1 -1
- package/dist/services/check-parser/bundler.d.ts +26 -0
- package/dist/services/check-parser/bundler.js +81 -7
- package/dist/services/check-parser/bundler.js.map +1 -1
- package/dist/services/check-parser/cache-hash.d.ts +1 -4
- package/dist/services/check-parser/cache-hash.js.map +1 -1
- package/dist/services/checkly-config-codegen.js +1 -4
- package/dist/services/checkly-config-codegen.js.map +1 -1
- package/dist/services/checkly-config-loader.d.ts +7 -33
- package/dist/services/checkly-config-loader.js +50 -47
- package/dist/services/checkly-config-loader.js.map +1 -1
- package/dist/services/config.d.ts +2 -0
- package/dist/services/config.js +14 -0
- package/dist/services/config.js.map +1 -1
- package/dist/services/content-hash.d.ts +14 -0
- package/dist/services/content-hash.js +22 -0
- package/dist/services/content-hash.js.map +1 -0
- package/dist/services/deploy-diff/diff-lines.d.ts +38 -0
- package/dist/services/deploy-diff/diff-lines.js +191 -0
- package/dist/services/deploy-diff/diff-lines.js.map +1 -0
- package/dist/services/deploy-diff/import-shape.d.ts +171 -0
- package/dist/services/deploy-diff/import-shape.js +633 -0
- package/dist/services/deploy-diff/import-shape.js.map +1 -0
- package/dist/services/deploy-diff/legacy-payload.d.ts +22 -0
- package/dist/services/deploy-diff/legacy-payload.js +51 -0
- package/dist/services/deploy-diff/legacy-payload.js.map +1 -0
- package/dist/services/deploy-diff/plan-summary.d.ts +37 -0
- package/dist/services/deploy-diff/plan-summary.js +154 -0
- package/dist/services/deploy-diff/plan-summary.js.map +1 -0
- package/dist/services/deploy-diff/preview-output.d.ts +61 -0
- package/dist/services/deploy-diff/preview-output.js +394 -0
- package/dist/services/deploy-diff/preview-output.js.map +1 -0
- package/dist/services/deploy-diff/render.d.ts +53 -0
- package/dist/services/deploy-diff/render.js +317 -0
- package/dist/services/deploy-diff/render.js.map +1 -0
- package/dist/services/deploy-diff/shape-changes.d.ts +2 -0
- package/dist/services/deploy-diff/shape-changes.js +35 -0
- package/dist/services/deploy-diff/shape-changes.js.map +1 -0
- package/dist/services/deploy-diff/source-index.d.ts +18 -0
- package/dist/services/deploy-diff/source-index.js +48 -0
- package/dist/services/deploy-diff/source-index.js.map +1 -0
- package/dist/services/deploy-diff/variable-names.d.ts +30 -0
- package/dist/services/deploy-diff/variable-names.js +64 -0
- package/dist/services/deploy-diff/variable-names.js.map +1 -0
- package/dist/services/project-parser.d.ts +5 -0
- package/dist/services/project-parser.js +25 -13
- package/dist/services/project-parser.js.map +1 -1
- package/dist/services/snapshot-service.d.ts +25 -8
- package/dist/services/snapshot-service.js +46 -4
- package/dist/services/snapshot-service.js.map +1 -1
- package/dist/services/test-filters.d.ts +18 -0
- package/dist/services/test-filters.js +10 -0
- package/dist/services/test-filters.js.map +1 -1
- package/dist/services/test-runner.js +7 -3
- package/dist/services/test-runner.js.map +1 -1
- package/dist/services/test-session-cancel.js +1 -1
- package/dist/services/test-session-cancel.js.map +1 -1
- package/dist/services/util.d.ts +17 -2
- package/dist/services/util.js +118 -11
- package/dist/services/util.js.map +1 -1
- package/dist/services/write-back/apply-edits.d.ts +29 -0
- package/dist/services/write-back/apply-edits.js +255 -0
- package/dist/services/write-back/apply-edits.js.map +1 -0
- package/dist/services/write-back/helper-edit.d.ts +135 -0
- package/dist/services/write-back/helper-edit.js +343 -0
- package/dist/services/write-back/helper-edit.js.map +1 -0
- package/dist/services/write-back/imports.d.ts +30 -0
- package/dist/services/write-back/imports.js +82 -0
- package/dist/services/write-back/imports.js.map +1 -0
- package/dist/services/write-back/literal-edit.d.ts +183 -0
- package/dist/services/write-back/literal-edit.js +359 -0
- package/dist/services/write-back/literal-edit.js.map +1 -0
- package/dist/services/write-back/output.d.ts +26 -0
- package/dist/services/write-back/output.js +110 -0
- package/dist/services/write-back/output.js.map +1 -0
- package/dist/services/write-back/plan.d.ts +173 -0
- package/dist/services/write-back/plan.js +968 -0
- package/dist/services/write-back/plan.js.map +1 -0
- package/dist/services/write-back/source-file.d.ts +118 -0
- package/dist/services/write-back/source-file.js +432 -0
- package/dist/services/write-back/source-file.js.map +1 -0
- package/dist/sourcegen/index.d.ts +1 -1
- package/dist/sourcegen/index.js +1 -1
- package/dist/sourcegen/index.js.map +1 -1
- package/dist/sourcegen/program.d.ts +19 -1
- package/dist/sourcegen/program.js +32 -7
- package/dist/sourcegen/program.js.map +1 -1
- package/oclif.manifest.json +676 -635
- package/package.json +13 -13
- package/dist/commands/rules.d.ts +0 -9
- package/dist/commands/rules.js +0 -13
- package/dist/commands/rules.js.map +0 -1
- package/dist/constructs/agentic-check-codegen.d.ts +0 -22
- package/dist/constructs/agentic-check-codegen.js +0 -59
- package/dist/constructs/agentic-check-codegen.js.map +0 -1
- package/dist/constructs/agentic-check.d.ts +0 -237
- package/dist/constructs/agentic-check.js +0 -100
- package/dist/constructs/agentic-check.js.map +0 -1
package/dist/commands/deploy.js
CHANGED
|
@@ -5,45 +5,96 @@ import { Flags } from '@oclif/core';
|
|
|
5
5
|
import { AuthCommand } from './authCommand.js';
|
|
6
6
|
import { parseProject } from '../services/project-parser.js';
|
|
7
7
|
import { loadChecklyConfig, resolveDependencyCacheVersion } from '../services/checkly-config-loader.js';
|
|
8
|
-
import {
|
|
8
|
+
import { Session } from '../constructs/index.js';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { splitConfigFilePath, getGitInformation, getGitRepoRoot } from '../services/util.js';
|
|
11
11
|
import commonMessages from '../messages/common-messages.js';
|
|
12
|
-
import { forceFlag } from '../helpers/flags.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
12
|
+
import { autoOrWholeNumberFlag, dryRunFlag, forceFlag } from '../helpers/flags.js';
|
|
13
|
+
import { formatPreview, NON_REPORTED_TYPES, PRETTY_RESOURCE_TYPES, ResourceDeployStatus, } from '../services/deploy-diff/preview-output.js';
|
|
14
|
+
import { DEPLOY_PLAN_DISABLED, ProjectDeployCancelledError, ProjectPlanStaleError, ProjectPlanDisabledError, ProjectPreviewNotSupportedError, } from '../rest/projects.js';
|
|
15
|
+
import { ConflictError, ValidationError } from '../rest/errors.js';
|
|
16
|
+
import { stripUnsupportedDeployFields } from '../services/deploy-diff/legacy-payload.js';
|
|
17
|
+
import { planChangeLines, planHasNoChanges, reducePlanForAgent } from '../services/deploy-diff/plan-summary.js';
|
|
18
|
+
import { formatWriteBackSkipped, formatWriteBackUpdated } from '../services/write-back/output.js';
|
|
19
|
+
import { applyWriteBack, hasWritableChanges, planWriteBack } from '../services/write-back/plan.js';
|
|
15
20
|
import { uploadSnapshots } from '../services/snapshot-service.js';
|
|
16
21
|
import { BrowserCheckBundle } from '../constructs/browser-check-bundle.js';
|
|
17
22
|
import { Bundler } from '../services/check-parser/bundler.js';
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
23
|
+
/**
|
|
24
|
+
* The deploy payload fields that arrived with the preview endpoint, which a
|
|
25
|
+
* deploy schema older than it rejects by name (`"sourceFile" is not allowed`).
|
|
26
|
+
* Kept in step with `stripUnsupportedDeployFields`.
|
|
27
|
+
*/
|
|
28
|
+
const PREVIEW_ERA_FIELDS = ['sourceFile', 'codeBundleSha256', 'sha256'];
|
|
29
|
+
/**
|
|
30
|
+
* Whether the API refused the payload because of a field an older deploy
|
|
31
|
+
* schema does not know: a 400 whose message names the field as not allowed.
|
|
32
|
+
* Only such a refusal is worth repeating in the older form: any other 400,
|
|
33
|
+
* including a bad value for one of these very fields, describes a problem
|
|
34
|
+
* the older form would not fix, or would hide. A refusal whose body the
|
|
35
|
+
* client recognises arrives as a {@link ValidationError}; one it does not
|
|
36
|
+
* arrives as the raw HTTP error.
|
|
37
|
+
*/
|
|
38
|
+
function rejectsPreviewEraField(err) {
|
|
39
|
+
const status = err instanceof ValidationError ? 400 : err?.response?.status ?? err?.data?.statusCode;
|
|
40
|
+
if (status !== 400) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const message = String(err?.data?.message ?? err?.response?.data?.message ?? err?.message ?? '');
|
|
44
|
+
return message.includes('is not allowed') && PREVIEW_ERA_FIELDS.some(field => message.includes(field));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The further choice a terminal gets when the plan shows a resource edited
|
|
48
|
+
* outside the project and the code can take the edit: write the account's
|
|
49
|
+
* current values into the code and deploy nothing, so the user reviews the
|
|
50
|
+
* diff and deploys again rather than overwriting the edit. Only the
|
|
51
|
+
* properties the write-back knows the construct's spelling of (a literal,
|
|
52
|
+
* or a helper such as `Frequency.EVERY_5M`) can be written; everything
|
|
53
|
+
* else is listed with its reason.
|
|
54
|
+
*/
|
|
55
|
+
function writeBackAlternatives(diff, project, command) {
|
|
56
|
+
if (!hasWritableChanges(diff, project)) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return [{
|
|
60
|
+
title: 'Update my code with the changes made in Checkly (deploys nothing)',
|
|
61
|
+
run: async () => {
|
|
62
|
+
const cwd = process.cwd();
|
|
63
|
+
const writeBack = await planWriteBack({ diff, project, cwd });
|
|
64
|
+
const skipped = writeBack.skipped.length > 0 ? formatWriteBackSkipped({ writeBack, project, cwd }) : undefined;
|
|
65
|
+
command.log();
|
|
66
|
+
if (writeBack.applied.length === 0) {
|
|
67
|
+
if (skipped !== undefined) {
|
|
68
|
+
command.log(skipped);
|
|
69
|
+
}
|
|
70
|
+
command.log('Nothing in the code could be updated automatically, so nothing was changed.');
|
|
71
|
+
command.log('Nothing was deployed.');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
await applyWriteBack(writeBack);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
// What has to be edited by hand is true whether or not the write
|
|
79
|
+
// went through, so it is still said.
|
|
80
|
+
if (skipped !== undefined) {
|
|
81
|
+
command.log(skipped);
|
|
82
|
+
}
|
|
83
|
+
// The error names the files already rewritten, if any.
|
|
84
|
+
command.style.longError('Could not update your code.', err.message);
|
|
85
|
+
command.log('Nothing was deployed.');
|
|
86
|
+
command.exit(1);
|
|
87
|
+
}
|
|
88
|
+
// Reported once the files hold it, not as an intention; what could not
|
|
89
|
+
// be written follows what was.
|
|
90
|
+
command.log(formatWriteBackUpdated({ writeBack, project, cwd }));
|
|
91
|
+
if (skipped !== undefined) {
|
|
92
|
+
command.log(skipped);
|
|
93
|
+
}
|
|
94
|
+
command.log('Nothing was deployed. Review with `git diff`, then run `npx checkly deploy` again.');
|
|
95
|
+
},
|
|
96
|
+
}];
|
|
97
|
+
}
|
|
47
98
|
export default class Deploy extends AuthCommand {
|
|
48
99
|
static coreCommand = true;
|
|
49
100
|
static hidden = false;
|
|
@@ -66,15 +117,53 @@ export default class Deploy extends AuthCommand {
|
|
|
66
117
|
default: false,
|
|
67
118
|
}),
|
|
68
119
|
'schedule-on-deploy': Flags.boolean({
|
|
69
|
-
description: '
|
|
70
|
-
|
|
120
|
+
description: 'Schedule checks to run right after the deploy instead of waiting for their next scheduled run. '
|
|
121
|
+
+ 'The other --schedule-on-deploy-* flags refine which checks it schedules.',
|
|
122
|
+
default: false,
|
|
71
123
|
allowNo: true,
|
|
72
124
|
}),
|
|
125
|
+
'schedule-on-deploy-scope': Flags.string({
|
|
126
|
+
description: 'Which checks to schedule after the deploy: "changed" schedules the checks the deploy created or '
|
|
127
|
+
+ 'updated, "all" also the ones it left unchanged.',
|
|
128
|
+
options: ['changed', 'all'],
|
|
129
|
+
default: 'changed',
|
|
130
|
+
}),
|
|
131
|
+
'schedule-on-deploy-threshold': autoOrWholeNumberFlag({
|
|
132
|
+
description: 'Schedule no checks after the deploy if it would schedule more than this many, not counting the '
|
|
133
|
+
+ 'ones --schedule-on-deploy-scope and --schedule-on-deploy-min-frequency leave out. '
|
|
134
|
+
+ '"auto" leaves the threshold to Checkly, which also caps the number you can set.',
|
|
135
|
+
default: 'auto',
|
|
136
|
+
}),
|
|
137
|
+
'schedule-on-deploy-min-frequency': autoOrWholeNumberFlag({
|
|
138
|
+
description: 'After the deploy, schedule only the checks that run every this many minutes or less often; '
|
|
139
|
+
+ 'more frequent checks wait for their next scheduled run. Goes by the frequency Checkly resolves for each '
|
|
140
|
+
+ 'check, its defaults included. 0 schedules every check. "auto" leaves the minimum to Checkly, which also '
|
|
141
|
+
+ 'caps the number you can set.',
|
|
142
|
+
default: 'auto',
|
|
143
|
+
}),
|
|
73
144
|
'preserve-resources': Flags.boolean({
|
|
74
145
|
description: 'Keep resources removed from code (and their run history) in your Checkly account instead of deleting them.',
|
|
75
146
|
default: false,
|
|
76
147
|
}),
|
|
77
148
|
'force': forceFlag(),
|
|
149
|
+
'dry-run': dryRunFlag(),
|
|
150
|
+
'plan': Flags.boolean({
|
|
151
|
+
description: 'Plan the deploy first: Checkly says what would change, property by property, compared '
|
|
152
|
+
+ 'with what is deployed. The deploy then applies that plan, leaves unchanged resources alone and '
|
|
153
|
+
+ 'refuses to run if your Checkly account changed in between. --no-plan deploys without a plan '
|
|
154
|
+
+ 'and writes every resource.',
|
|
155
|
+
default: true,
|
|
156
|
+
allowNo: true,
|
|
157
|
+
}),
|
|
158
|
+
'plan-token': Flags.string({
|
|
159
|
+
description: 'Deploy only if the plan still matches this token from an earlier run. '
|
|
160
|
+
+ 'Aborts if anything changed in your Checkly account since then. Not available with --no-plan.',
|
|
161
|
+
}),
|
|
162
|
+
'prune-relations': Flags.boolean({
|
|
163
|
+
description: 'Delete the alert channel subscriptions and private location assignments on this project\'s '
|
|
164
|
+
+ 'checks and groups that the project does not manage. Not available with --no-plan.',
|
|
165
|
+
default: false,
|
|
166
|
+
}),
|
|
78
167
|
'cancel-in-progress-deployment': Flags.boolean({
|
|
79
168
|
description: 'If a deployment for this project is already in progress, cancel it instead of waiting for it to finish.',
|
|
80
169
|
default: false,
|
|
@@ -102,33 +191,45 @@ export default class Deploy extends AuthCommand {
|
|
|
102
191
|
};
|
|
103
192
|
async run() {
|
|
104
193
|
const { flags, metadata } = await this.parse(Deploy);
|
|
105
|
-
const { force, preview, '
|
|
194
|
+
const { force, preview, 'dry-run': dryRun,
|
|
195
|
+
// Named for what it is, the user's request: `plan` further down is the
|
|
196
|
+
// plan Checkly answered with, which a run that asked may still not have.
|
|
197
|
+
'plan': planRequested, 'plan-token': requestedPlanToken, 'prune-relations': pruneRelations, 'cancel-in-progress-deployment': cancelInProgress, 'schedule-on-deploy': scheduleOnDeploy, 'schedule-on-deploy-scope': scheduleScope, 'schedule-on-deploy-threshold': scheduleThreshold, 'schedule-on-deploy-min-frequency': scheduleMinFrequency, 'preserve-resources': preserveResources, output: outputFlag, verbose, config: configFilename, 'verify-runtime-dependencies': verifyRuntimeDependencies, 'debug-bundle': debugBundle, 'debug-bundle-output-file': debugBundleOutputFile, } = flags;
|
|
106
198
|
const output = outputFlag || verbose;
|
|
199
|
+
// Both are promises only a plan can keep: the token, that nothing is
|
|
200
|
+
// deployed unless the account still is what was reviewed, and pruning,
|
|
201
|
+
// that what gets deleted was listed first. Checked here rather than with
|
|
202
|
+
// the flags' `dependsOn`, which a flag that has a default always satisfies.
|
|
203
|
+
if (!planRequested) {
|
|
204
|
+
const needsPlan = requestedPlanToken !== undefined
|
|
205
|
+
? '--plan-token'
|
|
206
|
+
: pruneRelations ? '--prune-relations' : undefined;
|
|
207
|
+
if (needsPlan !== undefined) {
|
|
208
|
+
this.style.longError(`${needsPlan} applies to a planned deploy only.`, `Re-run without --no-plan, or without ${needsPlan}.`);
|
|
209
|
+
this.exit(1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// The flags that refine scheduling mean nothing on a deploy that schedules
|
|
213
|
+
// no checks. Passing one is taken as having expected scheduling, which is
|
|
214
|
+
// opt-in, so the deploy stops rather than quietly scheduling nothing.
|
|
215
|
+
if (!scheduleOnDeploy) {
|
|
216
|
+
const refiningFlag = [
|
|
217
|
+
'schedule-on-deploy-scope',
|
|
218
|
+
'schedule-on-deploy-threshold',
|
|
219
|
+
'schedule-on-deploy-min-frequency',
|
|
220
|
+
].find(name => metadata.flags[name]?.setFromDefault !== true);
|
|
221
|
+
if (refiningFlag !== undefined) {
|
|
222
|
+
this.style.longError(`--${refiningFlag} applies only when checks are scheduled after the deploy.`, `Add --schedule-on-deploy to schedule them, or drop --${refiningFlag}.`);
|
|
223
|
+
this.exit(1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
107
226
|
const { configDirectory, configFilenames } = splitConfigFilePath(configFilename);
|
|
108
227
|
const { config: checklyConfig, constructs: checklyConfigConstructs, diagnostics: configDiagnostics, } = await loadChecklyConfig(configDirectory, configFilenames);
|
|
109
228
|
const account = this.account;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
changes: [
|
|
115
|
-
`Deploy project "${checklyConfig.projectName}" to account "${account.name}"`,
|
|
116
|
-
scheduleOnDeploy
|
|
117
|
-
? 'Schedule checks after deploy'
|
|
118
|
-
: 'Checks will NOT be scheduled after deploy',
|
|
119
|
-
preserveResources
|
|
120
|
-
? 'Keep any resources removed from code (and their run history) in your Checkly account, where you can manage them from the Checkly web app'
|
|
121
|
-
: 'Delete any resources removed from code, losing their run history. Pass --preserve-resources to keep them in your Checkly account instead',
|
|
122
|
-
],
|
|
123
|
-
flags,
|
|
124
|
-
flagMetadata: metadata.flags,
|
|
125
|
-
classification: {
|
|
126
|
-
readOnly: Deploy.readOnly,
|
|
127
|
-
destructive: Deploy.destructive,
|
|
128
|
-
idempotent: Deploy.idempotent,
|
|
129
|
-
},
|
|
130
|
-
}, { force });
|
|
131
|
-
}
|
|
229
|
+
// The confirmation happens further down, once the project has been parsed
|
|
230
|
+
// and Checkly has said what the deploy would change, so that one prompt
|
|
231
|
+
// can show the actual plan instead of asking about a deploy nobody has
|
|
232
|
+
// seen yet.
|
|
132
233
|
this.style.actionStart('Parsing your project');
|
|
133
234
|
const availableRuntimes = await api.runtimes.getAll();
|
|
134
235
|
const project = await parseProject({
|
|
@@ -153,6 +254,7 @@ export default class Deploy extends AuthCommand {
|
|
|
153
254
|
include: checklyConfig.checks?.include,
|
|
154
255
|
embeddedPackages: checklyConfig.bundle?.packages?.embed,
|
|
155
256
|
playwrightChecks: checklyConfig.checks?.playwrightChecks,
|
|
257
|
+
warnOnMissingCheckLocations: true,
|
|
156
258
|
});
|
|
157
259
|
const repoInfo = getGitInformation(project.repoUrl);
|
|
158
260
|
const repoRoot = getGitRepoRoot();
|
|
@@ -179,45 +281,60 @@ export default class Deploy extends AuthCommand {
|
|
|
179
281
|
})();
|
|
180
282
|
const archive = await bundler.finalize();
|
|
181
283
|
bundler.updateMarker(archive.archiveFile);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
284
|
+
const browserBundles = Object.values(projectBundle.data.check)
|
|
285
|
+
.map(({ bundle }) => bundle)
|
|
286
|
+
.filter((bundle) => bundle instanceof BrowserCheckBundle);
|
|
287
|
+
// Uploading is what produces the storage keys a deploy needs, and it is
|
|
288
|
+
// deferred until the deploy has been confirmed: a preview describes the
|
|
289
|
+
// code bundle and every snapshot by content hash, and a dry-run deploy
|
|
290
|
+
// accepts a payload without keys, so finding out what a deploy would
|
|
291
|
+
// change costs no uploads. `uploaded` tells `deployPayload` whether the
|
|
292
|
+
// snapshots carry keys yet.
|
|
293
|
+
let uploaded = false;
|
|
294
|
+
const uploadArtifacts = async () => {
|
|
295
|
+
uploaded = true;
|
|
296
|
+
// The remote code bundle is only consumed by Playwright check suites (via
|
|
297
|
+
// bundler.marker). If nothing registered files to bundle (e.g. a project of
|
|
298
|
+
// only uptime monitors), there is nothing to upload — skip the store() to
|
|
299
|
+
// avoid an unnecessary code-bundle upload.
|
|
300
|
+
if (!bundler.isEmpty) {
|
|
301
|
+
this.style.actionStart('Uploading Playwright tests');
|
|
302
|
+
try {
|
|
303
|
+
const storedArchive = await archive.store();
|
|
304
|
+
bundler.updateMarker(storedArchive.key);
|
|
305
|
+
this.style.actionSuccess();
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
this.style.actionFailure();
|
|
309
|
+
throw err;
|
|
310
|
+
}
|
|
204
311
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const bundle
|
|
211
|
-
|
|
312
|
+
// The step is only announced when there is a snapshot to upload. Every
|
|
313
|
+
// browser check gets its snapshot list either way, an empty one included.
|
|
314
|
+
if (browserBundles.some(bundle => bundle.rawSnapshots?.length)) {
|
|
315
|
+
this.style.actionStart('Uploading Playwright snapshots');
|
|
316
|
+
try {
|
|
317
|
+
for (const bundle of browserBundles) {
|
|
318
|
+
bundle.snapshots = await uploadSnapshots(bundle.rawSnapshots);
|
|
319
|
+
}
|
|
320
|
+
this.style.actionSuccess();
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
this.style.actionFailure();
|
|
324
|
+
throw err;
|
|
212
325
|
}
|
|
213
|
-
this.style.actionSuccess();
|
|
214
326
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
327
|
+
else {
|
|
328
|
+
for (const bundle of browserBundles) {
|
|
329
|
+
bundle.snapshots = [];
|
|
330
|
+
}
|
|
218
331
|
}
|
|
219
|
-
}
|
|
220
|
-
|
|
332
|
+
};
|
|
333
|
+
// Synthesized on demand rather than once: the snapshot entries are copied
|
|
334
|
+
// into the payload as values, so the payload sent after the upload has to
|
|
335
|
+
// be built after it to carry the keys.
|
|
336
|
+
const synthesize = () => ({ ...projectBundle.synthesize({ repoRoot }), repoInfo });
|
|
337
|
+
const projectPayload = synthesize();
|
|
221
338
|
if (!projectPayload.resources.length) {
|
|
222
339
|
if (preview) {
|
|
223
340
|
this.log('\nNo checks were detected. More information on how to set up a Checkly CLI project is available at https://checklyhq.com/docs/cli/.\n');
|
|
@@ -233,82 +350,419 @@ export default class Deploy extends AuthCommand {
|
|
|
233
350
|
this.log(`Successfully wrote debug bundle to "${debugBundleOutputFile}".`);
|
|
234
351
|
return;
|
|
235
352
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
353
|
+
const summaryOptions = { prettyTypes: PRETTY_RESOURCE_TYPES, foldedTypes: NON_REPORTED_TYPES };
|
|
354
|
+
const classification = {
|
|
355
|
+
readOnly: Deploy.readOnly,
|
|
356
|
+
destructive: Deploy.destructive,
|
|
357
|
+
idempotent: Deploy.idempotent,
|
|
358
|
+
};
|
|
359
|
+
// What the deploy does whatever it finds, worded as the confirmation
|
|
360
|
+
// prompt words it. The plan's own lines follow these. The scheduling
|
|
361
|
+
// threshold is left out: few deploys reach it, and the deploy reports it
|
|
362
|
+
// when one does.
|
|
363
|
+
const optionLines = [
|
|
364
|
+
`Deploy project "${checklyConfig.projectName}" to account "${account.name}"`,
|
|
365
|
+
scheduleOnDeploy
|
|
366
|
+
? `Schedule ${scheduleScope} checks after deploy`
|
|
367
|
+
: 'Leave checks to run at their next scheduled time. '
|
|
368
|
+
+ 'Pass --schedule-on-deploy to schedule them right after the deploy instead',
|
|
369
|
+
preserveResources
|
|
370
|
+
? 'Keep any resources removed from code (and their run history) in your Checkly account, where you can manage them from the Checkly web app'
|
|
371
|
+
: 'Delete any resources removed from code, losing their run history. Pass --preserve-resources to keep them in your Checkly account instead',
|
|
372
|
+
...pruneRelations
|
|
373
|
+
? ['Delete the alert channel subscriptions and private location assignments on this project\'s checks '
|
|
374
|
+
+ 'and groups that the project does not manage']
|
|
375
|
+
: [],
|
|
376
|
+
];
|
|
377
|
+
// Ask Checkly what this payload would change. Writes nothing, and needs no
|
|
378
|
+
// upload: the payload describes the code bundle and every snapshot by
|
|
379
|
+
// content hash.
|
|
380
|
+
// `full` buys each changed resource's current state, which is what the
|
|
381
|
+
// rendered construct diff (`--preview`, `--output`, the plan an interactive
|
|
382
|
+
// run shows before asking) and the machine-readable envelope (`--dry-run`,
|
|
383
|
+
// the `confirmation_required` an agent or CI run prints) show. Only a
|
|
384
|
+
// forced deploy prints nothing of the kind, so only it skips paying for
|
|
385
|
+
// the state.
|
|
386
|
+
const detail = dryRun || preview || output || !force ? 'full' : 'changes';
|
|
387
|
+
let plan;
|
|
388
|
+
// Set when the API has no preview endpoint, which also means it rejects the
|
|
389
|
+
// payload fields that arrived with it.
|
|
390
|
+
let previewNotSupported = false;
|
|
391
|
+
// With --no-plan the deploy applies to whatever the account looks like
|
|
392
|
+
// when it runs: no plan, no token, and nothing to render before the prompt.
|
|
393
|
+
if (planRequested) {
|
|
394
|
+
this.style.actionStart('Checking what would change');
|
|
243
395
|
try {
|
|
244
|
-
|
|
245
|
-
|
|
396
|
+
plan = await api.projects.preview(projectPayload, {
|
|
397
|
+
detail,
|
|
398
|
+
preserveResources,
|
|
399
|
+
pruneRelations,
|
|
400
|
+
onStatus: message => this.style.actionStatus(message),
|
|
401
|
+
});
|
|
246
402
|
this.style.actionSuccess();
|
|
247
403
|
}
|
|
248
404
|
catch (err) {
|
|
249
405
|
this.style.actionFailure();
|
|
250
|
-
|
|
406
|
+
// An API that has plans switched off is not one that predates them: it
|
|
407
|
+
// knows the payload fields that arrived with the preview endpoint, so
|
|
408
|
+
// the payload is not stripped of them.
|
|
409
|
+
const plansDisabled = err instanceof ProjectPlanDisabledError;
|
|
410
|
+
previewNotSupported = err instanceof ProjectPreviewNotSupportedError;
|
|
411
|
+
const previewSupported = !previewNotSupported && !plansDisabled;
|
|
412
|
+
// --prune-relations deletes data, and without a plan nothing can say
|
|
413
|
+
// what: an API that predates the preview endpoint would not prune at all,
|
|
414
|
+
// and an API that would prune cannot be asked what it is about to delete.
|
|
415
|
+
// Both are refused rather than silently downgraded.
|
|
416
|
+
if (pruneRelations) {
|
|
417
|
+
this.style.longError(previewSupported
|
|
418
|
+
? 'Could not check which relations --prune-relations would delete, so nothing was deployed.'
|
|
419
|
+
: plansDisabled
|
|
420
|
+
? 'Checkly has deploy plans switched off at the moment, so relations cannot be pruned.'
|
|
421
|
+
: 'This Checkly API cannot prune relations yet.', previewSupported ? 'Try again in a moment.' : 'Re-run without --prune-relations.');
|
|
422
|
+
this.exit(1);
|
|
423
|
+
}
|
|
424
|
+
// A deploy pinned to a plan cannot proceed without knowing the plan.
|
|
425
|
+
if (requestedPlanToken !== undefined) {
|
|
426
|
+
this.style.longError('Could not check the plan this deploy is pinned to.', previewSupported
|
|
427
|
+
? err.message
|
|
428
|
+
: plansDisabled
|
|
429
|
+
? 'Checkly has deploy plans switched off at the moment; re-run without --plan-token.'
|
|
430
|
+
: 'This Checkly API does not support deploy previews; re-run without --plan-token.');
|
|
431
|
+
this.exit(1);
|
|
432
|
+
}
|
|
433
|
+
// Deploying without a reviewed plan beats not deploying at all: one
|
|
434
|
+
// resource Checkly cannot read, or an API that is a version behind, must
|
|
435
|
+
// not make a project undeployable. The run falls back to the coarser
|
|
436
|
+
// dry-run diff and its delete guard, and sends no plan token.
|
|
437
|
+
this.style.longWarning(previewSupported
|
|
438
|
+
// Say which failure it was: the user is about to get a coarser answer
|
|
439
|
+
// than a planned deploy gives and deserves to know why.
|
|
440
|
+
? `Could not check what this deploy would change: ${err.message}`
|
|
441
|
+
: plansDisabled
|
|
442
|
+
? err.message
|
|
443
|
+
// A 404 from this path means the endpoint is not there; whether that
|
|
444
|
+
// is an API predating it or something else in the way, the CLI cannot
|
|
445
|
+
// tell, so it says what it observed.
|
|
446
|
+
: 'This Checkly API answered 404 for the deploy preview endpoint.', 'Falling back to a summary of created, updated and deleted resources.');
|
|
447
|
+
}
|
|
448
|
+
if (plan !== undefined && requestedPlanToken !== undefined && requestedPlanToken !== plan.planToken) {
|
|
449
|
+
this.style.longError('Your Checkly account no longer matches the plan this deploy is pinned to, so nothing was deployed.', 'Re-run `checkly deploy --preview` to see the current plan.');
|
|
251
450
|
this.exit(1);
|
|
252
451
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
452
|
+
}
|
|
453
|
+
// The payload goes out in the form the deploy route accepted before the
|
|
454
|
+
// preview endpoint existed in exactly the two cases where the full one
|
|
455
|
+
// cannot be sent: an API that does not have the endpoint rejects the fields
|
|
456
|
+
// that arrived with it, and a run that skipped the uploads describes
|
|
457
|
+
// snapshots it has no storage key for, which every write route requires.
|
|
458
|
+
//
|
|
459
|
+
// Not for every missing plan: a preview that failed transiently against a
|
|
460
|
+
// current API leaves the fields perfectly acceptable, and stripping them
|
|
461
|
+
// would blank the stored content hashes — making the NEXT deploy report
|
|
462
|
+
// every Playwright suite and every snapshot-bearing check as changed.
|
|
463
|
+
const deployPayload = () => previewNotSupported || !uploaded ? stripUnsupportedDeployFields(synthesize()) : synthesize();
|
|
464
|
+
// A planned run learns from the preview call whether the API has the
|
|
465
|
+
// endpoint, and with it whether the deploy route knows the fields that
|
|
466
|
+
// arrived with it. A run without a plan — one started with --no-plan, or
|
|
467
|
+
// a preview that failed for a reason other than a missing endpoint — does not, so a
|
|
468
|
+
// payload the route refuses because of one of those fields is sent once
|
|
469
|
+
// more in the older form; the rest of the run then sends that form too.
|
|
470
|
+
// If the older form is refused as well, the refusal of the payload the
|
|
471
|
+
// user asked for is the one that surfaces; any other failure of the
|
|
472
|
+
// second attempt is its own and keeps its own handling. The older form
|
|
473
|
+
// blanks the stored content hashes, which the next planned deploy reports
|
|
474
|
+
// as a change to every Playwright check suite and every check with
|
|
475
|
+
// snapshots — once, and worth a warning once a deploy has gone out that
|
|
476
|
+
// way.
|
|
477
|
+
let sentLegacyPayload = false;
|
|
478
|
+
const deployOrRetryLegacy = async (options) => {
|
|
479
|
+
try {
|
|
480
|
+
return await api.projects.deploy(deployPayload(), options);
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
if (plan !== undefined || previewNotSupported || !rejectsPreviewEraField(err)) {
|
|
484
|
+
throw err;
|
|
257
485
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
486
|
+
previewNotSupported = true;
|
|
487
|
+
try {
|
|
488
|
+
const result = await api.projects.deploy(deployPayload(), options);
|
|
489
|
+
sentLegacyPayload = true;
|
|
490
|
+
return result;
|
|
491
|
+
}
|
|
492
|
+
catch (retryErr) {
|
|
493
|
+
if (retryErr instanceof ValidationError) {
|
|
494
|
+
throw err;
|
|
495
|
+
}
|
|
496
|
+
throw retryErr;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
};
|
|
500
|
+
// Without a plan, deletions are only visible in a dry-run deploy. It runs
|
|
501
|
+
// before the uploads, like the preview: a run that is about to ask for
|
|
502
|
+
// confirmation, or that only reports, must not leave a code bundle and
|
|
503
|
+
// snapshots in storage for an answer that may be no. The dry run validates
|
|
504
|
+
// the payload it is given, in which a code bundle it has not been handed a
|
|
505
|
+
// key for is described by its path on disk and snapshots without a key are
|
|
506
|
+
// left out, as it was before the preview endpoint existed. A storage key
|
|
507
|
+
// the deploy route refuses is therefore caught by the deploy itself, after
|
|
508
|
+
// the uploads, not by this dry run.
|
|
509
|
+
let fallbackDiff;
|
|
510
|
+
if (plan === undefined && (preview || dryRun || (!preserveResources && !force))) {
|
|
511
|
+
this.style.actionStart('Verifying deployed state');
|
|
512
|
+
try {
|
|
513
|
+
const { data } = await deployOrRetryLegacy({
|
|
514
|
+
dryRun: true,
|
|
515
|
+
scheduleOnDeploy,
|
|
516
|
+
preserveResources,
|
|
517
|
+
});
|
|
518
|
+
fallbackDiff = data;
|
|
519
|
+
this.style.actionSuccess();
|
|
274
520
|
}
|
|
521
|
+
catch (err) {
|
|
522
|
+
this.style.actionFailure();
|
|
523
|
+
this.style.longError(`Your project could not be deployed.`, err);
|
|
524
|
+
this.exit(1);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
// The plan as `--preview` prints it, which is also what a terminal sees
|
|
528
|
+
// before it is asked. Without a plan, the dry run's findings are listed.
|
|
529
|
+
const renderPlan = (planToken) => formatPreview({
|
|
530
|
+
heading: { title: 'Deploy preview', projectName: project.name, accountName: account.name },
|
|
531
|
+
diff: plan?.diff ?? fallbackDiff?.diff ?? [],
|
|
532
|
+
project,
|
|
533
|
+
verbose,
|
|
534
|
+
pruneRelations,
|
|
535
|
+
rendering: plan !== undefined ? { plan: plan.diff, local: projectPayload.resources } : undefined,
|
|
536
|
+
planToken,
|
|
537
|
+
});
|
|
538
|
+
if (preview && !dryRun) {
|
|
539
|
+
this.log(renderPlan(plan?.planToken));
|
|
540
|
+
return;
|
|
275
541
|
}
|
|
542
|
+
// With a plan, every touched resource has a line; without one, only the
|
|
543
|
+
// deletions the dry run found are known.
|
|
544
|
+
const planLines = plan !== undefined
|
|
545
|
+
? planChangeLines(plan.diff, summaryOptions)
|
|
546
|
+
: this.collectDeletions(fallbackDiff?.diff ?? [])
|
|
547
|
+
.map(({ resourceType, logicalId }) => `Permanently delete ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}, `
|
|
548
|
+
+ 'losing its run history');
|
|
549
|
+
// A plan with nothing to write has nothing to approve, so nobody is asked:
|
|
550
|
+
// a terminal, an agent and a forced run all read that there are no changes
|
|
551
|
+
// and carry on. The deploy is still sent, pinned to the plan's token like
|
|
552
|
+
// any other. It writes no resource, but it records the deployment, renews
|
|
553
|
+
// the deployed state the next plan compares against (which is what clears
|
|
554
|
+
// the mark of a resource edited outside code once the code agrees with
|
|
555
|
+
// it), and schedules the checks unless told not to. --dry-run keeps its
|
|
556
|
+
// envelope, which a script reads the same way whatever the plan holds.
|
|
557
|
+
const nothingToApply = plan !== undefined && planHasNoChanges(plan.diff, summaryOptions);
|
|
558
|
+
if (nothingToApply && !dryRun) {
|
|
559
|
+
this.log(renderPlan());
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
// The one confirmation of the command: the plan is known by now, so the
|
|
563
|
+
// prompt, the agent envelope and --dry-run all describe the deploy that is
|
|
564
|
+
// about to run rather than a deploy nobody has seen. A terminal gets the
|
|
565
|
+
// plan rendered as `--preview` prints it, with the options under it; the
|
|
566
|
+
// plan lines are not repeated there since the overview names every
|
|
567
|
+
// resource. No plan-token footer: this run pins the token itself.
|
|
568
|
+
await this.confirmOrAbort({
|
|
569
|
+
command: 'deploy',
|
|
570
|
+
description: 'Deploy project to Checkly',
|
|
571
|
+
changes: [...optionLines, ...planLines],
|
|
572
|
+
...plan !== undefined
|
|
573
|
+
? {
|
|
574
|
+
terminal: {
|
|
575
|
+
plan: renderPlan,
|
|
576
|
+
changes: optionLines,
|
|
577
|
+
alternatives: writeBackAlternatives(plan.diff, project, this),
|
|
578
|
+
},
|
|
579
|
+
question: 'Apply these changes?',
|
|
580
|
+
}
|
|
581
|
+
: {},
|
|
582
|
+
// The token rides along in the echoed command, so the confirming run
|
|
583
|
+
// deploys the plan that was shown here and refuses a different one.
|
|
584
|
+
flags: plan !== undefined ? { ...flags, 'plan-token': plan.planToken } : flags,
|
|
585
|
+
flagMetadata: metadata.flags,
|
|
586
|
+
classification,
|
|
587
|
+
...plan !== undefined
|
|
588
|
+
? { preview: { planToken: plan.planToken, diff: reducePlanForAgent(plan.diff) } }
|
|
589
|
+
: {},
|
|
590
|
+
}, { force, dryRun });
|
|
591
|
+
}
|
|
592
|
+
await uploadArtifacts();
|
|
593
|
+
const runDeploy = () => deployOrRetryLegacy({
|
|
594
|
+
scheduleOnDeploy,
|
|
595
|
+
scheduleOnDeployScope: scheduleScope,
|
|
596
|
+
scheduleOnDeployThreshold: scheduleThreshold === 'auto' ? undefined : scheduleThreshold,
|
|
597
|
+
scheduleOnDeployMinFrequency: scheduleMinFrequency === 'auto' ? undefined : scheduleMinFrequency,
|
|
598
|
+
preserveResources,
|
|
599
|
+
pruneRelations,
|
|
600
|
+
// Read when the deploy is sent, so that a run which planned again after
|
|
601
|
+
// a stale plan is covered. Only a run that holds a plan asks the deploy
|
|
602
|
+
// to apply one: without it the deploy writes every resource, as it did
|
|
603
|
+
// before plans existed.
|
|
604
|
+
plan: plan !== undefined,
|
|
605
|
+
planToken: plan?.planToken,
|
|
606
|
+
cancelInProgress,
|
|
607
|
+
onProgress: progress => this.style.actionStatus(`${progress}% complete`),
|
|
608
|
+
onStatus: message => this.style.actionStatus(message),
|
|
609
|
+
});
|
|
276
610
|
try {
|
|
277
|
-
|
|
278
|
-
|
|
611
|
+
// "Deploying" right under "No changes" would read as a contradiction.
|
|
612
|
+
this.style.actionStart(nothingToApply ? 'Recording the deployment' : 'Deploying project');
|
|
613
|
+
let data;
|
|
614
|
+
try {
|
|
615
|
+
({ data } = await runDeploy());
|
|
279
616
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
617
|
+
catch (err) {
|
|
618
|
+
// A run that showed nobody a plan has nothing to protect: rather than
|
|
619
|
+
// failing a pipeline because someone touched the account while the code
|
|
620
|
+
// bundle was uploading, it plans again and deploys that. A pinned run,
|
|
621
|
+
// or one a person confirmed, is refused instead — see the catch below.
|
|
622
|
+
// A run without a plan sent no token, so it cannot be told its plan
|
|
623
|
+
// went stale; the guard keeps it out of the re-plan regardless.
|
|
624
|
+
if (!(err instanceof ProjectPlanStaleError) || !force || requestedPlanToken !== undefined || !planRequested) {
|
|
625
|
+
throw err;
|
|
626
|
+
}
|
|
627
|
+
this.style.actionStatus('Your Checkly account changed; checking again and deploying the current plan');
|
|
628
|
+
plan = await api.projects.preview(deployPayload(), { detail, preserveResources, pruneRelations });
|
|
629
|
+
({ data } = await runDeploy());
|
|
290
630
|
}
|
|
291
|
-
|
|
292
|
-
|
|
631
|
+
this.style.actionSuccess();
|
|
632
|
+
if (sentLegacyPayload) {
|
|
633
|
+
this.style.longWarning('This Checkly API does not know the fields the preview endpoint added, so the deploy was sent without them.', 'The next planned deploy reports every Playwright check suite and every check with snapshots as changed.');
|
|
293
634
|
}
|
|
294
|
-
|
|
295
|
-
|
|
635
|
+
// Judged on what the deploy answered, not on the plan it started from: a
|
|
636
|
+
// forced run that planned again after the account moved may have written
|
|
637
|
+
// something after all.
|
|
638
|
+
const wroteNothing = plan !== undefined && planHasNoChanges(data.diff, summaryOptions);
|
|
639
|
+
if (output && !wroteNothing) {
|
|
640
|
+
// The deploy response names every resource with its id; the plan the
|
|
641
|
+
// deploy was confirmed against is where each one's deployed state is.
|
|
642
|
+
// No heading: the success line that follows names the project and account.
|
|
643
|
+
this.log(formatPreview({
|
|
644
|
+
done: true,
|
|
645
|
+
diff: data.diff,
|
|
646
|
+
project,
|
|
647
|
+
verbose,
|
|
648
|
+
pruneRelations,
|
|
649
|
+
rendering: plan !== undefined ? { plan: plan.diff, local: projectPayload.resources } : undefined,
|
|
650
|
+
}));
|
|
651
|
+
}
|
|
652
|
+
await setTimeout(500);
|
|
653
|
+
// The checks Checkly marked with whether it scheduled them; none from an
|
|
654
|
+
// API that predates the marks.
|
|
655
|
+
const markedChecks = data.diff.filter(entry => entry.scheduled !== undefined);
|
|
656
|
+
if (wroteNothing) {
|
|
657
|
+
// Scheduling is the one thing such a deploy visibly does, so it is said.
|
|
658
|
+
// Without marks it is counted on what was sent and can be scheduled: a
|
|
659
|
+
// deploy that changed nothing schedules only under the `all` scope, a
|
|
660
|
+
// testOnly check is in the project but not in the deploy, and a
|
|
661
|
+
// heartbeat monitor waits for pings instead of running.
|
|
662
|
+
const heartbeats = project.getHeartbeatLogicalIds();
|
|
663
|
+
const scheduled = markedChecks.length > 0
|
|
664
|
+
? markedChecks.some(entry => entry.scheduled)
|
|
665
|
+
: (data.scheduled ?? scheduleOnDeploy)
|
|
666
|
+
&& scheduleScope === 'all'
|
|
667
|
+
&& Object.keys(projectBundle.data.check).some(logicalId => !heartbeats.includes(logicalId));
|
|
668
|
+
this.log(`Project "${project.name}" is up to date.${scheduled ? ' Checks were scheduled to run.' : ''}`);
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
296
671
|
this.log(`Successfully deployed project "${project.name}" to account "${account.name}".`);
|
|
297
|
-
// Print the ping URL for heartbeat checks.
|
|
298
|
-
const heartbeatLogicalIds = project.getHeartbeatLogicalIds();
|
|
299
|
-
const heartbeatCheckIds = data.diff.filter(check => heartbeatLogicalIds.includes(check.logicalId))
|
|
300
|
-
.map(check => check?.physicalId);
|
|
301
|
-
heartbeatCheckIds.forEach(async (id) => {
|
|
302
|
-
const { data: { pingUrl, name } } = await api.heartbeatCheck.get(id);
|
|
303
|
-
this.log(`Ping URL of heartbeat check ${chalk.green(name)} is ${chalk.italic.underline.blue(pingUrl)}.`);
|
|
304
|
-
});
|
|
305
672
|
}
|
|
673
|
+
// Checkly schedules none of a deploy's checks when there are more than
|
|
674
|
+
// the threshold allows, which is worth a warning. Otherwise it may still
|
|
675
|
+
// leave some out, such as the ones that already run often; a notice
|
|
676
|
+
// lists those, kept short so that it does not push people into relaxing
|
|
677
|
+
// the limits. A deploy that asked for no scheduling hears about neither,
|
|
678
|
+
// and the checks a deploy left unchanged are what the scope leaves out
|
|
679
|
+
// by design, not something to report.
|
|
680
|
+
const notScheduled = scheduleOnDeploy
|
|
681
|
+
? markedChecks.filter(entry => entry.scheduled === false
|
|
682
|
+
&& entry.scheduleSkippedReason !== 'NOT_REQUESTED'
|
|
683
|
+
&& entry.scheduleSkippedReason !== 'UNCHANGED')
|
|
684
|
+
: [];
|
|
685
|
+
if (scheduleOnDeploy && data.scheduled === false) {
|
|
686
|
+
// Set apart from the line above, which ends without one.
|
|
687
|
+
this.log();
|
|
688
|
+
this.style.longWarning('Checks were not scheduled', [
|
|
689
|
+
'This deploy would have scheduled more checks than the scheduling threshold allows.',
|
|
690
|
+
'',
|
|
691
|
+
'All checks will run at their next scheduled time.',
|
|
692
|
+
'',
|
|
693
|
+
'Helpful options:',
|
|
694
|
+
...scheduleScope === 'all'
|
|
695
|
+
? [
|
|
696
|
+
' --schedule-on-deploy-scope=changed',
|
|
697
|
+
' Only schedule the checks the deploy changed.',
|
|
698
|
+
'',
|
|
699
|
+
]
|
|
700
|
+
: [],
|
|
701
|
+
' --schedule-on-deploy-threshold=<number>',
|
|
702
|
+
' Change the threshold to a suitable number of checks.',
|
|
703
|
+
'',
|
|
704
|
+
' --schedule-on-deploy-min-frequency=<number>',
|
|
705
|
+
' Only schedule checks that run less often than the specified frequency',
|
|
706
|
+
' (in minutes), reducing the number of scheduled checks.',
|
|
707
|
+
'',
|
|
708
|
+
' For a full list of options, pass --help.',
|
|
709
|
+
].join('\n'));
|
|
710
|
+
}
|
|
711
|
+
else if (notScheduled.length > 0) {
|
|
712
|
+
this.log();
|
|
713
|
+
this.style.longInfo('Some checks were not scheduled', [
|
|
714
|
+
'The checks will run at their next scheduled time.',
|
|
715
|
+
'',
|
|
716
|
+
'Not scheduled:',
|
|
717
|
+
...Object.entries(countBy(notScheduled, entry => entry.scheduleSkippedReason ?? 'UNKNOWN'))
|
|
718
|
+
.map(([reason, count]) => `- ${describeNotScheduled(reason, count)}`),
|
|
719
|
+
].join('\n'));
|
|
720
|
+
}
|
|
721
|
+
// Print the ping URL for heartbeat checks.
|
|
722
|
+
const heartbeatLogicalIds = project.getHeartbeatLogicalIds();
|
|
723
|
+
const heartbeatCheckIds = data.diff.filter(check => heartbeatLogicalIds.includes(check.logicalId))
|
|
724
|
+
.map(check => check?.physicalId);
|
|
725
|
+
heartbeatCheckIds.forEach(async (id) => {
|
|
726
|
+
const { data: { pingUrl, name } } = await api.heartbeatCheck.get(id);
|
|
727
|
+
this.log(`Ping URL of heartbeat check ${chalk.green(name)} is ${chalk.italic.underline.blue(pingUrl)}.`);
|
|
728
|
+
});
|
|
306
729
|
}
|
|
307
730
|
catch (err) {
|
|
308
|
-
|
|
309
|
-
|
|
731
|
+
this.style.actionFailure();
|
|
732
|
+
if (err instanceof ProjectPlanStaleError) {
|
|
733
|
+
// Nothing was written. The way out is another run, which previews
|
|
734
|
+
// afresh: this run's token describes a state Checkly has left behind,
|
|
735
|
+
// so sending it again would be refused again.
|
|
736
|
+
if (err.diff.length) {
|
|
737
|
+
this.log(formatPreview({
|
|
738
|
+
heading: { title: 'Current plan', projectName: project.name, accountName: account.name },
|
|
739
|
+
diff: err.diff,
|
|
740
|
+
project,
|
|
741
|
+
verbose,
|
|
742
|
+
pruneRelations,
|
|
743
|
+
}));
|
|
744
|
+
this.style.longError(
|
|
745
|
+
// A plan with nothing to apply was confirmed by nobody.
|
|
746
|
+
nothingToApply
|
|
747
|
+
? 'Your Checkly account changed after the plan found no changes, so nothing was deployed.'
|
|
748
|
+
: 'Your Checkly account changed while this deploy was being confirmed, so nothing was deployed.', 'The plan above is the current one. Re-run `checkly deploy` to review and deploy it.');
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
// A refusal with no plan attached: the account moved for a reason the
|
|
752
|
+
// error itself explains, and there is nothing to print above.
|
|
753
|
+
this.style.longError(`${err.message} Nothing was deployed.`, 'Re-run `checkly deploy` to see the current plan and deploy it.');
|
|
754
|
+
}
|
|
310
755
|
}
|
|
311
|
-
if (err instanceof
|
|
756
|
+
else if (plan !== undefined && err instanceof ValidationError && err.data.code === DEPLOY_PLAN_DISABLED) {
|
|
757
|
+
// The plan was made, and plans were switched off before the deploy
|
|
758
|
+
// that applies it arrived. The refusal is worded for whoever sent a
|
|
759
|
+
// token; this run pinned its own.
|
|
760
|
+
this.style.longError('Checkly switched deploy plans off while this deploy was being prepared, so nothing was deployed.', requestedPlanToken !== undefined || pruneRelations
|
|
761
|
+
? 'Re-run the command without --plan-token and --prune-relations. '
|
|
762
|
+
+ 'While plans are switched off it deploys without one.'
|
|
763
|
+
: 'Re-run the command. While plans are switched off it deploys without one.');
|
|
764
|
+
}
|
|
765
|
+
else if (err instanceof ProjectDeployCancelledError) {
|
|
312
766
|
this.style.longError('Your deployment was cancelled.', err.message);
|
|
313
767
|
}
|
|
314
768
|
else if (err instanceof ConflictError) {
|
|
@@ -323,132 +777,34 @@ export default class Deploy extends AuthCommand {
|
|
|
323
777
|
this.exit(1);
|
|
324
778
|
}
|
|
325
779
|
}
|
|
326
|
-
collectDeletions(
|
|
327
|
-
return
|
|
780
|
+
collectDeletions(diff) {
|
|
781
|
+
return diff
|
|
328
782
|
.filter(change => change.action === ResourceDeployStatus.DELETE
|
|
783
|
+
// A resource the project no longer declares, not a relation it never
|
|
784
|
+
// managed: pruning those is reported under its own heading.
|
|
785
|
+
&& change.origin !== 'unmanaged'
|
|
329
786
|
&& !NON_REPORTED_TYPES.some(t => t === change.type))
|
|
330
787
|
.map(({ type, logicalId }) => ({ resourceType: type, logicalId }))
|
|
331
788
|
.sort((a, b) => a.resourceType.localeCompare(b.resourceType) || a.logicalId.localeCompare(b.logicalId));
|
|
332
789
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
const construct = project.data[type][logicalId];
|
|
353
|
-
if (action === ResourceDeployStatus.UPDATE) {
|
|
354
|
-
updating.push({ resourceType: type, logicalId, physicalId, construct });
|
|
355
|
-
}
|
|
356
|
-
else if (action === ResourceDeployStatus.CREATE) {
|
|
357
|
-
creating.push({ resourceType: type, logicalId, physicalId, construct });
|
|
358
|
-
}
|
|
359
|
-
else if (action === ResourceDeployStatus.DELETE) {
|
|
360
|
-
// Since the resource is being deleted, the construct isn't in the project.
|
|
361
|
-
deleting.push({ resourceType: type, logicalId });
|
|
362
|
-
}
|
|
363
|
-
else if (action === ResourceDeployStatus.DETACHED) {
|
|
364
|
-
// Newer backends report detached resources explicitly. The construct
|
|
365
|
-
// isn't in the project since it was removed from code.
|
|
366
|
-
detaching.push({ resourceType: type, logicalId });
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
// testOnly checks weren't sent to the BE and won't be in previewData.
|
|
370
|
-
// We load them from the `project` instead.
|
|
371
|
-
const skipping = project
|
|
372
|
-
.getTestOnlyConstructs().map(construct => ({
|
|
373
|
-
logicalId: construct.logicalId,
|
|
374
|
-
resourceType: construct.type,
|
|
375
|
-
construct,
|
|
376
|
-
}))
|
|
377
|
-
// There is an edge case when the check already exists in Checkly, but `testOnly: true` was just added.
|
|
378
|
-
// In this case, the check will be included in both `deleting` and `skipping`.
|
|
379
|
-
// To avoid displaying the check twice, we detect this case and only show the check in `deleting`.
|
|
380
|
-
// This implementation is O(n^2), but could be sped up with a map or set.
|
|
381
|
-
.filter(skip => !deleting.find(deletion => deletion.logicalId === skip.logicalId && deletion.resourceType === skip.resourceType));
|
|
382
|
-
// Having some order will make the output easier to read.
|
|
383
|
-
const compareEntries = (a, b) => a.resourceType.localeCompare(b.resourceType)
|
|
384
|
-
|| a.logicalId.localeCompare(b.logicalId);
|
|
385
|
-
// filter resources without contructs that are created dynamically
|
|
386
|
-
// on the flight (i.e. a non project member private-location)
|
|
387
|
-
const sortedUpdating = updating
|
|
388
|
-
.filter(({ construct }) => Boolean(construct))
|
|
389
|
-
.sort(compareEntries);
|
|
390
|
-
// filter resources without contructs that are created dynamically
|
|
391
|
-
// on the flight (i.e. a non project member private-location)
|
|
392
|
-
const sortedCreating = creating
|
|
393
|
-
.filter(({ construct }) => Boolean(construct))
|
|
394
|
-
.sort(compareEntries);
|
|
395
|
-
const sortedDeleting = deleting
|
|
396
|
-
.sort(compareEntries);
|
|
397
|
-
const sortedDetaching = detaching
|
|
398
|
-
.sort(compareEntries);
|
|
399
|
-
if (!sortedCreating.length && !sortedDeleting.length && !sortedDetaching.length
|
|
400
|
-
&& !sortedUpdating.length && !skipping.length) {
|
|
401
|
-
return '\nNo checks were detected. More information on how to set up a Checkly CLI project is available at https://checklyhq.com/docs/cli/.\n';
|
|
402
|
-
}
|
|
403
|
-
const output = [];
|
|
404
|
-
if (sortedCreating.filter(({ construct }) => Boolean(construct)).length) {
|
|
405
|
-
output.push(chalk.bold.green('Create:'));
|
|
406
|
-
for (const { logicalId, physicalId, construct } of sortedCreating) {
|
|
407
|
-
output.push(` ${construct.constructor.name}: ${logicalId}`);
|
|
408
|
-
if (verbose && construct.name) {
|
|
409
|
-
output.push(` name: ${construct.name}`);
|
|
410
|
-
}
|
|
411
|
-
if (verbose && physicalId) {
|
|
412
|
-
output.push(` id: ${physicalId}`);
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
output.push('');
|
|
416
|
-
}
|
|
417
|
-
if (sortedDeleting.length) {
|
|
418
|
-
output.push(chalk.bold.red('Delete:'));
|
|
419
|
-
for (const { resourceType, logicalId } of sortedDeleting) {
|
|
420
|
-
output.push(` ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`);
|
|
421
|
-
}
|
|
422
|
-
output.push('');
|
|
423
|
-
}
|
|
424
|
-
if (sortedDetaching.length) {
|
|
425
|
-
output.push(chalk.bold.yellow('Kept in your Checkly account (removed from code, now managed from the Checkly web app):'));
|
|
426
|
-
for (const { resourceType, logicalId } of sortedDetaching) {
|
|
427
|
-
output.push(` ${PRETTY_RESOURCE_TYPES[resourceType] ?? resourceType}: ${logicalId}`);
|
|
428
|
-
}
|
|
429
|
-
output.push('');
|
|
430
|
-
}
|
|
431
|
-
if (sortedUpdating.length) {
|
|
432
|
-
output.push(chalk.bold.magenta('Update and Unchanged:'));
|
|
433
|
-
for (const { logicalId, physicalId, construct } of sortedUpdating) {
|
|
434
|
-
output.push(` ${construct.constructor.name}: ${logicalId}`);
|
|
435
|
-
if (verbose && construct.name) {
|
|
436
|
-
output.push(` name: ${construct.name}`);
|
|
437
|
-
}
|
|
438
|
-
if (verbose && physicalId) {
|
|
439
|
-
output.push(` id: ${physicalId}`);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
output.push('');
|
|
443
|
-
}
|
|
444
|
-
if (skipping.length) {
|
|
445
|
-
output.push(chalk.bold.grey('Skip (testOnly):'));
|
|
446
|
-
for (const { logicalId, construct } of skipping) {
|
|
447
|
-
output.push(` ${construct.constructor.name}: ${logicalId}`);
|
|
448
|
-
}
|
|
449
|
-
output.push('');
|
|
450
|
-
}
|
|
451
|
-
return output.join('\n');
|
|
790
|
+
}
|
|
791
|
+
function countBy(items, key) {
|
|
792
|
+
const counts = {};
|
|
793
|
+
for (const item of items) {
|
|
794
|
+
counts[key(item)] = (counts[key(item)] ?? 0) + 1;
|
|
795
|
+
}
|
|
796
|
+
return counts;
|
|
797
|
+
}
|
|
798
|
+
/** One line of the not-scheduled notice: how many checks a skip reason left out. */
|
|
799
|
+
function describeNotScheduled(reason, count) {
|
|
800
|
+
const checks = count === 1 ? '1 check' : `${count} checks`;
|
|
801
|
+
switch (reason) {
|
|
802
|
+
case 'MIN_FREQUENCY':
|
|
803
|
+
return `${checks} that ${count === 1 ? 'runs' : 'run'} more often than the minimum frequency allows`;
|
|
804
|
+
default:
|
|
805
|
+
// A reason this CLI does not know yet, from a newer Checkly API: named
|
|
806
|
+
// as the API reports it.
|
|
807
|
+
return `${checks} for another reason (${reason})`;
|
|
452
808
|
}
|
|
453
809
|
}
|
|
454
810
|
//# sourceMappingURL=deploy.js.map
|