@sanity/cli 7.7.1 → 7.8.0

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.
Files changed (74) hide show
  1. package/README.md +17 -2
  2. package/dist/actions/auth/ensureAuthenticated.js +1 -1
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/validateToken.js +1 -1
  5. package/dist/actions/auth/login/validateToken.js.map +1 -1
  6. package/dist/actions/build/buildApp.js +2 -3
  7. package/dist/actions/build/buildApp.js.map +1 -1
  8. package/dist/actions/build/buildStudio.js +1 -2
  9. package/dist/actions/build/buildStudio.js.map +1 -1
  10. package/dist/actions/deploy/createUserApplication.js +8 -8
  11. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  12. package/dist/actions/deploy/deployApp.js +121 -24
  13. package/dist/actions/deploy/deployApp.js.map +1 -1
  14. package/dist/actions/deploy/deployChecks.js +90 -66
  15. package/dist/actions/deploy/deployChecks.js.map +1 -1
  16. package/dist/actions/deploy/deployRunner.js +8 -8
  17. package/dist/actions/deploy/deployRunner.js.map +1 -1
  18. package/dist/actions/deploy/deployStudio.js +126 -23
  19. package/dist/actions/deploy/deployStudio.js.map +1 -1
  20. package/dist/actions/deploy/deploymentPlan.js +29 -30
  21. package/dist/actions/deploy/deploymentPlan.js.map +1 -1
  22. package/dist/actions/deploy/findUserApplication.js +39 -14
  23. package/dist/actions/deploy/findUserApplication.js.map +1 -1
  24. package/dist/actions/deploy/resolveDeployTarget.js +43 -0
  25. package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
  26. package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
  27. package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
  28. package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
  29. package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
  30. package/dist/actions/graphql/SchemaError.js.map +1 -1
  31. package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
  32. package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
  33. package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
  34. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  35. package/dist/actions/organizations/getOrganization.js +1 -1
  36. package/dist/actions/organizations/getOrganization.js.map +1 -1
  37. package/dist/actions/schema/validateAction.js +1 -1
  38. package/dist/actions/schema/validateAction.js.map +1 -1
  39. package/dist/actions/skills/setupSkills.js +1 -1
  40. package/dist/actions/skills/setupSkills.js.map +1 -1
  41. package/dist/actions/undeploy/adapters.js +101 -0
  42. package/dist/actions/undeploy/adapters.js.map +1 -0
  43. package/dist/actions/undeploy/runUndeploy.js +165 -0
  44. package/dist/actions/undeploy/runUndeploy.js.map +1 -0
  45. package/dist/actions/undeploy/undeployPlan.js +108 -0
  46. package/dist/actions/undeploy/undeployPlan.js.map +1 -0
  47. package/dist/commands/datasets/copy.js +2 -1
  48. package/dist/commands/datasets/copy.js.map +1 -1
  49. package/dist/commands/mcp/configure.js +1 -1
  50. package/dist/commands/mcp/configure.js.map +1 -1
  51. package/dist/commands/projects/create.js +18 -13
  52. package/dist/commands/projects/create.js.map +1 -1
  53. package/dist/commands/projects/list.js +2 -1
  54. package/dist/commands/projects/list.js.map +1 -1
  55. package/dist/commands/schemas/validate.js +3 -2
  56. package/dist/commands/schemas/validate.js.map +1 -1
  57. package/dist/commands/skills/install.js +1 -1
  58. package/dist/commands/skills/install.js.map +1 -1
  59. package/dist/commands/tokens/list.js +1 -1
  60. package/dist/commands/tokens/list.js.map +1 -1
  61. package/dist/commands/undeploy.js +36 -74
  62. package/dist/commands/undeploy.js.map +1 -1
  63. package/dist/util/checks.js +83 -0
  64. package/dist/util/checks.js.map +1 -0
  65. package/dist/util/toStderrOutput.js +13 -0
  66. package/dist/util/toStderrOutput.js.map +1 -0
  67. package/oclif.manifest.json +1007 -980
  68. package/package.json +7 -8
  69. package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
  70. package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
  71. package/dist/util/compareDependencyVersions.js +0 -110
  72. package/dist/util/compareDependencyVersions.js.map +0 -1
  73. package/dist/util/getErrorMessage.js +0 -28
  74. package/dist/util/getErrorMessage.js.map +0 -1
