Skip to content

feat(deploy): add --url flag and unattended mode support#760

Open
rexxars wants to merge 6 commits intomainfrom
rexxars/deploy-external-unattended
Open

feat(deploy): add --url flag and unattended mode support#760
rexxars wants to merge 6 commits intomainfrom
rexxars/deploy-external-unattended

Conversation

@rexxars
Copy link
Member

@rexxars rexxars commented Mar 23, 2026

Summary

  • Adds a --url flag to sanity deploy that works for both external and internal studio deploys. For external deploys, it passes the full URL. For internal deploys, it strips the .sanity.studio suffix if present, so users can pass either my-studio or my-studio.sanity.studio.
  • The --url flag takes precedence over the studioHost config value, making it useful as a CLI override.
  • When --yes (unattended mode) is used without --url or studioHost config, the command now errors with a helpful message (pointing to --url) instead of hanging on an interactive prompt.
  • Covers both prompt sites: the select() prompt when multiple studios exist, and the input() prompt when no studio is configured.

Test plan

  • All 14 external deploy tests pass (7 new)
  • All 35 internal deploy tests pass (7 new)
  • Typecheck passes
  • Lint passes

🤖 Generated with Claude Code

Add --url flag to `sanity deploy` that works for both external and
internal studios. For external deploys it passes the full URL directly.
For internal deploys it strips the .sanity.studio suffix if present.
The flag takes precedence over the studioHost config value.

When --yes (unattended mode) is used without --url or studioHost, the
command now errors with a helpful message instead of hanging on an
interactive prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Mar 23, 2026

Claude finished @rexxars's task in 3m 3s —— View job


Code Review

  • Gather context
  • Read changed files
  • Analyze code
  • Post review

1. Dead code causes the 3.3% coverage drop

In resolveAppHost (deployStudio.ts:220–248), there are three return undefined statements that are never reached because output.error({exit: 1}) always throws:

if (validation !== true) {
  output.error(validation, {exit: 1})
  return undefined  // never reached
}
// ... and two more like this

These are there purely to satisfy TypeScript's control flow, but Istanbul counts them as uncovered branches. That's where the 3.3% drop comes from. You could suppress this with a type assertion or by extracting a helper typed as never, but it's cosmetic. The real fix would be to ensure Output.error is typed with {exit: number}: never overload.

Fix this →

2. Double URL normalization for external --url

When --url is passed with --external, resolveAppHost in deployStudio.ts already normalizes and validates the URL before it becomes appHost. But findUserApplication in findUserApplicationForStudio.ts:140–148 then calls normalizeUrl(appHost) and validateUrl(resolvedHost) again on the same already-normalized value:

// findUserApplicationForStudio.ts:140-148
const resolvedHost = urlType === 'external' ? normalizeUrl(appHost) : appHost
if (urlType === 'external') {
  const validation = validateUrl(resolvedHost)
  ...
}

This is harmless (normalization is idempotent) but redundant. The double-normalization path runs for both studioHost config values and --url flag values, even though --url values have already been validated.

rexxars and others added 4 commits March 23, 2026 16:21
Strip https:// prefix from --url for internal deploys so that
`--url https://my-studio.sanity.studio` correctly resolves to
`my-studio` instead of `https://my-studio`.

Also fix the unattended error path in findUserApplicationForStudio to
return null instead of undefined for consistency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Say "A studio found" instead of "Multiple studios found" when only one
studio exists in the unattended error path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… URLs

Validate that the resolved hostname for internal deploys only contains
valid characters (letters, numbers, hyphens). If the result still looks
like a URL (contains dots after stripping protocol and .sanity.studio),
suggest using --external instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move unattended error messaging to a single place in deployStudio.ts.
Previously, findUserApplicationForStudio emitted its own error when
apps existed but couldn't be selected, then the caller emitted a second
error since userApplication was null and flags.yes was true. Now
findUserApplicationForStudio just returns null in unattended mode and
lets the caller handle the error consistently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Mar 23, 2026

Coverage Delta

File Statements
packages/@sanity/cli/src/actions/deploy/deployStudio.ts 92.5% (- 3.3%)
packages/@sanity/cli/src/actions/deploy/findUserApplicationForStudio.ts 97.1% (+ 0.1%)
packages/@sanity/cli/src/commands/deploy.ts 100.0% (±0%)

Comparing 3 changed files against main @ 34577da2b6d07747e07360a32556a261e32b7adb

Overall Coverage

Metric Coverage
Statements 83.0% (±0%)
Branches 72.7% (+ 0.1%)
Functions 83.2% (+ 0.0%)
Lines 83.5% (±0%)

Tighten the hostname regex to disallow trailing hyphens, which are
invalid per DNS rules and would fail at the API with a confusing error.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rexxars rexxars requested review from binoy14 and runeb March 23, 2026 23:56
@rexxars rexxars marked this pull request as ready for review March 23, 2026 23:56
@rexxars rexxars requested a review from a team as a code owner March 23, 2026 23:56
Copy link
Collaborator

@binoy14 binoy14 left a comment

Choose a reason for hiding this comment

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

This is using cliConfig.studioHost which is deprecated would it be more confusing from the CLI usage perspective?

Copy link
Collaborator

Choose a reason for hiding this comment

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

The error tests should test for exit code?


expect(error).toBeInstanceOf(Error)
expect(error?.message).toContain('Invalid studio hostname')
expect(error?.message).toContain('letters, numbers, and hyphens')
Copy link
Collaborator

Choose a reason for hiding this comment

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

should test for error code?

})

expect(error).toBeInstanceOf(Error)
expect(error?.message).toContain('Invalid studio hostname')
Copy link
Collaborator

Choose a reason for hiding this comment

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

shoudl test for exit code?


expect(error).toBeInstanceOf(Error)
expect(error?.message).toContain('Cannot prompt for studio hostname in unattended mode')
expect(error?.message).toContain('Use --url to specify the studio hostname')
Copy link
Collaborator

Choose a reason for hiding this comment

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

should test for exit code?


expect(error).toBeInstanceOf(Error)
expect(error?.message).toContain('Cannot prompt for studio hostname in unattended mode')
expect(error?.message).toContain('Use --url to specify the studio hostname')
Copy link
Collaborator

Choose a reason for hiding this comment

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

should test for exit code?

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