feat(deploy): add --dry-run flag for instant deployability feedback#1255
feat(deploy): add --dry-run flag for instant deployability feedback#1255gu-stav wants to merge 7 commits into
Conversation
Agents and CI need a fast answer to "can this deploy" without uploading anything. --dry-run runs every deploy check read-only — target resolution, build, schema validation, output directory — aggregated instead of fail-fast so one run reports every problem, lists the files that would be uploaded, and exits non-zero when not deployable. Pairs with --json for machine-readable output via the report's stable check names.
📦 Bundle Stats —
|
| Metric | Value | vs refactor/deploy-target-resolution (faad45d) |
|---|---|---|
| Internal (raw) | 2.1 KB | - |
| Internal (gzip) | 799 B | - |
| Bundled (raw) | 11.13 MB | - |
| Bundled (gzip) | 2.10 MB | - |
| Import time | 849ms | +0ms, +0.1% |
bin:sanity
| Metric | Value | vs refactor/deploy-target-resolution (faad45d) |
|---|---|---|
| Internal (raw) | 1023 B | - |
| Internal (gzip) | 486 B | - |
| Bundled (raw) | 9.87 MB | - |
| Bundled (gzip) | 1.77 MB | - |
| Import time | 1.91s | -3ms, -0.2% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @sanity/cli-core
Compared against refactor/deploy-target-resolution (faad45d5)
| Metric | Value | vs refactor/deploy-target-resolution (faad45d) |
|---|---|---|
| Internal (raw) | 96.3 KB | - |
| Internal (gzip) | 22.7 KB | - |
| Bundled (raw) | 21.70 MB | - |
| Bundled (gzip) | 3.45 MB | - |
| Import time | 754ms | -0ms, -0.0% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — create-sanity
Compared against refactor/deploy-target-resolution (faad45d5)
| Metric | Value | vs refactor/deploy-target-resolution (faad45d) |
|---|---|---|
| Internal (raw) | 908 B | - |
| Internal (gzip) | 483 B | - |
| Bundled (raw) | 931 B | - |
| Bundled (gzip) | 491 B | - |
| Import time | ❌ ChildProcess denied: node | - |
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fa77286. Configure here.
|
|
||
| static override description = 'Builds and deploys Sanity Studio or application to Sanity hosting' | ||
|
|
||
| static override enableJsonFlag = true |
There was a problem hiding this comment.
Non-dry-run deploy exposes json flag
Medium Severity
enableJsonFlag applies to every sanity deploy invocation, but only the dry-run path returns a DryRunReport. A real deploy finishes with undefined, so --json advertises machine-readable output that is not defined for actual deployments.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit fa77286. Configure here.
| } | ||
|
|
||
| return report | ||
| } |
There was a problem hiding this comment.
Custom output dir prompt skipped
Medium Severity
--dry-run returns before the logic that warns when a non-default sourceDir exists and is non-empty and would prompt to proceed. Dry-run can report deployable while a normal deploy would block or cancel on that confirmation.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit fa77286. Configure here.
Coverage Delta
Comparing 15 changed files against main @ Overall Coverage
|
…validations Dry-run checks invented their own wording for constraints the deploy already enforces. Messages now come from the same place the real deploy gets them — errorMessages constants and a shared autoUpdateIssueMessage — so the dry run states existing constraints instead of introducing new copy that could drift.
… run The title comparison and the cannot-prompt-unattended constraint were the last validation logic stated twice. The unattended error now lives where the needs-input verdict is handled, and the title diff is computed by one function both the deploy and the dry run consume.
…d dry runs The step order and skip conditions were the last thing stated twice. Each deploy type now has one sequence; validations report through a checks seam (fail-fast in real deploys, aggregated in dry runs) and side effects branch on dryRun. A real deploy now fails with the same message its dry run would report, and a step added to the sequence is automatically covered by both.
listDeployFiles -> listDeploymentFiles, runStudioDeploySteps -> createStudioDeployment, runAppDeploySteps -> createAppDeployment.
The four deploy test files each restated the user-applications endpoints and full application response literals — ~1000 lines of setup noise that buried what each test was actually about. Shared fixtures with defaults (deployTestHelpers.ts) reduce a scenario to one or two expressive lines, and encode the endpoint shapes once.


Description
sanity deploy --dry-runanswers "can this deploy" without deploying — a fast feedback loop for agents and CI.--jsonemits the report ({deployable, checks, target, files}) with stable check names, via oclif'senableJsonFlag. The exit code is set without throwing so the full report still reaches stdout on failure.The schema worker's
dryRunmode stops after extraction and validation (skips lexicon and schema-store uploads) but still writes the create-manifest locally, so the file summary matches what a real deploy would put in the tarball.There is no separate dry-run implementation: each deploy type has one step sequence (
createStudioDeployment/createAppDeployment) shared by real deploys and dry runs. Validations report through a checks seam — fail-fast in real deploys, aggregated in dry runs — so a real deploy fails with the same message its dry run reports, and a step added to the sequence is covered by both modes automatically.Example output
And when checks fail (exit code 2, every problem in one pass):
Stacked on #1254 — will retarget to
mainonce that merges; only the last commit is new here.What to review
deployStudio.ts/deployApp.tsagainst the previous deploy flow — real-deploy behavior (prompts, spinners, error messages, exit codes) should be unchanged.DeployChecksimplementations indeployChecks.ts: dry runs aggregate (a throwing step becomes a fail check), real deploys fail fast with the same message.--jsonconsumers.Testing
Command-level tests cover both deploy types: all-checks-pass with file listing, would-create hostname, aggregated failures with exit code 2,
--jsonoutput, app title sync, and the would-prompt failure when no appId is configured.Note
Low Risk
CLI-only validation path; no production deploy or upload behavior changes when the flag is not used. Dry-run still may run builds and write local manifest files.
Overview
Adds
sanity deploy --dry-runso studio and app deploys can be validated without creating applications, uploading schemas, or sending tarballs.The deploy command branches early into
dryRunDeployStudio/dryRunDeployApp, which run the same steps as a real deploy (target resolution via sharedresolve*DeployTarget, optional build withyes: true, schema extraction, output dir) through acreateDryRunCheckscollector that turns thrown steps into fail checks instead of aborting. Results are rendered withrenderDryRunReport(pass/fail/skip/warn, file list with sizes) and adeployableflag; failures set exit code 2 without throwing so--jsonstill returns the full{checks, target, files}report.Schema deployment gains a
dryRunpath in the worker: validate and optionally write create-manifest locally, skip lexicon/schema-store uploads and telemetry, and return per-workspace summaries for the schema check.Reviewed by Cursor Bugbot for commit fa77286. Bugbot is set up for automated code reviews on this repo. Configure here.