@@ -4,16 +4,16 @@ import { createGzip } from 'node:zlib';
4
4
  import { formatSchemaValidation, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
5
5
  import { exitCodes } from '@sanity/cli-core';
6
6
  import { spinner } from '@sanity/cli-core/ux';
7
- import { getWorkbench } from '@sanity/workbench-cli/deploy';
7
+ import { buildExposes, deployStudio as deployWorkbenchStudio, getWorkbench } from '@sanity/workbench-cli/deploy';
8
8
  import { pack } from 'tar-fs';
9
9
  import { createDeployment } from '../../services/userApplications.js';
10
10
  import { getAppId } from '../../util/appId.js';
11
- import { NO_PROJECT_ID } from '../../util/errorMessages.js';
11
+ import { NO_ORGANIZATION_ID, NO_PROJECT_ID } from '../../util/errorMessages.js';
12
12
  import { buildStudio } from '../build/buildStudio.js';
13
13
  import { createStudioUserApplication } from './createUserApplication.js';
14
14
  import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
15
15
  import { deployDebug } from './deployDebug.js';
16
- import { listDeploymentFiles } from './deploymentPlan.js';
16
+ import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
17
17
  import { runDeploy } from './deployRunner.js';
18
18
  import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
19
19
  import { findUserApplicationForStudio } from './findUserApplication.js';
@@ -29,16 +29,15 @@ export function deployStudio(options) {
29
29
  const { cliConfig, flags, output, sourceDir } = options;
30
30
  const workDir = options.projectRoot.directory;
31
31
  const isExternal = !!flags.external;
32
- const isWorkbenchApp = getWorkbench(cliConfig) !== null;
32
+ const workbench = getWorkbench(cliConfig);
33
+ const isWorkbenchApp = workbench !== null;
33
34
  const projectId = cliConfig.api?.projectId;
35
+ const organizationId = cliConfig.app?.organizationId;
36
+ const appId = getAppId(cliConfig);
34
37
  const dryRun = !!flags['dry-run'];
35
- const isAutoUpdating = checkAutoUpdates(reporter, {
36
- cliConfig,
37
- flags
38
- });
38
+ // A federated app deploys through Sanity's build/hosting pipeline, which
39
+ // --external skips — fail before doing any other work.
39
40
  if (isExternal && isWorkbenchApp) {
40
- // A federated app deploys through Sanity's build/hosting pipeline, which
41
- // --external skips.
42
41
  reporter.report({
43
42
  exitCode: exitCodes.USAGE_ERROR,
44
43
  message: 'Deploying a federated application to an external host is not yet supported',
@@ -46,6 +45,11 @@ export function deployStudio(options) {
46
45
  status: 'fail'
47
46
  });
48
47
  }
48
+ const appTitle = workbench ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name : '';
49
+ const isAutoUpdating = checkAutoUpdates(reporter, {
50
+ cliConfig,
51
+ flags
52
+ });
49
53
  const version = await checkPackageVersion(reporter, {
50
54
  moduleName: STUDIO_PACKAGE,
51
55
  workDir
@@ -58,10 +62,35 @@ export function deployStudio(options) {
58
62
  solution: 'Add `api.projectId` to sanity.cli.ts',
59
63
  status: 'fail'
60
64
  });
61
- const application = await resolveStudioApplication(options, {
62
- dryRun,
63
- reporter
64
- });
65
+ // Workbench studios deploy to Brett (which needs the org); plain studios
66
+ // resolve/create on user-applications, unchanged.
67
+ let application = null;
68
+ let studioCreated = false;
69
+ let studioTarget = null;
70
+ if (workbench && !isExternal) {
71
+ reporter.report(organizationId ? {
72
+ message: `Organization: ${organizationId}`,
73
+ status: 'pass'
74
+ } : {
75
+ message: NO_ORGANIZATION_ID,
76
+ solution: 'Add `app.organizationId` to sanity.cli.ts',
77
+ status: 'fail'
78
+ });
79
+ // Both modes, so a bad appId fails before the build; its resolved URL feeds
80
+ // the deploy result.
81
+ studioTarget = await checkStudioTarget(reporter, {
82
+ appId,
83
+ isWorkbenchApp: true,
84
+ studioHost: cliConfig.studioHost,
85
+ title: appTitle
86
+ });
87
+ } else {
88
+ ;
89
+ ({ application, created: studioCreated } = await resolveStudioApplication(options, {
90
+ dryRun,
91
+ reporter
92
+ }));
93
+ }
65
94
  await checkBuild(reporter, {
66
95
  build: ()=>buildStudio({
67
96
  autoUpdatesEnabled: isAutoUpdating,
@@ -85,14 +114,57 @@ export function deployStudio(options) {
85
114
  sourceDir
86
115
  });
87
116
  }
117
+ // Report the exposes deploying with the studio, both modes. External studios
118
+ // host their own bundle, so nothing registers.
119
+ const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : [];
88
120
  // Dry run stops here — everything below mutates.
89
121
  if (dryRun) return;
90
122
  // A real deploy has already exited if anything failed; landing here without a
91
- // resolved application or version means the deploy target was never resolved.
92
- if (!application || !version) return;
123
+ // resolved version means the deploy target was never resolved.
124
+ if (!version) return;
93
125
  const studioManifest = await uploadStudioSchema(options, {
94
126
  isExternal
95
127
  });
128
+ // Workbench studios deploy to Brett; plain studios use user-applications.
129
+ if (workbench && !isExternal && organizationId) {
130
+ const { applicationId } = await deployWorkbenchStudio({
131
+ appId,
132
+ interfaces: buildExposes(workbench, {
133
+ appName: workbench.name,
134
+ appTitle,
135
+ exposesAppView: true,
136
+ version
137
+ }),
138
+ isAutoUpdating,
139
+ organizationId,
140
+ output,
141
+ projectId,
142
+ sourceDir,
143
+ studioHost: cliConfig.studioHost,
144
+ title: appTitle,
145
+ version,
146
+ workspaces: toWorkspaces(studioManifest)
147
+ });
148
+ logWorkbenchStudioDeployed({
149
+ applicationId,
150
+ cliConfig,
151
+ output
152
+ });
153
+ return {
154
+ applicationType: 'studio',
155
+ applicationVersion: version,
156
+ ...exposes.length > 0 ? {
157
+ exposes
158
+ } : {},
159
+ target: {
160
+ action: appId ? 'update' : 'create',
161
+ applicationId,
162
+ title: appTitle,
163
+ url: studioTarget?.url ?? null
164
+ }
165
+ };
166
+ }
167
+ if (!application) return;
96
168
  const location = await shipStudioDeployment({
97
169
  application,
98
170
  isAutoUpdating,
@@ -105,6 +177,7 @@ export function deployStudio(options) {
105
177
  applicationType: 'studio',
106
178
  applicationVersion: version,
107
179
  target: {
180
+ action: studioCreated ? 'create' : 'update',
108
181
  applicationId: application.id,
109
182
  title: application.title ?? null,
110
183
  url: location
@@ -117,22 +190,27 @@ export function deployStudio(options) {
117
190
  */ async function resolveStudioApplication(options, { dryRun, reporter }) {
118
191
  const { cliConfig, flags, output } = options;
119
192
  const isExternal = !!flags.external;
193
+ const appId = getAppId(cliConfig);
120
194
  // Sets the title on a newly registered studio; blank falls back to undefined
121
195
  const title = flags.title?.trim() || undefined;
122
196
  if (dryRun) {
123
197
  await checkStudioTarget(reporter, {
124
- appId: getAppId(cliConfig),
198
+ appId,
125
199
  isExternal,
126
200
  projectId: cliConfig.api?.projectId,
127
201
  studioHost: cliConfig.studioHost,
128
202
  title,
129
203
  urlFlag: flags.url
130
204
  });
131
- return null;
205
+ return {
206
+ application: null,
207
+ created: false
208
+ };
132
209
  }
133
210
  const projectId = cliConfig.api?.projectId ?? '';
134
- let application = await findUserApplicationForStudio({
135
- appId: getAppId(cliConfig),
211
+ // `created` is true when a configured-but-unregistered host was just registered.
212
+ const { application, created } = await findUserApplicationForStudio({
213
+ appId,
136
214
  isExternal,
137
215
  output,
138
216
  projectId,
@@ -150,15 +228,22 @@ export function deployStudio(options) {
150
228
  output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
151
229
  output.log('you will need one. Please enter the subdomain you want to use.');
152
230
  }
153
- application = await createStudioUserApplication({
231
+ const registered = await createStudioUserApplication({
154
232
  projectId,
155
233
  title,
156
234
  urlType: isExternal ? 'external' : 'internal'
157
235
  });
158
- deployDebug('Created user application', application);
236
+ deployDebug('Created user application', registered);
237
+ return {
238
+ application: registered,
239
+ created: true
240
+ };
159
241
  }
160
242
  deployDebug('Found user application', application);
161
- return application;
243
+ return {
244
+ application,
245
+ created
246
+ };
162
247
  }
163
248
  /** Extracts the studio schema and manifest and uploads them to the schema store. */ async function uploadStudioSchema(options, { isExternal }) {
164
249
  const { cliConfig, flags, output, projectRoot, sourceDir } = options;
@@ -236,6 +321,24 @@ export default defineCliConfig({
236
321
  output.log(`\n${example}`);
237
322
  return location;
238
323
  }
324
+ function toWorkspaces(manifest) {
325
+ return (manifest?.workspaces ?? []).map((workspace)=>({
326
+ basePath: workspace.basePath,
327
+ dataset: workspace.dataset,
328
+ icon: workspace.icon,
329
+ name: workspace.name,
330
+ projectId: workspace.projectId,
331
+ subtitle: workspace.subtitle,
332
+ title: workspace.title
333
+ }));
334
+ }
335
+ /** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */ function logWorkbenchStudioDeployed({ applicationId, cliConfig, output }) {
336
+ output.log(`\nSuccess! Studio deployed`);
337
+ if (getAppId(cliConfig)) return;
338
+ output.log(`\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`);
339
+ output.log(`to the \`deployment\` section in sanity.cli.js or sanity.cli.ts`);
340
+ output.log(`to avoid prompting for application id on next deploy.`);
341
+ }
239
342
  function studioBuildSkipReason({ build, isExternal }) {
240
343
  if (isExternal) return 'Build skipped for externally hosted studios';
241
344
  if (!build) return 'Build skipped (--no-build) — validating existing output directory';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {getWorkbench} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n type CheckReporter,\n checkStudioTarget,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: CheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const isWorkbenchApp = getWorkbench(cliConfig) !== null\n const projectId = cliConfig.api?.projectId\n const dryRun = !!flags['dry-run']\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n if (isExternal && isWorkbenchApp) {\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips.\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n const application = await resolveStudioApplication(options, {dryRun, reporter})\n\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved application or version means the deploy target was never resolved.\n if (!application || !version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n applicationType: 'studio',\n applicationVersion: version,\n target: {applicationId: application.id, title: application.title ?? null, url: location},\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: CheckReporter},\n): Promise<UserApplication | null> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId: getAppId(cliConfig),\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return null\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n let application = await findUserApplicationForStudio({\n appId: getAppId(cliConfig),\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n application = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', application)\n }\n\n deployDebug('Found user application', application)\n return application\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","getWorkbench","pack","createDeployment","getAppId","NO_PROJECT_ID","buildStudio","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","deployStudio","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","isWorkbenchApp","projectId","api","dryRun","isAutoUpdating","report","exitCode","USAGE_ERROR","message","solution","status","version","moduleName","application","resolveStudioApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","studioManifest","uploadStudioSchema","location","shipStudioDeployment","applicationType","applicationVersion","target","applicationId","id","title","url","trim","undefined","appId","studioHost","urlFlag","unattended","yes","log","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","isApp","manifest","fail","succeed","named","example"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deployStudio.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip, type Gzip} from 'node:zlib'\n\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n type BrettWorkspace,\n buildExposes,\n deployStudio as deployWorkbenchStudio,\n getWorkbench,\n} from '@sanity/workbench-cli/deploy'\nimport {type StudioManifest} from 'sanity'\nimport {pack} from 'tar-fs'\n\nimport {createDeployment, type UserApplication} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_ORGANIZATION_ID, NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {createStudioUserApplication} from './createUserApplication.js'\nimport {\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n checkStudioTarget,\n type DeployCheckReporter,\n type DeployTarget,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles, reportExposes} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\n\nconst STUDIO_PACKAGE = 'sanity'\n\nexport function deployStudio(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({flags, projectRoot, sourceDir}) =>\n flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runStudioDeployment,\n type: 'studio',\n })\n}\n\n/** Validates the deploy, extracts and uploads the schema, and ships the build. */\nasync function runStudioDeployment(\n options: DeployAppOptions,\n reporter: DeployCheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const isExternal = !!flags.external\n const workbench = getWorkbench(cliConfig)\n const isWorkbenchApp = workbench !== null\n const projectId = cliConfig.api?.projectId\n const organizationId = cliConfig.app?.organizationId\n const appId = getAppId(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A federated app deploys through Sanity's build/hosting pipeline, which\n // --external skips — fail before doing any other work.\n if (isExternal && isWorkbenchApp) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: 'Deploying a federated application to an external host is not yet supported',\n solution: 'Remove the --external flag to deploy to Sanity hosting',\n status: 'fail',\n })\n }\n\n const appTitle = workbench\n ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name\n : ''\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n const version = await checkPackageVersion(reporter, {\n moduleName: STUDIO_PACKAGE,\n workDir,\n })\n\n reporter.report(\n projectId\n ? {message: `Project: ${projectId}`, status: 'pass'}\n : {\n message: NO_PROJECT_ID,\n solution: 'Add `api.projectId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n // Workbench studios deploy to Brett (which needs the org); plain studios\n // resolve/create on user-applications, unchanged.\n let application: UserApplication | null = null\n let studioCreated = false\n let studioTarget: DeployTarget | null = null\n if (workbench && !isExternal) {\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n // Both modes, so a bad appId fails before the build; its resolved URL feeds\n // the deploy result.\n studioTarget = await checkStudioTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n studioHost: cliConfig.studioHost,\n title: appTitle,\n })\n } else {\n ;({application, created: studioCreated} = await resolveStudioApplication(options, {\n dryRun,\n reporter,\n }))\n }\n\n await checkBuild(reporter, {\n build: () =>\n buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: studioBuildSkipReason({build: flags.build, isExternal}),\n successMessage: 'Studio built',\n })\n\n if (!isExternal) {\n await verifyOutputDir({isWorkbenchApp, reporter, sourceDir})\n }\n\n // Report the exposes deploying with the studio, both modes. External studios\n // host their own bundle, so nothing registers.\n const exposes = workbench && !isExternal ? reportExposes(reporter, workbench) : []\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved version means the deploy target was never resolved.\n if (!version) return\n\n const studioManifest = await uploadStudioSchema(options, {isExternal})\n // Workbench studios deploy to Brett; plain studios use user-applications.\n if (workbench && !isExternal && organizationId) {\n const {applicationId} = await deployWorkbenchStudio({\n appId,\n interfaces: buildExposes(workbench, {\n appName: workbench.name,\n appTitle,\n exposesAppView: true,\n version,\n }),\n isAutoUpdating,\n organizationId,\n output,\n projectId,\n sourceDir,\n studioHost: cliConfig.studioHost,\n title: appTitle,\n version,\n workspaces: toWorkspaces(studioManifest),\n })\n logWorkbenchStudioDeployed({applicationId, cliConfig, output})\n return {\n applicationType: 'studio',\n applicationVersion: version,\n ...(exposes.length > 0 ? {exposes} : {}),\n target: {\n action: appId ? 'update' : 'create',\n applicationId,\n title: appTitle,\n url: studioTarget?.url ?? null,\n },\n }\n }\n\n if (!application) return\n const location = await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n })\n\n return {\n applicationType: 'studio',\n applicationVersion: version,\n target: {\n action: studioCreated ? 'create' : 'update',\n applicationId: application.id,\n title: application.title ?? null,\n url: location,\n },\n }\n}\n\n/**\n * Finds the application a real deploy targets, registering a studio host when\n * none is configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveStudioApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: DeployCheckReporter},\n): Promise<{application: UserApplication | null; created: boolean}> {\n const {cliConfig, flags, output} = options\n const isExternal = !!flags.external\n const appId = getAppId(cliConfig)\n // Sets the title on a newly registered studio; blank falls back to undefined\n const title = flags.title?.trim() || undefined\n\n if (dryRun) {\n await checkStudioTarget(reporter, {\n appId,\n isExternal,\n projectId: cliConfig.api?.projectId,\n studioHost: cliConfig.studioHost,\n title,\n urlFlag: flags.url,\n })\n return {application: null, created: false}\n }\n\n const projectId = cliConfig.api?.projectId ?? ''\n // `created` is true when a configured-but-unregistered host was just registered.\n const {application, created} = await findUserApplicationForStudio({\n appId,\n isExternal,\n output,\n projectId,\n studioHost: cliConfig.studioHost,\n title,\n unattended: !!flags.yes,\n urlFlag: flags.url,\n })\n\n if (!application) {\n if (isExternal) {\n output.log('Your project has not been registered with an external studio URL.')\n output.log('Please enter the full URL where your studio is hosted.')\n } else {\n output.log('Your project has not been assigned a studio hostname.')\n output.log('To deploy your Sanity Studio to our hosted sanity.studio service,')\n output.log('you will need one. Please enter the subdomain you want to use.')\n }\n\n const registered = await createStudioUserApplication({\n projectId,\n title,\n urlType: isExternal ? 'external' : 'internal',\n })\n deployDebug('Created user application', registered)\n return {application: registered, created: true}\n }\n\n deployDebug('Found user application', application)\n return {application, created}\n}\n\n/** Extracts the studio schema and manifest and uploads them to the schema store. */\nasync function uploadStudioSchema(\n options: DeployAppOptions,\n {isExternal}: {isExternal: boolean},\n): Promise<StudioManifest | null> {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests(\n {\n configPath: projectRoot.path,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId: cliConfig.api?.projectId ?? '',\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir: projectRoot.directory,\n },\n output,\n )\n } catch (error) {\n deployDebug('Error deploying studio schemas and manifests', error)\n if (error instanceof SchemaExtractionError) {\n output.error(formatSchemaValidation(error.validation || []), {exit: 1})\n }\n output.error(`Error deploying studio schemas and manifests: ${error}`, {exit: 1})\n }\n\n if (!studioManifest) {\n output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {\n exit: 1,\n })\n }\n\n return studioManifest\n}\n\nasync function shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n isExternal: boolean\n options: DeployAppOptions\n studioManifest: StudioManifest | null\n version: string\n}): Promise<string> {\n const {cliConfig, output, sourceDir} = options\n\n let tarball: Gzip | undefined\n if (!isExternal) {\n tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n }\n\n const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n let location: string\n try {\n ;({location} = await createDeployment({\n applicationId: application.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId: cliConfig.api?.projectId,\n tarball,\n version,\n }))\n } catch (error) {\n spin.fail()\n throw error\n }\n spin.succeed()\n\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(\n isExternal\n ? `\\nSuccess! Studio registered${named}`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}${named}`,\n )\n\n if (getAppId(cliConfig)) return location\n\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${application.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n output.log(`\\n${example}`)\n\n return location\n}\n\nfunction toWorkspaces(manifest: StudioManifest | null): BrettWorkspace[] {\n return (manifest?.workspaces ?? []).map((workspace) => ({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n icon: workspace.icon,\n name: workspace.name,\n projectId: workspace.projectId,\n subtitle: workspace.subtitle,\n title: workspace.title,\n }))\n}\n\n/** Renders the workbench studio's deploy result; the appId hint shows only when none is configured. */\nfunction logWorkbenchStudioDeployed({\n applicationId,\n cliConfig,\n output,\n}: {\n applicationId: string\n cliConfig: DeployAppOptions['cliConfig']\n output: DeployAppOptions['output']\n}): void {\n output.log(`\\nSuccess! Studio deployed`)\n if (getAppId(cliConfig)) return\n\n output.log(`\\nAdd ${styleText('cyan', `appId: '${applicationId}'`)}`)\n output.log(`to the \\`deployment\\` section in sanity.cli.js or sanity.cli.ts`)\n output.log(`to avoid prompting for application id on next deploy.`)\n}\n\nfunction studioBuildSkipReason({build, isExternal}: {build: boolean; isExternal: boolean}) {\n if (isExternal) return 'Build skipped for externally hosted studios'\n if (!build) return 'Build skipped (--no-build) — validating existing output directory'\n return\n}\n"],"names":["basename","dirname","styleText","createGzip","formatSchemaValidation","SchemaExtractionError","exitCodes","spinner","buildExposes","deployStudio","deployWorkbenchStudio","getWorkbench","pack","createDeployment","getAppId","NO_ORGANIZATION_ID","NO_PROJECT_ID","buildStudio","createStudioUserApplication","checkAutoUpdates","checkBuild","checkPackageVersion","checkStudioTarget","verifyOutputDir","deployDebug","listDeploymentFiles","reportExposes","runDeploy","deployStudioSchemasAndManifests","findUserApplicationForStudio","STUDIO_PACKAGE","options","listFiles","flags","projectRoot","sourceDir","external","Promise","resolve","directory","run","runStudioDeployment","type","reporter","cliConfig","output","workDir","isExternal","workbench","isWorkbenchApp","projectId","api","organizationId","app","appId","dryRun","report","exitCode","USAGE_ERROR","message","solution","status","appTitle","title","trim","name","isAutoUpdating","version","moduleName","application","studioCreated","studioTarget","studioHost","created","resolveStudioApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","studioBuildSkipReason","successMessage","exposes","studioManifest","uploadStudioSchema","applicationId","interfaces","appName","exposesAppView","workspaces","toWorkspaces","logWorkbenchStudioDeployed","applicationType","applicationVersion","length","target","action","url","location","shipStudioDeployment","id","undefined","urlFlag","unattended","yes","log","registered","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","isApp","manifest","fail","succeed","named","example","map","workspace","basePath","dataset","icon","subtitle"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { readdir, stat } from 'node:fs/promises';
2
2
  import { join, relative, sep } from 'node:path';
3
3
  import { styleText } from 'node:util';
4
- import { logSymbols } from '@sanity/cli-core/ux';
4
+ import { summarizeExposes } from '@sanity/workbench-cli/deploy';
5
+ import { checkStatusIcon, nestLines, renderIssues } from '../../util/checks.js';
5
6
  import { pluralize } from '../../util/pluralize.js';
6
7
  /**
7
8
  * Lists the files a deploy would pack from `sourceDir`, as paths relative to
@@ -49,17 +50,43 @@ function totalBytes(files) {
49
50
  if (check.status === 'fail') errors[check.message] = check.solution ?? null;
50
51
  else if (check.status === 'warn') warnings.push(check.message);
51
52
  }
53
+ // `exposes`, `config` and `isSingleton` are workbench-only; plain
54
+ // apps (and apps that don't set the flag) omit them.
52
55
  return {
53
56
  applicationType: plan.type,
54
57
  applicationVersion: plan.version,
55
58
  errors,
59
+ ...plan.exposes.length > 0 ? {
60
+ exposes: plan.exposes
61
+ } : {},
56
62
  files: plan.files,
63
+ ...plan.config ? {
64
+ config: plan.config
65
+ } : {},
57
66
  isDeployable: isDeployable(plan),
67
+ ...plan.isSingleton === undefined ? {} : {
68
+ isSingleton: plan.isSingleton
69
+ },
58
70
  target: plan.target,
59
71
  totalBytes: totalBytes(plan.files),
60
72
  warnings
61
73
  };
62
74
  }
75
+ /**
76
+ * Reports an app's exposes as pass checks and returns them structured for the
77
+ * `--json` payload. The structured list rides on the first check so a dry run's
78
+ * collector can read it back.
79
+ */ export function reportExposes(reporter, app) {
80
+ const { exposes, lines } = summarizeExposes(app);
81
+ for (const [index, message] of lines.entries()){
82
+ reporter.report({
83
+ exposes: index === 0 ? exposes : undefined,
84
+ message,
85
+ status: 'pass'
86
+ });
87
+ }
88
+ return exposes;
89
+ }
63
90
  export function renderDeploymentPlan(plan, output) {
64
91
  const label = plan.type === 'coreApp' ? 'application' : 'studio';
65
92
  const problems = plan.checks.filter((check)=>check.status === 'fail');
@@ -68,7 +95,7 @@ export function renderDeploymentPlan(plan, output) {
68
95
  // Only pass/skip here; problems and warnings render below with their fixes.
69
96
  for (const check of plan.checks){
70
97
  if (check.status === 'pass' || check.status === 'skip') {
71
- output.log(` ${statusIcon(check.status)} ${check.message}`);
98
+ output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`));
72
99
  }
73
100
  }
74
101
  output.log(isDeployable(plan) ? styleText('green', `\nThis ${label} can be deployed.`) : styleText('red', `\nThis ${label} can't be deployed.`));
@@ -82,36 +109,8 @@ export function renderDeploymentPlan(plan, output) {
82
109
  }
83
110
  }
84
111
  }
85
- function renderIssues(output, title, checks) {
86
- if (checks.length === 0) return;
87
- output.log(`\n${title}`);
88
- for (const check of checks){
89
- const fix = check.solution ? `: ${check.solution}` : '';
90
- output.log(` ${statusIcon(check.status)} ${check.message}${fix}`);
91
- }
92
- }
93
112
  function formatMB(bytes) {
94
113
  return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
95
114
  }
96
- function statusIcon(status) {
97
- switch(status){
98
- case 'fail':
99
- {
100
- return logSymbols.error;
101
- }
102
- case 'skip':
103
- {
104
- return logSymbols.info;
105
- }
106
- case 'warn':
107
- {
108
- return logSymbols.warning;
109
- }
110
- default:
111
- {
112
- return logSymbols.success;
113
- }
114
- }
115
- }
116
115
 
117
116
  //# sourceMappingURL=deploymentPlan.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\n\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployTarget} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n files: DeploymentFile[]\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n /** Installed framework version the deploy would use; `null` when not found. */\n version: string | null\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n applicationType: DeploymentPlan['type']\n applicationVersion: string | null\n errors: Record<string, string | null>\n files: DeploymentFile[]\n isDeployable: boolean\n target: DeployTarget | null\n totalBytes: number\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n return {\n applicationType: plan.type,\n applicationVersion: plan.version,\n errors,\n files: plan.files,\n isDeployable: isDeployable(plan),\n target: plan.target,\n totalBytes: totalBytes(plan.files),\n warnings,\n }\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(` ${statusIcon(check.status)} ${check.message}`)\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction renderIssues(output: Output, title: string, checks: DeployCheck[]): void {\n if (checks.length === 0) return\n\n output.log(`\\n${title}`)\n for (const check of checks) {\n const fix = check.solution ? `: ${check.solution}` : ''\n output.log(` ${statusIcon(check.status)} ${check.message}${fix}`)\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n\nfunction statusIcon(status: DeployCheck['status']): string {\n switch (status) {\n case 'fail': {\n return logSymbols.error\n }\n case 'skip': {\n return logSymbols.info\n }\n case 'warn': {\n return logSymbols.warning\n }\n default: {\n return logSymbols.success\n }\n }\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","logSymbols","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","applicationType","type","applicationVersion","version","target","renderDeploymentPlan","output","label","problems","filter","log","statusIcon","renderIssues","length","formatMB","title","fix","bytes","toFixed","error","info","warning","success"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/deploymentPlan.ts"],"sourcesContent":["import {readdir, stat} from 'node:fs/promises'\nimport {join, relative, sep} from 'node:path'\nimport {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core'\nimport {type DeployedExpose, summarizeExposes} from '@sanity/workbench-cli/deploy'\n\nimport {checkStatusIcon, nestLines, renderIssues} from '../../util/checks.js'\nimport {pluralize} from '../../util/pluralize.js'\nimport {type DeployCheck, type DeployCheckReporter, type DeployTarget} from './deployChecks.js'\n\nexport interface DeploymentFile {\n /** Path relative to the project root, POSIX-style. */\n path: string\n size: number\n}\n\n/** What a `--dry-run` deploy would do: the real deploy sequence with every mutation gated off. */\nexport interface DeploymentPlan {\n checks: DeployCheck[]\n /** Media-library config summary; `null` unless a config deploys. */\n config: string | null\n /** Workbench views and services registered with the deploy. */\n exposes: DeployedExpose[]\n files: DeploymentFile[]\n /** The resolved deploy target; `null` when the checks can't determine one. */\n target: DeployTarget | null\n type: 'coreApp' | 'studio'\n /** Installed framework version the deploy would use; `null` when not found. */\n version: string | null\n\n /** The app's explicit `isSingleton` flag; `undefined` when the app doesn't set it. */\n isSingleton?: boolean\n}\n\n/**\n * Lists the files a deploy would pack from `sourceDir`, as paths relative to\n * `fromDir`. A missing directory yields an empty list rather than throwing.\n */\nexport async function listDeploymentFiles(\n sourceDir: string,\n fromDir: string,\n): Promise<DeploymentFile[]> {\n const walk = async (dir: string): Promise<string[]> => {\n let entries\n try {\n entries = await readdir(dir, {withFileTypes: true})\n } catch {\n return []\n }\n const nested = await Promise.all(\n entries.map((entry) => {\n const full = join(dir, entry.name)\n return entry.isDirectory() ? walk(full) : Promise.resolve([full])\n }),\n )\n return nested.flat()\n }\n\n const absolute = await walk(sourceDir)\n const files = await Promise.all(\n absolute.map(async (file) => ({\n // Deploy paths are POSIX-style regardless of the host OS (Windows gives `\\`).\n path: relative(fromDir, file).split(sep).join('/'),\n size: (await stat(file)).size,\n })),\n )\n return files.toSorted((a, b) => a.path.localeCompare(b.path))\n}\n\nexport function isDeployable(plan: DeploymentPlan): boolean {\n return plan.checks.every((check) => check.status !== 'fail')\n}\n\nfunction totalBytes(files: DeploymentFile[]): number {\n return files.reduce((sum, file) => sum + file.size, 0)\n}\n\n/**\n * A problem-focused, machine-readable projection of the plan: blocking problems\n * mapped to their fix, warnings as messages. Derived from the same checks the\n * human report renders (its pass/skip lines are informational and omitted here).\n */\nexport function deploymentPlanToJson(plan: DeploymentPlan): {\n applicationType: DeploymentPlan['type']\n applicationVersion: string | null\n config?: string\n errors: Record<string, string | null>\n exposes?: DeployedExpose[]\n files: DeploymentFile[]\n isDeployable: boolean\n isSingleton?: boolean\n target: DeployTarget | null\n totalBytes: number\n warnings: string[]\n} {\n const errors: Record<string, string | null> = {}\n const warnings: string[] = []\n for (const check of plan.checks) {\n if (check.status === 'fail') errors[check.message] = check.solution ?? null\n else if (check.status === 'warn') warnings.push(check.message)\n }\n\n // `exposes`, `config` and `isSingleton` are workbench-only; plain\n // apps (and apps that don't set the flag) omit them.\n return {\n applicationType: plan.type,\n applicationVersion: plan.version,\n errors,\n ...(plan.exposes.length > 0 ? {exposes: plan.exposes} : {}),\n files: plan.files,\n ...(plan.config ? {config: plan.config} : {}),\n isDeployable: isDeployable(plan),\n ...(plan.isSingleton === undefined ? {} : {isSingleton: plan.isSingleton}),\n target: plan.target,\n totalBytes: totalBytes(plan.files),\n warnings,\n }\n}\n\n/**\n * Reports an app's exposes as pass checks and returns them structured for the\n * `--json` payload. The structured list rides on the first check so a dry run's\n * collector can read it back.\n */\nexport function reportExposes(\n reporter: DeployCheckReporter,\n app: Parameters<typeof summarizeExposes>[0],\n): DeployedExpose[] {\n const {exposes, lines} = summarizeExposes(app)\n for (const [index, message] of lines.entries()) {\n reporter.report({exposes: index === 0 ? exposes : undefined, message, status: 'pass'})\n }\n return exposes\n}\n\nexport function renderDeploymentPlan(plan: DeploymentPlan, output: Output): void {\n const label = plan.type === 'coreApp' ? 'application' : 'studio'\n const problems = plan.checks.filter((check) => check.status === 'fail')\n const warnings = plan.checks.filter((check) => check.status === 'warn')\n\n output.log('\\nDry run — no changes made.\\n')\n\n // Only pass/skip here; problems and warnings render below with their fixes.\n for (const check of plan.checks) {\n if (check.status === 'pass' || check.status === 'skip') {\n output.log(nestLines(` ${checkStatusIcon(check.status)} ${check.message}`))\n }\n }\n\n output.log(\n isDeployable(plan)\n ? styleText('green', `\\nThis ${label} can be deployed.`)\n : styleText('red', `\\nThis ${label} can't be deployed.`),\n )\n\n renderIssues(output, 'Problems to fix:', problems)\n renderIssues(output, 'Warnings:', warnings)\n\n // A blocked deploy uploads nothing, so only list files for a deployable plan.\n if (isDeployable(plan) && plan.files.length > 0) {\n output.log(\n `\\nFiles to deploy (${plan.files.length} ${pluralize('file', plan.files.length)}, ${formatMB(totalBytes(plan.files))}):`,\n )\n for (const file of plan.files) {\n output.log(` ${file.path} (${formatMB(file.size)})`)\n }\n }\n}\n\nfunction formatMB(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`\n}\n"],"names":["readdir","stat","join","relative","sep","styleText","summarizeExposes","checkStatusIcon","nestLines","renderIssues","pluralize","listDeploymentFiles","sourceDir","fromDir","walk","dir","entries","withFileTypes","nested","Promise","all","map","entry","full","name","isDirectory","resolve","flat","absolute","files","file","path","split","size","toSorted","a","b","localeCompare","isDeployable","plan","checks","every","check","status","totalBytes","reduce","sum","deploymentPlanToJson","errors","warnings","message","solution","push","applicationType","type","applicationVersion","version","exposes","length","config","isSingleton","undefined","target","reportExposes","reporter","app","lines","index","report","renderDeploymentPlan","output","label","problems","filter","log","formatMB","bytes","toFixed"],"mappings":"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"}
@@ -2,11 +2,12 @@
2
2
  * Finds (or creates) the user application a deploy targets — the interactive
3
3
  * adapter that turns the resolveDeployTarget verdicts into prompts, creation,
4
4
  * and exits. Dry runs consume the same verdicts read-only (see deployChecks).
5
- */ import { select, Separator, spinner } from '@sanity/cli-core/ux';
5
+ */ import { getErrorMessage } from '@sanity/cli-core/errors';
6
+ import { select, Separator, spinner } from '@sanity/cli-core/ux';
6
7
  import { createUserApplication } from '../../services/userApplications.js';
7
8
  import { getAppId } from '../../util/appId.js';
8
- import { getErrorMessage } from '../../util/getErrorMessage.js';
9
- import { createFailFastReporter, describeAppTarget, describeAppTargetError, describeStudioTarget } from './deployChecks.js';
9
+ import { createFailFastReporter } from '../../util/checks.js';
10
+ import { describeAppTarget, describeAppTargetError, describeStudioTarget } from './deployChecks.js';
10
11
  import { deployDebug } from './deployDebug.js';
11
12
  import { resolveAppDeployTarget, resolveStudioDeployTarget } from './resolveDeployTarget.js';
12
13
  export async function findUserApplication(options) {
@@ -73,31 +74,49 @@ export async function findUserApplicationForStudio(options) {
73
74
  output.error(`Failed to resolve deploy target: ${getErrorMessage(error)}`, {
74
75
  exit: 1
75
76
  });
76
- return null;
77
+ return {
78
+ application: null,
79
+ created: false
80
+ };
77
81
  }
78
82
  if (resolution.type === 'found') {
79
83
  spin.succeed();
80
- return resolution.application;
84
+ return {
85
+ application: resolution.application,
86
+ created: false
87
+ };
81
88
  }
82
- // The configured host isn't registered yet — a deploy registers it without prompting
89
+ // The configured host isn't registered yet — a deploy registers it without
90
+ // prompting, so this returns a newly created studio.
83
91
  if (resolution.type === 'would-create') {
84
92
  spin.succeed();
85
- return createFromConfiguredHost({
93
+ const application = await createFromConfiguredHost({
86
94
  appHost: resolution.appHost,
87
95
  output,
88
96
  projectId,
89
97
  title,
90
98
  urlType
91
99
  });
100
+ return {
101
+ application,
102
+ created: application !== null
103
+ };
92
104
  }
93
105
  if (resolution.type === 'needs-input' && !unattended) {
94
106
  spin.succeed();
95
107
  // Nothing to select from — the caller prompts for a brand new host
96
- if (resolution.existing.length === 0) return null;
97
- return promptForExistingStudio({
98
- existing: resolution.existing,
99
- urlType
100
- });
108
+ if (resolution.existing.length === 0) return {
109
+ application: null,
110
+ created: false
111
+ };
112
+ // A selected existing studio is an update; "new" returns null and the caller registers it.
113
+ return {
114
+ application: await promptForExistingStudio({
115
+ existing: resolution.existing,
116
+ urlType
117
+ }),
118
+ created: false
119
+ };
101
120
  }
102
121
  spin.fail();
103
122
  // 'blocked' diagnoses as a skip (its root cause fails an earlier check), so it
@@ -106,12 +125,18 @@ export async function findUserApplicationForStudio(options) {
106
125
  output.error(resolution.message, {
107
126
  exit: 1
108
127
  });
109
- return null;
128
+ return {
129
+ application: null,
130
+ created: false
131
+ };
110
132
  }
111
133
  createFailFastReporter(output).report(describeStudioTarget(resolution, {
112
134
  isExternal
113
135
  }));
114
- return null;
136
+ return {
137
+ application: null,
138
+ created: false
139
+ };
115
140
  }
116
141
  /**
117
142
  * The host is configured (studioHost or --url) but not registered yet: