@sanity/cli 7.4.2 → 7.6.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.
- package/dist/actions/build/buildApp.js +7 -183
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStudio.js +6 -213
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/build/shouldAutoUpdate.js +100 -22
- package/dist/actions/build/shouldAutoUpdate.js.map +1 -1
- package/dist/actions/deploy/{createStudioUserApplication.js → createUserApplication.js} +56 -5
- package/dist/actions/deploy/createUserApplication.js.map +1 -0
- package/dist/actions/deploy/deployApp.js +232 -185
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +263 -0
- package/dist/actions/deploy/deployChecks.js.map +1 -0
- package/dist/actions/deploy/deployRunner.js +57 -0
- package/dist/actions/deploy/deployRunner.js.map +1 -0
- package/dist/actions/deploy/deployStudio.js +189 -198
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +89 -0
- package/dist/actions/deploy/deploymentPlan.js.map +1 -0
- package/dist/actions/deploy/findUserApplication.js +209 -0
- package/dist/actions/deploy/findUserApplication.js.map +1 -0
- package/dist/actions/deploy/resolveDeployTarget.js +185 -0
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -0
- package/dist/actions/dev/devAction.js +1 -1
- package/dist/actions/dev/devAction.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +18 -6
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startAppDevServer.js +1 -1
- package/dist/actions/dev/servers/startAppDevServer.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +2 -1
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js +14 -0
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
- package/dist/actions/mcp/editorConfigs.js +11 -5
- package/dist/actions/mcp/editorConfigs.js.map +1 -1
- package/dist/commands/deploy.js +27 -11
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.js +2 -1
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.js +2 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/exports/index.d.ts +9 -0
- package/dist/exports/index.js +1 -8
- package/dist/exports/index.js.map +1 -1
- package/dist/server/devServer.js +27 -3
- package/dist/server/devServer.js.map +1 -1
- package/dist/util/appId.js +13 -5
- package/dist/util/appId.js.map +1 -1
- package/dist/util/compareDependencyVersions.js.map +1 -1
- package/dist/util/determineIsApp.js +1 -1
- package/dist/util/determineIsApp.js.map +1 -1
- package/dist/util/errorMessages.js +2 -0
- package/dist/util/errorMessages.js.map +1 -1
- package/oclif.manifest.json +14 -1
- package/package.json +8 -8
- package/templates/app-quickstart/.claude/skills/sanity-app-sdk/SKILL.md +56 -0
- package/templates/app-quickstart/AGENTS.md +40 -0
- package/templates/app-quickstart/README.md +22 -0
- package/templates/app-quickstart/src/ExampleComponent.css +6 -0
- package/templates/app-quickstart/src/ExampleComponent.tsx +30 -9
- package/templates/app-sanity-ui/.claude/skills/sanity-app-sdk/SKILL.md +60 -0
- package/templates/app-sanity-ui/AGENTS.md +44 -0
- package/templates/app-sanity-ui/README.md +23 -0
- package/templates/app-sanity-ui/src/ExampleComponent.tsx +8 -5
- package/dist/actions/build/handlePrereleaseVersions.js +0 -44
- package/dist/actions/build/handlePrereleaseVersions.js.map +0 -1
- package/dist/actions/deploy/createStudioUserApplication.js.map +0 -1
- package/dist/actions/deploy/createUserApplicationForApp.js +0 -56
- package/dist/actions/deploy/createUserApplicationForApp.js.map +0 -1
- package/dist/actions/deploy/findUserApplicationForApp.js +0 -111
- package/dist/actions/deploy/findUserApplicationForApp.js.map +0 -1
- package/dist/actions/deploy/findUserApplicationForStudio.js +0 -172
- package/dist/actions/deploy/findUserApplicationForStudio.js.map +0 -1
- package/dist/actions/deploy/viewDeployment.js +0 -32
- package/dist/actions/deploy/viewDeployment.js.map +0 -1
|
@@ -1,105 +1,68 @@
|
|
|
1
1
|
import { basename, dirname } from 'node:path';
|
|
2
2
|
import { styleText } from 'node:util';
|
|
3
3
|
import { createGzip } from 'node:zlib';
|
|
4
|
-
import { CLIError } from '@oclif/core/errors';
|
|
5
4
|
import { formatSchemaValidation, SchemaExtractionError } from '@sanity/cli-build/_internal/extract';
|
|
6
|
-
import { exitCodes
|
|
5
|
+
import { exitCodes } from '@sanity/cli-core';
|
|
7
6
|
import { spinner } from '@sanity/cli-core/ux';
|
|
8
7
|
import { getWorkbench } from '@sanity/workbench-cli/deploy';
|
|
9
8
|
import { pack } from 'tar-fs';
|
|
10
9
|
import { createDeployment } from '../../services/userApplications.js';
|
|
11
10
|
import { getAppId } from '../../util/appId.js';
|
|
12
11
|
import { NO_PROJECT_ID } from '../../util/errorMessages.js';
|
|
13
|
-
import { getErrorMessage } from '../../util/getErrorMessage.js';
|
|
14
12
|
import { buildStudio } from '../build/buildStudio.js';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { createStudioUserApplication } from './createStudioUserApplication.js';
|
|
13
|
+
import { createStudioUserApplication } from './createUserApplication.js';
|
|
14
|
+
import { checkAutoUpdates, checkBuild, checkPackageVersion, checkStudioTarget, verifyOutputDir } from './deployChecks.js';
|
|
18
15
|
import { deployDebug } from './deployDebug.js';
|
|
16
|
+
import { listDeploymentFiles } from './deploymentPlan.js';
|
|
17
|
+
import { runDeploy } from './deployRunner.js';
|
|
19
18
|
import { deployStudioSchemasAndManifests } from './deployStudioSchemasAndManifests.js';
|
|
20
|
-
import { findUserApplicationForStudio } from './
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
import { findUserApplicationForStudio } from './findUserApplication.js';
|
|
20
|
+
export function deployStudio(options) {
|
|
21
|
+
return runDeploy(options, {
|
|
22
|
+
listFiles: ({ flags, projectRoot, sourceDir })=>flags.external ? Promise.resolve([]) : listDeploymentFiles(sourceDir, projectRoot.directory),
|
|
23
|
+
run: runStudioDeployment,
|
|
24
|
+
type: 'studio'
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/** Validates the deploy, extracts and uploads the schema, and ships the build. */ async function runStudioDeployment(options, reporter) {
|
|
28
|
+
const { cliConfig, flags, output, sourceDir } = options;
|
|
29
|
+
const workDir = options.projectRoot.directory;
|
|
30
|
+
const isExternal = !!flags.external;
|
|
31
|
+
const isWorkbenchApp = getWorkbench(cliConfig) !== null;
|
|
28
32
|
const projectId = cliConfig.api?.projectId;
|
|
29
|
-
const
|
|
30
|
-
const isAutoUpdating =
|
|
33
|
+
const dryRun = !!flags['dry-run'];
|
|
34
|
+
const isAutoUpdating = checkAutoUpdates(reporter, {
|
|
31
35
|
cliConfig,
|
|
32
|
-
flags
|
|
33
|
-
output
|
|
36
|
+
flags
|
|
34
37
|
});
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
if (isExternal && isWorkbenchApp) {
|
|
39
|
+
// A federated app deploys through Sanity's build/hosting pipeline, which
|
|
40
|
+
// --external skips.
|
|
41
|
+
reporter.report({
|
|
42
|
+
exitCode: exitCodes.USAGE_ERROR,
|
|
43
|
+
message: 'Deploying a federated application to an external host is not yet supported',
|
|
44
|
+
solution: 'Remove the --external flag to deploy to Sanity hosting',
|
|
45
|
+
status: 'fail'
|
|
43
46
|
});
|
|
44
|
-
return;
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
isExternal,
|
|
50
|
-
output,
|
|
51
|
-
studioHost: cliConfig.studioHost
|
|
48
|
+
const version = await checkPackageVersion(reporter, {
|
|
49
|
+
moduleName: 'sanity',
|
|
50
|
+
workDir
|
|
52
51
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
let userApplication = await findUserApplicationForStudio({
|
|
68
|
-
appHost,
|
|
69
|
-
appId,
|
|
70
|
-
output,
|
|
71
|
-
projectId,
|
|
72
|
-
unattended: !!flags.yes,
|
|
73
|
-
urlType
|
|
74
|
-
});
|
|
75
|
-
if (!userApplication) {
|
|
76
|
-
if (flags.yes) {
|
|
77
|
-
const flagHint = isExternal ? 'Use --url to specify the external studio URL' : 'Use --url to specify the studio hostname';
|
|
78
|
-
output.error(`Cannot prompt for ${isExternal ? 'external studio URL' : 'studio hostname'} in unattended mode. ${flagHint}.`, {
|
|
79
|
-
exit: exitCodes.USAGE_ERROR
|
|
80
|
-
});
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
if (isExternal) {
|
|
84
|
-
output.log('Your project has not been registered with an external studio URL.');
|
|
85
|
-
output.log('Please enter the full URL where your studio is hosted.');
|
|
86
|
-
} else {
|
|
87
|
-
output.log('Your project has not been assigned a studio hostname.');
|
|
88
|
-
output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
|
|
89
|
-
output.log('you will need one. Please enter the subdomain you want to use.');
|
|
90
|
-
}
|
|
91
|
-
userApplication = await createStudioUserApplication({
|
|
92
|
-
projectId,
|
|
93
|
-
urlType
|
|
94
|
-
});
|
|
95
|
-
deployDebug('Created user application', userApplication);
|
|
96
|
-
}
|
|
97
|
-
deployDebug('Found user application', userApplication);
|
|
98
|
-
// Always build the project, unless --no-build is passed or --external is passed
|
|
99
|
-
const shouldBuild = flags.build && !isExternal;
|
|
100
|
-
if (shouldBuild) {
|
|
101
|
-
deployDebug(`Building studio`);
|
|
102
|
-
await buildStudio({
|
|
52
|
+
reporter.report(projectId ? {
|
|
53
|
+
message: `Project: ${projectId}`,
|
|
54
|
+
status: 'pass'
|
|
55
|
+
} : {
|
|
56
|
+
message: NO_PROJECT_ID,
|
|
57
|
+
solution: 'Add `api.projectId` to sanity.cli.ts',
|
|
58
|
+
status: 'fail'
|
|
59
|
+
});
|
|
60
|
+
const application = await resolveStudioApplication(options, {
|
|
61
|
+
dryRun,
|
|
62
|
+
reporter
|
|
63
|
+
});
|
|
64
|
+
await checkBuild(reporter, {
|
|
65
|
+
build: ()=>buildStudio({
|
|
103
66
|
autoUpdatesEnabled: isAutoUpdating,
|
|
104
67
|
calledFromDeploy: true,
|
|
105
68
|
cliConfig,
|
|
@@ -107,136 +70,164 @@ export async function deployStudio(options) {
|
|
|
107
70
|
outDir: sourceDir,
|
|
108
71
|
output,
|
|
109
72
|
workDir
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
73
|
+
}),
|
|
74
|
+
skipReason: studioBuildSkipReason({
|
|
75
|
+
build: flags.build,
|
|
76
|
+
isExternal
|
|
77
|
+
}),
|
|
78
|
+
successMessage: 'Studio built'
|
|
79
|
+
});
|
|
80
|
+
if (!isExternal) {
|
|
81
|
+
await verifyOutputDir({
|
|
82
|
+
isWorkbenchApp,
|
|
83
|
+
reporter,
|
|
84
|
+
sourceDir
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// Dry run stops here — everything below mutates.
|
|
88
|
+
if (dryRun) return;
|
|
89
|
+
// A real deploy has already exited if anything failed; landing here without a
|
|
90
|
+
// resolved application or version means the deploy target was never resolved.
|
|
91
|
+
if (!application || !version) return;
|
|
92
|
+
const studioManifest = await uploadStudioSchema(options, {
|
|
93
|
+
isExternal
|
|
94
|
+
});
|
|
95
|
+
await shipStudioDeployment({
|
|
96
|
+
application,
|
|
97
|
+
isAutoUpdating,
|
|
98
|
+
isExternal,
|
|
99
|
+
options,
|
|
100
|
+
studioManifest,
|
|
101
|
+
version
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Finds the application a real deploy targets, registering a studio host when
|
|
106
|
+
* none is configured. A dry run resolves and reports the target read-only instead.
|
|
107
|
+
*/ async function resolveStudioApplication(options, { dryRun, reporter }) {
|
|
108
|
+
const { cliConfig, flags, output } = options;
|
|
109
|
+
const isExternal = !!flags.external;
|
|
110
|
+
// Sets the title on a newly registered studio; blank falls back to undefined
|
|
111
|
+
const title = flags.title?.trim() || undefined;
|
|
112
|
+
if (dryRun) {
|
|
113
|
+
await checkStudioTarget(reporter, {
|
|
114
|
+
appId: getAppId(cliConfig),
|
|
115
|
+
isExternal,
|
|
116
|
+
projectId: cliConfig.api?.projectId,
|
|
117
|
+
studioHost: cliConfig.studioHost,
|
|
118
|
+
title,
|
|
119
|
+
urlFlag: flags.url
|
|
120
|
+
});
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const projectId = cliConfig.api?.projectId ?? '';
|
|
124
|
+
let application = await findUserApplicationForStudio({
|
|
125
|
+
appId: getAppId(cliConfig),
|
|
126
|
+
isExternal,
|
|
127
|
+
output,
|
|
128
|
+
projectId,
|
|
129
|
+
studioHost: cliConfig.studioHost,
|
|
130
|
+
title,
|
|
131
|
+
unattended: !!flags.yes,
|
|
132
|
+
urlFlag: flags.url
|
|
133
|
+
});
|
|
134
|
+
if (!application) {
|
|
135
|
+
if (isExternal) {
|
|
136
|
+
output.log('Your project has not been registered with an external studio URL.');
|
|
137
|
+
output.log('Please enter the full URL where your studio is hosted.');
|
|
138
|
+
} else {
|
|
139
|
+
output.log('Your project has not been assigned a studio hostname.');
|
|
140
|
+
output.log('To deploy your Sanity Studio to our hosted sanity.studio service,');
|
|
141
|
+
output.log('you will need one. Please enter the subdomain you want to use.');
|
|
133
142
|
}
|
|
134
|
-
|
|
135
|
-
|
|
143
|
+
application = await createStudioUserApplication({
|
|
144
|
+
projectId,
|
|
145
|
+
title,
|
|
146
|
+
urlType: isExternal ? 'external' : 'internal'
|
|
147
|
+
});
|
|
148
|
+
deployDebug('Created user application', application);
|
|
149
|
+
}
|
|
150
|
+
deployDebug('Found user application', application);
|
|
151
|
+
return application;
|
|
152
|
+
}
|
|
153
|
+
/** Extracts the studio schema and manifest and uploads them to the schema store. */ async function uploadStudioSchema(options, { isExternal }) {
|
|
154
|
+
const { cliConfig, flags, output, projectRoot, sourceDir } = options;
|
|
155
|
+
let studioManifest = null;
|
|
156
|
+
try {
|
|
157
|
+
studioManifest = await deployStudioSchemasAndManifests({
|
|
158
|
+
configPath: projectRoot.path,
|
|
159
|
+
isExternal,
|
|
160
|
+
outPath: `${sourceDir}/static`,
|
|
161
|
+
projectId: cliConfig.api?.projectId ?? '',
|
|
162
|
+
schemaRequired: flags['schema-required'],
|
|
163
|
+
verbose: flags.verbose,
|
|
164
|
+
workDir: projectRoot.directory
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
deployDebug('Error deploying studio schemas and manifests', error);
|
|
168
|
+
if (error instanceof SchemaExtractionError) {
|
|
169
|
+
output.error(formatSchemaValidation(error.validation || []), {
|
|
136
170
|
exit: 1
|
|
137
171
|
});
|
|
138
172
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
output.error(`Error deploying studio schemas and manifests: ${error}`, {
|
|
174
|
+
exit: 1
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
if (!studioManifest) {
|
|
178
|
+
output.error('Failed to generate studio manifest. Please check your schemas and manifests.', {
|
|
179
|
+
exit: 1
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return studioManifest;
|
|
183
|
+
}
|
|
184
|
+
async function shipStudioDeployment({ application, isAutoUpdating, isExternal, options, studioManifest, version }) {
|
|
185
|
+
const { cliConfig, output, sourceDir } = options;
|
|
186
|
+
let tarball;
|
|
187
|
+
if (!isExternal) {
|
|
188
|
+
tarball = pack(dirname(sourceDir), {
|
|
189
|
+
entries: [
|
|
190
|
+
basename(sourceDir)
|
|
191
|
+
]
|
|
192
|
+
}).pipe(createGzip());
|
|
193
|
+
}
|
|
194
|
+
const spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start();
|
|
195
|
+
let location;
|
|
196
|
+
try {
|
|
197
|
+
;
|
|
198
|
+
({ location } = await createDeployment({
|
|
199
|
+
applicationId: application.id,
|
|
165
200
|
isApp: false,
|
|
166
201
|
isAutoUpdating,
|
|
167
202
|
manifest: studioManifest,
|
|
168
|
-
projectId,
|
|
203
|
+
projectId: cliConfig.api?.projectId,
|
|
169
204
|
tarball,
|
|
170
|
-
version
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
205
|
+
version
|
|
206
|
+
}));
|
|
207
|
+
} catch (error) {
|
|
208
|
+
spin.fail();
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
spin.succeed();
|
|
212
|
+
output.log(isExternal ? `\nSuccess! Studio registered` : `\nSuccess! Studio deployed to ${styleText('cyan', location)}`);
|
|
213
|
+
if (getAppId(cliConfig)) return;
|
|
214
|
+
const example = `Example:
|
|
180
215
|
export default defineCliConfig({
|
|
181
216
|
//…
|
|
182
217
|
deployment: {
|
|
183
|
-
${styleText('cyan', `appId: '${
|
|
218
|
+
${styleText('cyan', `appId: '${application.id}'`)},
|
|
184
219
|
},
|
|
185
220
|
//…
|
|
186
221
|
})`;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
} catch (error) {
|
|
193
|
-
// if the error is a CLIError, we can just output the message and preserve its exit code
|
|
194
|
-
if (error instanceof CLIError) {
|
|
195
|
-
output.error(error.message, {
|
|
196
|
-
exit: error.oclif?.exit ?? exitCodes.RUNTIME_ERROR
|
|
197
|
-
});
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
spin.fail();
|
|
201
|
-
deployDebug('Error deploying studio', error);
|
|
202
|
-
output.error(`Error deploying studio: ${error}`, {
|
|
203
|
-
exit: 1
|
|
204
|
-
});
|
|
205
|
-
}
|
|
222
|
+
output.log(`\nAdd ${styleText('cyan', `appId: '${application.id}'`)}`);
|
|
223
|
+
output.log(`to the \`deployment\` section in sanity.cli.js or sanity.cli.ts`);
|
|
224
|
+
output.log(`to avoid prompting for application id on next deploy.`);
|
|
225
|
+
output.log(`\n${example}`);
|
|
206
226
|
}
|
|
207
|
-
function
|
|
208
|
-
|
|
209
|
-
if (!
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
if (isExternal) {
|
|
213
|
-
const normalized = normalizeUrl(url);
|
|
214
|
-
const validation = validateUrl(normalized);
|
|
215
|
-
if (validation !== true) {
|
|
216
|
-
output.error(validation, {
|
|
217
|
-
exit: exitCodes.USAGE_ERROR
|
|
218
|
-
});
|
|
219
|
-
return undefined;
|
|
220
|
-
}
|
|
221
|
-
return normalized;
|
|
222
|
-
}
|
|
223
|
-
// For internal deploys, strip protocol prefix and .sanity.studio suffix if present
|
|
224
|
-
const hostname = url.replace(/^https?:\/\//i, '').replace(/\.sanity\.studio\/?$/i, '');
|
|
225
|
-
// If the result still looks like a URL (contains dots), the user likely meant --external
|
|
226
|
-
if (hostname.includes('.')) {
|
|
227
|
-
output.error(`"${hostname}" does not look like a sanity.studio hostname. Did you mean to use --external?`, {
|
|
228
|
-
exit: exitCodes.USAGE_ERROR
|
|
229
|
-
});
|
|
230
|
-
return undefined;
|
|
231
|
-
}
|
|
232
|
-
// Validate hostname characters (alphanumeric and hyphens only)
|
|
233
|
-
if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {
|
|
234
|
-
output.error(`Invalid studio hostname "${hostname}". Hostnames can only contain letters, numbers, and hyphens.`, {
|
|
235
|
-
exit: exitCodes.USAGE_ERROR
|
|
236
|
-
});
|
|
237
|
-
return undefined;
|
|
238
|
-
}
|
|
239
|
-
return hostname;
|
|
227
|
+
function studioBuildSkipReason({ build, isExternal }) {
|
|
228
|
+
if (isExternal) return 'Build skipped for externally hosted studios';
|
|
229
|
+
if (!build) return 'Build skipped (--no-build) — validating existing output directory';
|
|
230
|
+
return;
|
|
240
231
|
}
|
|
241
232
|
|
|
242
233
|
//# sourceMappingURL=deployStudio.js.map
|
|
@@ -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 {CLIError} from '@oclif/core/errors'\nimport {formatSchemaValidation, SchemaExtractionError} from '@sanity/cli-build/_internal/extract'\nimport {exitCodes, getLocalPackageVersion, type Output} 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} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {NO_PROJECT_ID} from '../../util/errorMessages.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {buildStudio} from '../build/buildStudio.js'\nimport {shouldAutoUpdate} from '../build/shouldAutoUpdate.js'\nimport {checkDir} from './checkDir.js'\nimport {createStudioUserApplication} from './createStudioUserApplication.js'\nimport {deployDebug} from './deployDebug.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplicationForStudio.js'\nimport {type DeployAppOptions} from './types.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nexport async function deployStudio(options: DeployAppOptions) {\n const {cliConfig, flags, output, projectRoot, sourceDir} = options\n const workbench = getWorkbench(cliConfig)\n\n const workDir = projectRoot.directory\n const configPath = projectRoot.path\n\n const appId = getAppId(cliConfig)\n const projectId = cliConfig.api?.projectId\n const installedSanityVersion = await getLocalPackageVersion('sanity', workDir)\n const isAutoUpdating = shouldAutoUpdate({cliConfig, flags, output})\n\n const isExternal = !!flags.external\n const urlType: 'external' | 'internal' = isExternal ? 'external' : 'internal'\n\n // Federated (`unstable_defineApp`) applications integrate through Sanity's\n // build and hosting pipeline, which `--external` skips. Fail before any\n // prompts or API calls.\n if (isExternal && workbench) {\n output.error(\n 'Deploying a federated application to an external host is not yet supported. ' +\n 'Remove the `--external` flag to deploy to Sanity hosting.',\n {exit: exitCodes.USAGE_ERROR},\n )\n return\n }\n\n // Resolve the app host from --url flag (takes precedence) or studioHost config\n const appHost = resolveAppHost({flags, isExternal, output, studioHost: cliConfig.studioHost})\n\n if (!installedSanityVersion) {\n output.error(`Failed to find installed sanity version`, {exit: 1})\n return\n }\n\n if (!projectId) {\n output.error(NO_PROJECT_ID, {exit: 1})\n return\n }\n\n let spin = spinner('Verifying local content')\n\n try {\n let userApplication = await findUserApplicationForStudio({\n appHost,\n appId,\n output,\n projectId,\n unattended: !!flags.yes,\n urlType,\n })\n\n if (!userApplication) {\n if (flags.yes) {\n const flagHint = isExternal\n ? 'Use --url to specify the external studio URL'\n : 'Use --url to specify the studio hostname'\n output.error(\n `Cannot prompt for ${isExternal ? 'external studio URL' : 'studio hostname'} in unattended mode. ${flagHint}.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return\n }\n\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 userApplication = await createStudioUserApplication({projectId, urlType})\n\n deployDebug('Created user application', userApplication)\n }\n\n deployDebug('Found user application', userApplication)\n\n // Always build the project, unless --no-build is passed or --external is passed\n const shouldBuild = flags.build && !isExternal\n if (shouldBuild) {\n deployDebug(`Building studio`)\n await buildStudio({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n })\n }\n\n let studioManifest: StudioManifest | null = null\n try {\n studioManifest = await deployStudioSchemasAndManifests({\n configPath,\n isExternal,\n outPath: `${sourceDir}/static`,\n projectId,\n schemaRequired: flags['schema-required'],\n verbose: flags.verbose,\n workDir,\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 let tarball: Gzip | undefined\n\n if (!isExternal) {\n // Ensure that the directory exists, is a directory and seems to have valid content\n spin = spin.start()\n try {\n await (workbench ? workbench.checkBuiltOutput(sourceDir) : checkDir(sourceDir))\n spin.succeed()\n } catch (err) {\n spin.fail()\n deployDebug('Error checking directory', err)\n output.error(getErrorMessage(err), {exit: 1})\n }\n\n // Create a tarball of the given directory\n const parentDir = dirname(sourceDir)\n const base = basename(sourceDir)\n tarball = pack(parentDir, {entries: [base]}).pipe(createGzip())\n }\n\n spin = spinner(isExternal ? 'Registering studio' : 'Deploying to sanity.studio').start()\n\n const {location} = await createDeployment({\n applicationId: userApplication.id,\n isApp: false,\n isAutoUpdating,\n manifest: studioManifest,\n projectId,\n tarball,\n version: installedSanityVersion,\n })\n\n spin.succeed()\n\n if (isExternal) {\n output.log(`\\nSuccess! Studio registered`)\n } else {\n output.log(`\\nSuccess! Studio deployed to ${styleText('cyan', location)}`)\n }\n\n if (!appId) {\n const example = `Example:\nexport default defineCliConfig({\n //…\n deployment: {\n ${styleText('cyan', `appId: '${userApplication.id}'`)},\n },\n //…\n})`\n output.log(`\\nAdd ${styleText('cyan', `appId: '${userApplication.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 } catch (error) {\n // if the error is a CLIError, we can just output the message and preserve its exit code\n if (error instanceof CLIError) {\n output.error(error.message, {exit: error.oclif?.exit ?? exitCodes.RUNTIME_ERROR})\n return\n }\n\n spin.fail()\n deployDebug('Error deploying studio', error)\n output.error(`Error deploying studio: ${error}`, {exit: 1})\n }\n}\n\nfunction resolveAppHost({\n flags,\n isExternal,\n output,\n studioHost,\n}: {\n flags: DeployAppOptions['flags']\n isExternal: boolean\n output: Output\n studioHost: string | undefined\n}): string | undefined {\n const url = flags.url\n if (!url) {\n return studioHost\n }\n\n if (isExternal) {\n const normalized = normalizeUrl(url)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n output.error(validation, {exit: exitCodes.USAGE_ERROR})\n return undefined\n }\n return normalized\n }\n\n // For internal deploys, strip protocol prefix and .sanity.studio suffix if present\n const hostname = url.replace(/^https?:\\/\\//i, '').replace(/\\.sanity\\.studio\\/?$/i, '')\n\n // If the result still looks like a URL (contains dots), the user likely meant --external\n if (hostname.includes('.')) {\n output.error(\n `\"${hostname}\" does not look like a sanity.studio hostname. Did you mean to use --external?`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return undefined\n }\n\n // Validate hostname characters (alphanumeric and hyphens only)\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(hostname)) {\n output.error(\n `Invalid studio hostname \"${hostname}\". Hostnames can only contain letters, numbers, and hyphens.`,\n {exit: exitCodes.USAGE_ERROR},\n )\n return undefined\n }\n\n return hostname\n}\n"],"names":["basename","dirname","styleText","createGzip","CLIError","formatSchemaValidation","SchemaExtractionError","exitCodes","getLocalPackageVersion","spinner","getWorkbench","pack","createDeployment","getAppId","NO_PROJECT_ID","getErrorMessage","buildStudio","shouldAutoUpdate","checkDir","createStudioUserApplication","deployDebug","deployStudioSchemasAndManifests","findUserApplicationForStudio","normalizeUrl","validateUrl","deployStudio","options","cliConfig","flags","output","projectRoot","sourceDir","workbench","workDir","directory","configPath","path","appId","projectId","api","installedSanityVersion","isAutoUpdating","isExternal","external","urlType","error","exit","USAGE_ERROR","appHost","resolveAppHost","studioHost","spin","userApplication","unattended","yes","flagHint","log","shouldBuild","build","autoUpdatesEnabled","calledFromDeploy","outDir","studioManifest","outPath","schemaRequired","verbose","validation","tarball","start","checkBuiltOutput","succeed","err","fail","parentDir","base","entries","pipe","location","applicationId","id","isApp","manifest","version","example","message","oclif","RUNTIME_ERROR","url","normalized","undefined","hostname","replace","includes","test"],"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 {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 {runDeploy} from './deployRunner.js'\nimport {deployStudioSchemasAndManifests} from './deployStudioSchemasAndManifests.js'\nimport {findUserApplicationForStudio} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\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<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: 'sanity',\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 await shipStudioDeployment({\n application,\n isAutoUpdating,\n isExternal,\n options,\n studioManifest,\n version,\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 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 } 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<void> {\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 output.log(\n isExternal\n ? `\\nSuccess! Studio registered`\n : `\\nSuccess! Studio deployed to ${styleText('cyan', location)}`,\n )\n\n if (getAppId(cliConfig)) return\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\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","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","shipStudioDeployment","title","trim","undefined","appId","studioHost","urlFlag","url","unattended","yes","log","urlType","configPath","path","outPath","schemaRequired","verbose","error","validation","exit","tarball","entries","pipe","spin","start","location","applicationId","id","isApp","manifest","fail","succeed","example"],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { join, relative, sep } from 'node:path';
|
|
3
|
+
import { styleText } from 'node:util';
|
|
4
|
+
import { logSymbols } from '@sanity/cli-core/ux';
|
|
5
|
+
/**
|
|
6
|
+
* Lists the files a deploy would pack from `sourceDir`, as paths relative to
|
|
7
|
+
* `fromDir`. A missing directory yields an empty list rather than throwing.
|
|
8
|
+
*/ export async function listDeploymentFiles(sourceDir, fromDir) {
|
|
9
|
+
const walk = async (dir)=>{
|
|
10
|
+
let entries;
|
|
11
|
+
try {
|
|
12
|
+
entries = await readdir(dir, {
|
|
13
|
+
withFileTypes: true
|
|
14
|
+
});
|
|
15
|
+
} catch {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const nested = await Promise.all(entries.map((entry)=>{
|
|
19
|
+
const full = join(dir, entry.name);
|
|
20
|
+
return entry.isDirectory() ? walk(full) : Promise.resolve([
|
|
21
|
+
full
|
|
22
|
+
]);
|
|
23
|
+
}));
|
|
24
|
+
return nested.flat();
|
|
25
|
+
};
|
|
26
|
+
const absolute = await walk(sourceDir);
|
|
27
|
+
const files = await Promise.all(absolute.map(async (file)=>({
|
|
28
|
+
// Deploy paths are POSIX-style regardless of the host OS (Windows gives `\`).
|
|
29
|
+
path: relative(fromDir, file).split(sep).join('/'),
|
|
30
|
+
size: (await stat(file)).size
|
|
31
|
+
})));
|
|
32
|
+
return files.toSorted((a, b)=>a.path.localeCompare(b.path));
|
|
33
|
+
}
|
|
34
|
+
export function renderDeploymentPlan(plan, output) {
|
|
35
|
+
const label = plan.type === 'coreApp' ? 'application' : 'studio';
|
|
36
|
+
const problems = plan.checks.filter((check)=>check.status === 'fail');
|
|
37
|
+
const warnings = plan.checks.filter((check)=>check.status === 'warn');
|
|
38
|
+
const totalBytes = plan.files.reduce((sum, file)=>sum + file.size, 0);
|
|
39
|
+
output.log('\nDry run — no changes made.\n');
|
|
40
|
+
// Only pass/skip here; problems and warnings render below with their fixes.
|
|
41
|
+
for (const check of plan.checks){
|
|
42
|
+
if (check.status === 'pass' || check.status === 'skip') {
|
|
43
|
+
output.log(` ${statusIcon(check.status)} ${check.message}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
output.log(problems.length === 0 ? styleText('green', `\nThis ${label} can be deployed.`) : styleText('red', `\nThis ${label} can't be deployed.`));
|
|
47
|
+
renderIssues(output, 'Problems to fix:', problems);
|
|
48
|
+
renderIssues(output, 'Warnings:', warnings);
|
|
49
|
+
// A blocked deploy uploads nothing, so only list files for a deployable plan.
|
|
50
|
+
if (problems.length === 0 && plan.files.length > 0) {
|
|
51
|
+
output.log(`\nFiles to deploy (${formatMB(totalBytes)}):`);
|
|
52
|
+
for (const file of plan.files){
|
|
53
|
+
output.log(` ${file.path} (${formatMB(file.size)})`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function renderIssues(output, title, checks) {
|
|
58
|
+
if (checks.length === 0) return;
|
|
59
|
+
output.log(`\n${title}`);
|
|
60
|
+
for (const check of checks){
|
|
61
|
+
const fix = check.solution ? `: ${check.solution}` : '';
|
|
62
|
+
output.log(` ${statusIcon(check.status)} ${check.message}${fix}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function formatMB(bytes) {
|
|
66
|
+
return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
|
|
67
|
+
}
|
|
68
|
+
function statusIcon(status) {
|
|
69
|
+
switch(status){
|
|
70
|
+
case 'fail':
|
|
71
|
+
{
|
|
72
|
+
return logSymbols.error;
|
|
73
|
+
}
|
|
74
|
+
case 'skip':
|
|
75
|
+
{
|
|
76
|
+
return logSymbols.info;
|
|
77
|
+
}
|
|
78
|
+
case 'warn':
|
|
79
|
+
{
|
|
80
|
+
return logSymbols.warning;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
{
|
|
84
|
+
return logSymbols.success;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
//# sourceMappingURL=deploymentPlan.js.map
|
|
@@ -0,0 +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 {type DeployCheck} 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 type: 'coreApp' | 'studio'\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 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 const totalBytes = plan.files.reduce((sum, file) => sum + file.size, 0)\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 problems.length === 0\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 (problems.length === 0 && plan.files.length > 0) {\n output.log(`\\nFiles to deploy (${formatMB(totalBytes)}):`)\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","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","renderDeploymentPlan","plan","output","label","type","problems","checks","filter","check","status","warnings","totalBytes","reduce","sum","log","statusIcon","message","length","renderIssues","formatMB","title","fix","solution","bytes","toFixed","error","info","warning","success"],"mappings":"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"}
|