Skip to content

feat(cli): support unattended mode for GraphQL commands#1500

Open
jonahsnider wants to merge 8 commits into
mainfrom
aigro-5168/unattended-graphql
Open

feat(cli): support unattended mode for GraphQL commands#1500
jonahsnider wants to merge 8 commits into
mainfrom
aigro-5168/unattended-graphql

Conversation

@jonahsnider

@jonahsnider jonahsnider commented Jul 13, 2026

Copy link
Copy Markdown
Member

Description

Adds unattended-mode support for GraphQL commands.

What to review

Review the flags, prompt handling, and automated coverage.

Testing


Note

Medium Risk
Changes confirmation and exit-code behavior for deploy, undeploy, and schema delete/extract—scripts relying on old exit codes or silent skips may need updates, though defaults in interactive use are largely preserved.

Overview
Adds unattended-mode behavior across GraphQL deploy/undeploy and experimental schema commands so CI and scripted runs do not hang on prompts.

GraphQL: Deploy and undeploy now use isUnattended() instead of raw isInteractive(). In unattended mode, destructive or ambiguous steps require --force (generation mismatch, breaking changes, multi-API flag overrides, undeploy). User declines and cancellations exit with USER_ABORT and clearer messages instead of generic “Operation cancelled”. Missing dataset / usage mistakes use USAGE_ERROR.

Schema: schema delete gains --yes and blocks unattended deletes without it, with an interactive confirm otherwise. schema extract gains --force to overwrite an existing output file; unattended runs error without it. schema list --json routes fetch/permission diagnostics to stderr (warn) so stdout stays valid JSON.

Tests are aligned with explicit isInteractive mocks and the new exit codes.

Reviewed by Cursor Bugbot for commit c2bd3f1. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against main (abece333)

@sanity/cli

Metric Value vs main (abece33)
Internal (raw) 2.2 KB -
Internal (gzip) 838 B -
Bundled (raw) 11.20 MB +36.8 KB, +0.3%
Bundled (gzip) 2.11 MB +6.8 KB, +0.3%
Import time 881ms -11ms, -1.2%

bin:sanity

Metric Value vs main (abece33)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.90 MB +33.7 KB, +0.3%
Bundled (gzip) 1.78 MB +6.3 KB, +0.3%
Import time 2.31s +8ms, +0.3%

🗺️ 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 main (abece333)

Metric Value vs main (abece33)
Internal (raw) 113.4 KB +5.3 KB, +4.9%
Internal (gzip) 29.0 KB +2.0 KB, +7.5%
Bundled (raw) 21.76 MB +38.9 KB, +0.2%
Bundled (gzip) 3.46 MB +7.9 KB, +0.2%
Import time 783ms -5ms, -0.6%

🗺️ 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-build

Compared against main (abece333)

@sanity/cli-build/_internal/build

Metric Value vs main (abece33)
Internal (raw) 113.8 KB +2.9 KB, +2.6%
Internal (gzip) 28.7 KB +788 B, +2.8%
Bundled (raw) 18.10 MB +166.2 KB, +0.9%
Bundled (gzip) 3.64 MB +40.1 KB, +1.1%
Import time 1.38s +16ms, +1.1%

@sanity/cli-build/_internal/env

Metric Value vs main (abece33)
Internal (raw) 1.8 KB -
Internal (gzip) 644 B -
Bundled (raw) 1.31 MB +2.9 KB, +0.2%
Bundled (gzip) 333.8 KB +896 B, +0.3%
Import time 126ms -2ms, -1.7%

@sanity/cli-build/_internal/extract

Metric Value vs main (abece33)
Internal (raw) 8.6 KB -
Internal (gzip) 2.7 KB -
Bundled (raw) 155.0 KB +3.9 KB, +2.6%
Bundled (gzip) 39.5 KB +1.2 KB, +3.0%
Import time 250ms +1ms, +0.5%

🗺️ ./_internal/env · ./_internal/extract · @sanity/cli-build:./_internal/build treemap too large to embed · 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 main (abece333)

Metric Value vs main (abece33)
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.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli/src/actions/graphql/resolveApiGeneration.ts 5.5% (- 0.7%)
packages/@sanity/cli/src/commands/graphql/deploy.ts 41.1% (+ 0.2%)
packages/@sanity/cli/src/commands/graphql/undeploy.ts 97.7% (- 0.1%)
packages/@sanity/cli/src/commands/schemas/extract.ts 100.0% (±0%)

Comparing 4 changed files against main @ b683ef164bb77f6926ecd39cda9bc88e2eae7297

Overall Coverage

Metric Coverage
Statements 77.9% (+ 0.0%)
Branches 69.2% (+ 0.1%)
Functions 73.7% (+ 0.0%)
Lines 78.4% (+ 0.0%)

@jonahsnider
jonahsnider marked this pull request as ready for review July 15, 2026 21:44
@jonahsnider
jonahsnider requested a review from a team as a code owner July 15, 2026 21:44

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b2d6d24. Configure here.

Comment thread packages/@sanity/cli/src/commands/graphql/deploy.ts
Comment thread packages/@sanity/cli/src/commands/graphql/deploy.ts Outdated
Comment thread packages/@sanity/cli/src/commands/schemas/extract.ts
message: 'Are you sure you want to deploy?',
}))

return confirmDeploy ? specifiedGeneration : undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, there's a lot of ternary statements in this codebase, interesting that the agent decided to refactor this

// Confirm deletion unless --force is used
if (!force) {
if (this.isUnattended()) {
this.error('GraphQL API undeploy requires confirmation. Pass --force to continue.', {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious if there's an acceptable subset of commands that we wouldn't want to expose in unattended mode? seeing --force surfaced as an escape hatch for a destructive operation makes sense in human mode, i'm wondering about whether we expect the agent harness (and human who owns it) to be responsible for deciding when to take an action like this


if (!yes && this.isUnattended()) {
this.error(
'Schema deletion requires confirmation. Re-run with --yes to delete the schemas.',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in re my earlier comment on unattended destructive actions, this messaging is great, might be worth porting it to other destructive op sites


if (!shouldOverwrite) {
this.output.log('Schema extraction cancelled')
return this.exit(exitCodes.USER_ABORT)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit / fast follow candidate, if the exitCodes set includes semantics around 1 | 2 | 3, might be worth a pass to refactor all the places we currently supply a bare integer

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i don't really know why there's a mix of constants and magic numbers. i can't imagine there's a good reason. i'll standardize on the constants in my changes and come back later to update the rest

shapirodaniel
shapirodaniel previously approved these changes Jul 16, 2026

@shapirodaniel shapirodaniel left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few high level qs, otherwise 🚢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants