@sanity/cli 7.7.0 → 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.
- package/README.md +17 -2
- package/dist/actions/auth/ensureAuthenticated.js +1 -1
- package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
- package/dist/actions/auth/login/validateToken.js +1 -1
- package/dist/actions/auth/login/validateToken.js.map +1 -1
- package/dist/actions/build/buildApp.js +2 -3
- package/dist/actions/build/buildApp.js.map +1 -1
- package/dist/actions/build/buildStudio.js +1 -2
- package/dist/actions/build/buildStudio.js.map +1 -1
- package/dist/actions/deploy/createUserApplication.js +8 -8
- package/dist/actions/deploy/createUserApplication.js.map +1 -1
- package/dist/actions/deploy/deployApp.js +121 -24
- package/dist/actions/deploy/deployApp.js.map +1 -1
- package/dist/actions/deploy/deployChecks.js +99 -68
- package/dist/actions/deploy/deployChecks.js.map +1 -1
- package/dist/actions/deploy/deployRunner.js +43 -24
- package/dist/actions/deploy/deployRunner.js.map +1 -1
- package/dist/actions/deploy/deployStudio.js +126 -23
- package/dist/actions/deploy/deployStudio.js.map +1 -1
- package/dist/actions/deploy/deploymentPlan.js +29 -30
- package/dist/actions/deploy/deploymentPlan.js.map +1 -1
- package/dist/actions/deploy/findUserApplication.js +39 -14
- package/dist/actions/deploy/findUserApplication.js.map +1 -1
- package/dist/actions/deploy/resolveDeployTarget.js +43 -0
- package/dist/actions/deploy/resolveDeployTarget.js.map +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js +1 -1
- package/dist/actions/dev/servers/getDevServerConfig.js.map +1 -1
- package/dist/actions/dev/servers/startStudioDevServer.js +1 -2
- package/dist/actions/dev/servers/startStudioDevServer.js.map +1 -1
- package/dist/actions/graphql/SchemaError.js.map +1 -1
- package/dist/actions/graphql/extractGraphQLAPIs.js +3 -1
- package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js +1 -1
- package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
- package/dist/actions/organizations/getOrganization.js +1 -1
- package/dist/actions/organizations/getOrganization.js.map +1 -1
- package/dist/actions/schema/validateAction.js +1 -1
- package/dist/actions/schema/validateAction.js.map +1 -1
- package/dist/actions/skills/setupSkills.js +1 -1
- package/dist/actions/skills/setupSkills.js.map +1 -1
- package/dist/actions/undeploy/adapters.js +101 -0
- package/dist/actions/undeploy/adapters.js.map +1 -0
- package/dist/actions/undeploy/runUndeploy.js +165 -0
- package/dist/actions/undeploy/runUndeploy.js.map +1 -0
- package/dist/actions/undeploy/undeployPlan.js +108 -0
- package/dist/actions/undeploy/undeployPlan.js.map +1 -0
- package/dist/commands/datasets/copy.js +2 -1
- package/dist/commands/datasets/copy.js.map +1 -1
- package/dist/commands/mcp/configure.js +1 -1
- package/dist/commands/mcp/configure.js.map +1 -1
- package/dist/commands/projects/create.js +18 -13
- package/dist/commands/projects/create.js.map +1 -1
- package/dist/commands/projects/list.js +2 -1
- package/dist/commands/projects/list.js.map +1 -1
- package/dist/commands/schemas/validate.js +3 -2
- package/dist/commands/schemas/validate.js.map +1 -1
- package/dist/commands/skills/install.js +1 -1
- package/dist/commands/skills/install.js.map +1 -1
- package/dist/commands/tokens/list.js +1 -1
- package/dist/commands/tokens/list.js.map +1 -1
- package/dist/commands/undeploy.js +36 -74
- package/dist/commands/undeploy.js.map +1 -1
- package/dist/util/checks.js +83 -0
- package/dist/util/checks.js.map +1 -0
- package/dist/util/toStderrOutput.js +13 -0
- package/dist/util/toStderrOutput.js.map +1 -0
- package/oclif.manifest.json +1298 -1271
- package/package.json +6 -7
- package/dist/actions/undeploy/getStudioOrAppUserApplication.js +0 -36
- package/dist/actions/undeploy/getStudioOrAppUserApplication.js.map +0 -1
- package/dist/util/compareDependencyVersions.js +0 -110
- package/dist/util/compareDependencyVersions.js.map +0 -1
- package/dist/util/getErrorMessage.js +0 -28
- package/dist/util/getErrorMessage.js.map +0 -1
package/README.md
CHANGED
|
@@ -3427,13 +3427,28 @@ Removes the deployed Sanity Studio/App from Sanity hosting
|
|
|
3427
3427
|
|
|
3428
3428
|
```
|
|
3429
3429
|
USAGE
|
|
3430
|
-
$ sanity undeploy [-y]
|
|
3430
|
+
$ sanity undeploy [--dry-run] [-j] [-y]
|
|
3431
3431
|
|
|
3432
3432
|
FLAGS
|
|
3433
|
-
-
|
|
3433
|
+
-j, --json Output the result as JSON
|
|
3434
|
+
-y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
|
|
3435
|
+
--dry-run Report what would be undeployed without deleting anything
|
|
3434
3436
|
|
|
3435
3437
|
DESCRIPTION
|
|
3436
3438
|
Removes the deployed Sanity Studio/App from Sanity hosting
|
|
3439
|
+
|
|
3440
|
+
EXAMPLES
|
|
3441
|
+
Undeploy the studio or application after confirming
|
|
3442
|
+
|
|
3443
|
+
$ sanity undeploy
|
|
3444
|
+
|
|
3445
|
+
Report what would be undeployed without deleting anything
|
|
3446
|
+
|
|
3447
|
+
$ sanity undeploy --dry-run
|
|
3448
|
+
|
|
3449
|
+
Undeploy without prompting and report the result as JSON
|
|
3450
|
+
|
|
3451
|
+
$ sanity undeploy --json --yes
|
|
3437
3452
|
```
|
|
3438
3453
|
|
|
3439
3454
|
## `sanity users invite [EMAIL]`
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getCliToken } from '@sanity/cli-core';
|
|
2
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
2
3
|
import { isHttpError } from '@sanity/client';
|
|
3
4
|
import { LoginError } from '../../errors/LoginError.js';
|
|
4
5
|
import { getCliUser } from '../../services/user.js';
|
|
5
|
-
import { getErrorMessage } from '../../util/getErrorMessage.js';
|
|
6
6
|
import { login } from './login/login.js';
|
|
7
7
|
/**
|
|
8
8
|
* Ensure the user is authenticated. Validates the current token via /users/me
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {LoginError} from '../../errors/LoginError.js'\nimport {getCliUser} from '../../services/user.js'\nimport {login} from './login/login.js'\n\n/**\n * Ensure the user is authenticated. Validates the current token via /users/me\n * and triggers interactive login if the token is missing or invalid.\n *\n * @returns The authenticated user.\n * @throws LoginError if interactive login fails or is cancelled.\n * @throws Error if a network/server error occurs during validation.\n * @internal\n */\nexport async function ensureAuthenticated(options: {\n output: Output\n telemetry: CLITelemetryStore\n}): Promise<SanityOrgUser> {\n const user = await validateSession()\n if (user) return user\n\n try {\n await login({output: options.output, telemetry: options.telemetry})\n } catch (cause) {\n throw new LoginError(getErrorMessage(cause), {cause})\n }\n\n try {\n return await getCliUser()\n } catch (cause) {\n if (isHttpError(cause) && (cause.statusCode === 401 || cause.statusCode === 403)) {\n throw new LoginError(\n `Login succeeded but failed to verify session: ${getErrorMessage(cause)}`,\n {cause},\n )\n }\n throw cause\n }\n}\n\n/**\n * Validate the current CLI auth token by calling /users/me.\n *\n * Returns null only for auth failures (401/403). Re-throws network errors,\n * timeouts, and server errors so callers can handle them appropriately\n * instead of triggering an unnecessary re-login prompt.\n *\n * @returns The authenticated user if valid, null if invalid/missing.\n * @internal\n */\nexport async function validateSession(): Promise<SanityOrgUser | null> {\n const token = await getCliToken()\n if (!token) return null\n\n try {\n return await getCliUser()\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n return null\n }\n throw error\n }\n}\n"],"names":["getCliToken","getErrorMessage","isHttpError","LoginError","getCliUser","login","ensureAuthenticated","options","user","validateSession","output","telemetry","cause","statusCode","token","error"],"mappings":"AAAA,SAEEA,WAAW,QAGN,mBAAkB;AACzB,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,KAAK,QAAO,mBAAkB;AAEtC;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBAAoBC,OAGzC;IACC,MAAMC,OAAO,MAAMC;IACnB,IAAID,MAAM,OAAOA;IAEjB,IAAI;QACF,MAAMH,MAAM;YAACK,QAAQH,QAAQG,MAAM;YAAEC,WAAWJ,QAAQI,SAAS;QAAA;IACnE,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIT,WAAWF,gBAAgBW,QAAQ;YAACA;QAAK;IACrD;IAEA,IAAI;QACF,OAAO,MAAMR;IACf,EAAE,OAAOQ,OAAO;QACd,IAAIV,YAAYU,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIV,WACR,CAAC,8CAA8C,EAAEF,gBAAgBW,QAAQ,EACzE;gBAACA;YAAK;QAEV;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeH;IACpB,MAAMK,QAAQ,MAAMd;IACpB,IAAI,CAACc,OAAO,OAAO;IAEnB,IAAI;QACF,OAAO,MAAMV;IACf,EAAE,OAAOW,OAAO;QACd,IAAIb,YAAYa,UAAWA,CAAAA,MAAMF,UAAU,KAAK,OAAOE,MAAMF,UAAU,KAAK,GAAE,GAAI;YAChF,OAAO;QACT;QACA,MAAME;IACR;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getGlobalCliClient } from '@sanity/cli-core';
|
|
2
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
2
3
|
import { isHttpError } from '@sanity/client';
|
|
3
4
|
import { USERS_API_VERSION } from '../../../services/user.js';
|
|
4
|
-
import { getErrorMessage } from '../../../util/getErrorMessage.js';
|
|
5
5
|
export async function validateToken(token) {
|
|
6
6
|
const trimmedToken = token.trim();
|
|
7
7
|
if (!trimmedToken) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/auth/login/validateToken.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/auth/login/validateToken.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {USERS_API_VERSION} from '../../../services/user.js'\n\nexport async function validateToken(token: string): Promise<string> {\n const trimmedToken = token.trim()\n\n if (!trimmedToken) {\n throw new Error(\n 'Token is required on standard input. Run `sanity login --with-token < token.txt`.',\n )\n }\n\n try {\n await getTokenUser(trimmedToken)\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n throw new Error('Token is invalid or expired. Check the token and try again.', {cause: error})\n }\n\n throw new Error(`Could not verify token: ${getErrorMessage(error)}`, {cause: error})\n }\n\n return trimmedToken\n}\n\nexport async function isSanityApiToken(token: string): Promise<boolean> {\n return isSanityApiTokenUser(await getTokenUser(token))\n}\n\nasync function getTokenUser(token: string): Promise<unknown> {\n const client = await getGlobalCliClient({\n apiVersion: USERS_API_VERSION,\n requireUser: true,\n token,\n })\n\n return client.users.getById('me')\n}\n\nfunction isSanityApiTokenUser(user: unknown): boolean {\n return (\n typeof user === 'object' &&\n user !== null &&\n 'provider' in user &&\n (user as {provider?: unknown}).provider === 'sanity-token'\n )\n}\n"],"names":["getGlobalCliClient","getErrorMessage","isHttpError","USERS_API_VERSION","validateToken","token","trimmedToken","trim","Error","getTokenUser","error","statusCode","cause","isSanityApiToken","isSanityApiTokenUser","client","apiVersion","requireUser","users","getById","user","provider"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AACnD,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,iBAAiB,QAAO,4BAA2B;AAE3D,OAAO,eAAeC,cAAcC,KAAa;IAC/C,MAAMC,eAAeD,MAAME,IAAI;IAE/B,IAAI,CAACD,cAAc;QACjB,MAAM,IAAIE,MACR;IAEJ;IAEA,IAAI;QACF,MAAMC,aAAaH;IACrB,EAAE,OAAOI,OAAO;QACd,IAAIR,YAAYQ,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIH,MAAM,+DAA+D;gBAACI,OAAOF;YAAK;QAC9F;QAEA,MAAM,IAAIF,MAAM,CAAC,wBAAwB,EAAEP,gBAAgBS,QAAQ,EAAE;YAACE,OAAOF;QAAK;IACpF;IAEA,OAAOJ;AACT;AAEA,OAAO,eAAeO,iBAAiBR,KAAa;IAClD,OAAOS,qBAAqB,MAAML,aAAaJ;AACjD;AAEA,eAAeI,aAAaJ,KAAa;IACvC,MAAMU,SAAS,MAAMf,mBAAmB;QACtCgB,YAAYb;QACZc,aAAa;QACbZ;IACF;IAEA,OAAOU,OAAOG,KAAK,CAACC,OAAO,CAAC;AAC9B;AAEA,SAASL,qBAAqBM,IAAa;IACzC,OACE,OAAOA,SAAS,YAChBA,SAAS,QACT,cAAcA,QACd,AAACA,KAA8BC,QAAQ,KAAK;AAEhD"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { buildApp as internalBuildApp } from '@sanity/cli-build/_internal/build';
|
|
1
|
+
import { compareDependencyVersions, buildApp as internalBuildApp } from '@sanity/cli-build/_internal/build';
|
|
2
2
|
import { resolveWorkbenchApp } from '@sanity/workbench-cli/build';
|
|
3
3
|
import { getAppId } from '../../util/appId.js';
|
|
4
|
-
import { compareDependencyVersions } from '../../util/compareDependencyVersions.js';
|
|
5
4
|
import { warnAboutMissingAppId } from '../../util/warnAboutMissingAppId.js';
|
|
6
5
|
import { determineBasePath } from './determineBasePath.js';
|
|
7
6
|
/**
|
|
@@ -36,7 +35,7 @@ import { determineBasePath } from './determineBasePath.js';
|
|
|
36
35
|
determineBasePath: ()=>determineBasePath(cliConfig, 'app', output),
|
|
37
36
|
entry: app?.entry,
|
|
38
37
|
exposes: workbench ? {
|
|
39
|
-
|
|
38
|
+
config: workbench.config,
|
|
40
39
|
services: workbench.services,
|
|
41
40
|
views: workbench.views
|
|
42
41
|
} : undefined,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/buildApp.ts"],"sourcesContent":["import {buildApp as internalBuildApp} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/buildApp.ts"],"sourcesContent":["import {\n compareDependencyVersions,\n buildApp as internalBuildApp,\n} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity app.\n *\n * @internal\n */\nexport async function buildApp(options: BuildOptions): Promise<void> {\n const {cliConfig, flags, outDir, output, workDir} = options\n\n const app = cliConfig && 'app' in cliConfig ? cliConfig.app : undefined\n // `views`/`services` live on the branded `unstable_defineApp` result, not the\n // legacy `app` config object — resolve the workbench capability to read them.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n await internalBuildApp({\n appId,\n appTitle: app?.title,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !options.calledFromDeploy) {\n warnAboutMissingAppId({appType: 'app', output})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'app', output),\n entry: app?.entry,\n exposes: workbench\n ? {\n config: workbench.config,\n services: workbench.services,\n views: workbench.views,\n }\n : undefined,\n isWorkbenchApp: !!workbench,\n minify: flags.minify,\n outDir,\n output,\n reactCompiler: cliConfig && 'reactCompiler' in cliConfig ? cliConfig.reactCompiler : undefined,\n schemaExtraction: cliConfig?.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: flags.yes,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["compareDependencyVersions","buildApp","internalBuildApp","resolveWorkbenchApp","getAppId","warnAboutMissingAppId","determineBasePath","options","cliConfig","flags","outDir","output","workDir","app","undefined","workbench","appId","appTitle","title","autoUpdatesEnabled","checkAppId","calledFromDeploy","appType","packages","entry","exposes","config","services","views","isWorkbenchApp","minify","reactCompiler","schemaExtraction","sourceMap","Boolean","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SACEA,yBAAyB,EACzBC,YAAYC,gBAAgB,QACvB,oCAAmC;AAC1C,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeL,SAASM,OAAqB;IAClD,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGL;IAEpD,MAAMM,MAAML,aAAa,SAASA,YAAYA,UAAUK,GAAG,GAAGC;IAC9D,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAMC,YAAYZ,oBAAoBK;IAEtC,MAAMQ,QAAQZ,SAASI;IAEvB,MAAMN,iBAAiB;QACrBc;QACAC,UAAUJ,KAAKK;QACfC,oBAAoBZ,QAAQY,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACJ,SAAS,CAACT,QAAQc,gBAAgB,EAAE;gBACvChB,sBAAsB;oBAACiB,SAAS;oBAAOX;gBAAM;YAC/C;QACF;QACAX,2BAA2B,CAACuB,WAAavB,0BAA0BuB,UAAUX,SAAS;gBAACI;YAAK;QAC5FV,mBAAmB,IAAMA,kBAAkBE,WAAW,OAAOG;QAC7Da,OAAOX,KAAKW;QACZC,SAASV,YACL;YACEW,QAAQX,UAAUW,MAAM;YACxBC,UAAUZ,UAAUY,QAAQ;YAC5BC,OAAOb,UAAUa,KAAK;QACxB,IACAd;QACJe,gBAAgB,CAAC,CAACd;QAClBe,QAAQrB,MAAMqB,MAAM;QACpBpB;QACAC;QACAoB,eAAevB,aAAa,mBAAmBA,YAAYA,UAAUuB,aAAa,GAAGjB;QACrFkB,kBAAkBxB,WAAWwB;QAC7BC,WAAWC,QAAQzB,KAAK,CAAC,cAAc;QACvC0B,OAAO1B,MAAM0B,KAAK;QAClBC,gBAAgB3B,MAAM4B,GAAG;QACzBC,MAAM9B,UAAU8B,IAAI;QACpBC,gBAAgBxB,WAAWyB;QAC3B5B;IACF;AACF"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { buildStudio as internalBuildStudio } from '@sanity/cli-build/_internal/build';
|
|
1
|
+
import { compareDependencyVersions, buildStudio as internalBuildStudio } from '@sanity/cli-build/_internal/build';
|
|
2
2
|
import { resolveWorkbenchApp } from '@sanity/workbench-cli/build';
|
|
3
3
|
import { getAppId } from '../../util/appId.js';
|
|
4
|
-
import { compareDependencyVersions } from '../../util/compareDependencyVersions.js';
|
|
5
4
|
import { determineIsApp } from '../../util/determineIsApp.js';
|
|
6
5
|
import { getPackageManagerChoice } from '../../util/packageManager/packageManagerChoice.js';
|
|
7
6
|
import { upgradePackages } from '../../util/packageManager/upgradePackages.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/buildStudio.ts"],"sourcesContent":["import {buildStudio as internalBuildStudio} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/buildStudio.ts"],"sourcesContent":["import {\n compareDependencyVersions,\n buildStudio as internalBuildStudio,\n} from '@sanity/cli-build/_internal/build'\nimport {resolveWorkbenchApp} from '@sanity/workbench-cli/build'\n\nimport {getAppId} from '../../util/appId.js'\nimport {determineIsApp} from '../../util/determineIsApp.js'\nimport {getPackageManagerChoice} from '../../util/packageManager/packageManagerChoice.js'\nimport {upgradePackages} from '../../util/packageManager/upgradePackages.js'\nimport {warnAboutMissingAppId} from '../../util/warnAboutMissingAppId.js'\nimport {determineBasePath} from './determineBasePath.js'\nimport {type BuildOptions} from './types.js'\n\n/**\n * Build the Sanity Studio.\n *\n * @internal\n */\nexport async function buildStudio(options: BuildOptions): Promise<void> {\n const {calledFromDeploy, cliConfig, flags, outDir, output, workDir} = options\n\n // `views`/`services` live on the branded `unstable_defineApp` result — resolve\n // the workbench capability so it's gated on the brand, like the app build.\n const workbench = resolveWorkbenchApp(cliConfig)\n\n const appId = getAppId(cliConfig)\n\n const upgradePkgs = async (options: {\n packages: [name: string, version: string][]\n }): Promise<void> => {\n await upgradePackages(\n {\n packageManager: (await getPackageManagerChoice(workDir, {interactive: false})).chosen,\n packages: options.packages,\n },\n {output, workDir},\n )\n }\n\n await internalBuildStudio({\n appId,\n autoUpdatesEnabled: options.autoUpdatesEnabled,\n checkAppId: () => {\n // Warn if auto updates enabled but no appId configured.\n // Skip when called from deploy, since deploy handles appId itself\n // (prompts the user and tells them to add it to config).\n if (!appId && !calledFromDeploy) {\n warnAboutMissingAppId({appType: 'studio', output, projectId: cliConfig?.api?.projectId})\n }\n },\n compareDependencyVersions: (packages) => compareDependencyVersions(packages, workDir, {appId}),\n determineBasePath: () => determineBasePath(cliConfig, 'studio', output),\n exposes: workbench ? {services: workbench.services, views: workbench.views} : undefined,\n isApp: determineIsApp(cliConfig),\n isWorkbenchApp: !!workbench,\n minify: Boolean(flags.minify),\n outDir,\n output,\n reactCompiler: cliConfig.reactCompiler,\n schemaExtraction: cliConfig.schemaExtraction,\n sourceMap: Boolean(flags['source-maps']),\n stats: flags.stats,\n unattendedMode: Boolean(flags.yes),\n upgradePackages: upgradePkgs,\n vite: cliConfig.vite,\n workbenchAppId: workbench?.name,\n workDir,\n })\n}\n"],"names":["compareDependencyVersions","buildStudio","internalBuildStudio","resolveWorkbenchApp","getAppId","determineIsApp","getPackageManagerChoice","upgradePackages","warnAboutMissingAppId","determineBasePath","options","calledFromDeploy","cliConfig","flags","outDir","output","workDir","workbench","appId","upgradePkgs","packageManager","interactive","chosen","packages","autoUpdatesEnabled","checkAppId","appType","projectId","api","exposes","services","views","undefined","isApp","isWorkbenchApp","minify","Boolean","reactCompiler","schemaExtraction","sourceMap","stats","unattendedMode","yes","vite","workbenchAppId","name"],"mappings":"AAAA,SACEA,yBAAyB,EACzBC,eAAeC,mBAAmB,QAC7B,oCAAmC;AAC1C,SAAQC,mBAAmB,QAAO,8BAA6B;AAE/D,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,cAAc,QAAO,+BAA8B;AAC3D,SAAQC,uBAAuB,QAAO,oDAAmD;AACzF,SAAQC,eAAe,QAAO,+CAA8C;AAC5E,SAAQC,qBAAqB,QAAO,sCAAqC;AACzE,SAAQC,iBAAiB,QAAO,yBAAwB;AAGxD;;;;CAIC,GACD,OAAO,eAAeR,YAAYS,OAAqB;IACrD,MAAM,EAACC,gBAAgB,EAAEC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAC,GAAGN;IAEtE,+EAA+E;IAC/E,2EAA2E;IAC3E,MAAMO,YAAYd,oBAAoBS;IAEtC,MAAMM,QAAQd,SAASQ;IAEvB,MAAMO,cAAc,OAAOT;QAGzB,MAAMH,gBACJ;YACEa,gBAAgB,AAAC,CAAA,MAAMd,wBAAwBU,SAAS;gBAACK,aAAa;YAAK,EAAC,EAAGC,MAAM;YACrFC,UAAUb,QAAQa,QAAQ;QAC5B,GACA;YAACR;YAAQC;QAAO;IAEpB;IAEA,MAAMd,oBAAoB;QACxBgB;QACAM,oBAAoBd,QAAQc,kBAAkB;QAC9CC,YAAY;YACV,wDAAwD;YACxD,kEAAkE;YAClE,yDAAyD;YACzD,IAAI,CAACP,SAAS,CAACP,kBAAkB;gBAC/BH,sBAAsB;oBAACkB,SAAS;oBAAUX;oBAAQY,WAAWf,WAAWgB,KAAKD;gBAAS;YACxF;QACF;QACA3B,2BAA2B,CAACuB,WAAavB,0BAA0BuB,UAAUP,SAAS;gBAACE;YAAK;QAC5FT,mBAAmB,IAAMA,kBAAkBG,WAAW,UAAUG;QAChEc,SAASZ,YAAY;YAACa,UAAUb,UAAUa,QAAQ;YAAEC,OAAOd,UAAUc,KAAK;QAAA,IAAIC;QAC9EC,OAAO5B,eAAeO;QACtBsB,gBAAgB,CAAC,CAACjB;QAClBkB,QAAQC,QAAQvB,MAAMsB,MAAM;QAC5BrB;QACAC;QACAsB,eAAezB,UAAUyB,aAAa;QACtCC,kBAAkB1B,UAAU0B,gBAAgB;QAC5CC,WAAWH,QAAQvB,KAAK,CAAC,cAAc;QACvC2B,OAAO3B,MAAM2B,KAAK;QAClBC,gBAAgBL,QAAQvB,MAAM6B,GAAG;QACjCnC,iBAAiBY;QACjBwB,MAAM/B,UAAU+B,IAAI;QACpBC,gBAAgB3B,WAAW4B;QAC3B7B;IACF;AACF"}
|
|
@@ -5,6 +5,13 @@ import { createUserApplication as createUserApplicationRequest } from '../../ser
|
|
|
5
5
|
import { NO_ORGANIZATION_ID } from '../../util/errorMessages.js';
|
|
6
6
|
import { deployDebug } from './deployDebug.js';
|
|
7
7
|
import { normalizeUrl, validateUrl } from './urlUtils.js';
|
|
8
|
+
const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
|
|
9
|
+
// appHosts have some restrictions (no uppercase, must start with a letter)
|
|
10
|
+
export function generateAppSlug() {
|
|
11
|
+
const firstChar = customAlphabet(LETTERS, 1)();
|
|
12
|
+
const rest = customAlphabet(`${LETTERS}0123456789`, 11)();
|
|
13
|
+
return `${firstChar}${rest}`;
|
|
14
|
+
}
|
|
8
15
|
// TODO: replace with `Promise.withResolvers()` once it lands in node 22
|
|
9
16
|
function promiseWithResolvers() {
|
|
10
17
|
let resolve;
|
|
@@ -81,16 +88,9 @@ export async function createUserApplication(organizationId, title) {
|
|
|
81
88
|
});
|
|
82
89
|
return tryCreateApp(resolvedTitle, organizationId);
|
|
83
90
|
}
|
|
84
|
-
// appHosts have some restrictions (no uppercase, must start with a letter)
|
|
85
|
-
const generateId = ()=>{
|
|
86
|
-
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
87
|
-
const firstChar = customAlphabet(letters, 1)();
|
|
88
|
-
const rest = customAlphabet('abcdefghijklmnopqrstuvwxyz0123456789', 11)();
|
|
89
|
-
return `${firstChar}${rest}`;
|
|
90
|
-
};
|
|
91
91
|
const tryCreateApp = async (title, organizationId)=>{
|
|
92
92
|
// we will likely prepend this with an org ID or other parameter in the future
|
|
93
|
-
const appHost =
|
|
93
|
+
const appHost = generateAppSlug();
|
|
94
94
|
const spin = spinner('Creating application').start();
|
|
95
95
|
try {
|
|
96
96
|
const response = await createUserApplicationRequest({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId)\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nconst LETTERS = 'abcdefghijklmnopqrstuvwxyz'\n\n// appHosts have some restrictions (no uppercase, must start with a letter)\nexport function generateAppSlug(): string {\n const firstChar = customAlphabet(LETTERS, 1)()\n const rest = customAlphabet(`${LETTERS}0123456789`, 11)()\n return `${firstChar}${rest}`\n}\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId)\n}\n\nconst tryCreateApp = async (title: string, organizationId: string) => {\n // we will likely prepend this with an org ID or other parameter in the future\n const appHost = generateAppSlug()\n\n const spin = spinner('Creating application').start()\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'coreApp',\n body: {appHost, title, type: 'coreApp', urlType: 'internal'},\n organizationId,\n })\n\n spin.succeed()\n return response\n } catch (e) {\n // if the name is taken, generate a new one and try again\n if ([402, 409].includes(e?.statusCode)) {\n deployDebug('App host taken, retrying with new host')\n return tryCreateApp(title, organizationId)\n }\n\n spin.fail()\n\n deployDebug('Error creating core application', e)\n throw e\n }\n}\n"],"names":["CLIError","input","spinner","customAlphabet","createUserApplication","createUserApplicationRequest","NO_ORGANIZATION_ID","deployDebug","normalizeUrl","validateUrl","LETTERS","generateAppSlug","firstChar","rest","promiseWithResolvers","resolve","reject","promise","Promise","res","rej","createStudioUserApplication","options","projectId","title","urlType","isExternal","message","validate","inp","appHost","normalized","validation","replace","response","appType","body","type","e","includes","statusCode","exit","organizationId","Error","resolvedTitle","value","length","tryCreateApp","spin","start","succeed","fail"],"mappings":"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"}
|
|
@@ -2,19 +2,19 @@ import { basename, dirname } from 'node:path';
|
|
|
2
2
|
import { styleText } from 'node:util';
|
|
3
3
|
import { createGzip } from 'node:zlib';
|
|
4
4
|
import { exitCodes } from '@sanity/cli-core';
|
|
5
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
5
6
|
import { spinner } from '@sanity/cli-core/ux';
|
|
6
|
-
import {
|
|
7
|
+
import { buildExposes, deployConfig, deployCoreApp as deployWorkbenchCoreApp, getWorkbench, resolveInstallationId, summarizeConfig } from '@sanity/workbench-cli/deploy';
|
|
7
8
|
import { pack } from 'tar-fs';
|
|
8
9
|
import { createDeployment, updateUserApplication } from '../../services/userApplications.js';
|
|
9
10
|
import { getAppId } from '../../util/appId.js';
|
|
10
11
|
import { EXTERNAL_APP_NOT_SUPPORTED, NO_ORGANIZATION_ID } from '../../util/errorMessages.js';
|
|
11
|
-
import { getErrorMessage } from '../../util/getErrorMessage.js';
|
|
12
12
|
import { buildApp } from '../build/buildApp.js';
|
|
13
13
|
import { extractCoreAppManifest, resolveTitleUpdate } from '../manifest/extractCoreAppManifest.js';
|
|
14
|
-
import { createUserApplication } from './createUserApplication.js';
|
|
14
|
+
import { createUserApplication, generateAppSlug } from './createUserApplication.js';
|
|
15
15
|
import { checkAppId, checkAppTarget, checkAutoUpdates, checkBuild, checkPackageVersion, verifyOutputDir } from './deployChecks.js';
|
|
16
16
|
import { deployDebug } from './deployDebug.js';
|
|
17
|
-
import { listDeploymentFiles } from './deploymentPlan.js';
|
|
17
|
+
import { listDeploymentFiles, reportExposes } from './deploymentPlan.js';
|
|
18
18
|
import { runDeploy } from './deployRunner.js';
|
|
19
19
|
import { findUserApplication } from './findUserApplication.js';
|
|
20
20
|
import { getCoreAppUrl } from './urlUtils.js';
|
|
@@ -30,12 +30,14 @@ export function deployApp(options) {
|
|
|
30
30
|
const { cliConfig, flags, output, sourceDir } = options;
|
|
31
31
|
const workDir = options.projectRoot.directory;
|
|
32
32
|
const organizationId = cliConfig.app?.organizationId;
|
|
33
|
+
const appId = getAppId(cliConfig);
|
|
33
34
|
const workbench = getWorkbench(cliConfig);
|
|
34
35
|
const dryRun = !!flags['dry-run'];
|
|
35
36
|
// A singleton (the Media Library) persists its config instead of hosting an
|
|
36
|
-
// application; anything
|
|
37
|
-
const
|
|
37
|
+
// application; anything that exposes a view or service still ships one.
|
|
38
|
+
const deploySingletonConfig = workbench?.deploySingletonConfig ?? false;
|
|
38
39
|
const deployApplication = !workbench || workbench.hasInterfaces;
|
|
40
|
+
const appTitle = workbench ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name : '';
|
|
39
41
|
// A federated app with no entry, view or service would ship a remote with
|
|
40
42
|
// nothing to load — reported first so it fails before any prompt or API call.
|
|
41
43
|
if (workbench) {
|
|
@@ -62,7 +64,7 @@ export function deployApp(options) {
|
|
|
62
64
|
moduleName: APP_PACKAGE,
|
|
63
65
|
workDir
|
|
64
66
|
});
|
|
65
|
-
} else if (
|
|
67
|
+
} else if (deploySingletonConfig) {
|
|
66
68
|
version = await checkPackageVersion(reporter, {
|
|
67
69
|
moduleName: 'sanity',
|
|
68
70
|
workDir
|
|
@@ -80,17 +82,27 @@ export function deployApp(options) {
|
|
|
80
82
|
cliConfig
|
|
81
83
|
});
|
|
82
84
|
let application = null;
|
|
85
|
+
let appCreated = false;
|
|
83
86
|
if (flags.external) {
|
|
84
87
|
reporter.report({
|
|
85
88
|
message: EXTERNAL_APP_NOT_SUPPORTED,
|
|
86
89
|
solution: 'Remove the --external flag — apps deploy to Sanity hosting',
|
|
87
90
|
status: 'fail'
|
|
88
91
|
});
|
|
92
|
+
} else if (deployApplication && workbench) {
|
|
93
|
+
// Both modes, so a bad appId fails before the build rather than at the POST.
|
|
94
|
+
await checkAppTarget(reporter, {
|
|
95
|
+
appId,
|
|
96
|
+
isWorkbenchApp: true,
|
|
97
|
+
slug: workbench.slug,
|
|
98
|
+
title: appTitle
|
|
99
|
+
});
|
|
89
100
|
} else if (deployApplication) {
|
|
90
|
-
|
|
101
|
+
;
|
|
102
|
+
({ application, created: appCreated } = await resolveAppApplication(options, {
|
|
91
103
|
dryRun,
|
|
92
104
|
reporter
|
|
93
|
-
});
|
|
105
|
+
}));
|
|
94
106
|
}
|
|
95
107
|
await checkBuild(reporter, {
|
|
96
108
|
build: ()=>buildApp({
|
|
@@ -126,14 +138,17 @@ export function deployApp(options) {
|
|
|
126
138
|
// Resolve the installation in both modes so the report — dry-run and real —
|
|
127
139
|
// shows whether the config is deployable; a missing one fails the deploy here.
|
|
128
140
|
let installationId;
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
let config;
|
|
142
|
+
const configAppType = workbench?.config?.appType;
|
|
143
|
+
if (deploySingletonConfig && organizationId && workbench?.config && configAppType) {
|
|
131
144
|
installationId = await resolveInstallationId({
|
|
132
145
|
appType: configAppType,
|
|
133
146
|
organizationId
|
|
134
147
|
});
|
|
148
|
+
config = summarizeConfig(workbench.config);
|
|
135
149
|
reporter.report(installationId ? {
|
|
136
|
-
|
|
150
|
+
config,
|
|
151
|
+
message: config,
|
|
137
152
|
status: 'pass'
|
|
138
153
|
} : {
|
|
139
154
|
exitCode: exitCodes.USAGE_ERROR,
|
|
@@ -142,10 +157,20 @@ export function deployApp(options) {
|
|
|
142
157
|
status: 'fail'
|
|
143
158
|
});
|
|
144
159
|
}
|
|
160
|
+
// Report the exposes deploying with the application, both modes.
|
|
161
|
+
const exposes = deployApplication && workbench ? reportExposes(reporter, workbench) : [];
|
|
162
|
+
// Surface the app's explicit singleton flag when set, both modes.
|
|
163
|
+
if (deployApplication && workbench?.isSingleton !== undefined) {
|
|
164
|
+
reporter.report({
|
|
165
|
+
isSingleton: workbench.isSingleton,
|
|
166
|
+
message: `Singleton: ${workbench.isSingleton}`,
|
|
167
|
+
status: 'pass'
|
|
168
|
+
});
|
|
169
|
+
}
|
|
145
170
|
// Dry run stops here — everything below mutates.
|
|
146
171
|
if (dryRun) return;
|
|
147
172
|
if (installationId && version && configAppType) {
|
|
148
|
-
await
|
|
173
|
+
await deployConfig({
|
|
149
174
|
appType: configAppType,
|
|
150
175
|
installationId,
|
|
151
176
|
output,
|
|
@@ -153,21 +178,75 @@ export function deployApp(options) {
|
|
|
153
178
|
version
|
|
154
179
|
});
|
|
155
180
|
}
|
|
156
|
-
// A config-only singleton ships no application, only its
|
|
181
|
+
// A config-only singleton ships no application, only its config.
|
|
157
182
|
if (!deployApplication) {
|
|
158
183
|
if (installationId && version) {
|
|
159
184
|
return {
|
|
160
185
|
applicationType: 'coreApp',
|
|
161
186
|
applicationVersion: version,
|
|
187
|
+
...config ? {
|
|
188
|
+
config
|
|
189
|
+
} : {},
|
|
162
190
|
installationId,
|
|
163
191
|
target: null
|
|
164
192
|
};
|
|
165
193
|
}
|
|
166
194
|
return;
|
|
167
195
|
}
|
|
196
|
+
// A real deploy already exited on a version-resolution failure; this narrows the type.
|
|
197
|
+
if (!version) return;
|
|
198
|
+
// Workbench apps deploy to Brett; plain coreApps use user-applications.
|
|
199
|
+
if (workbench && organizationId) {
|
|
200
|
+
const appId = getAppId(cliConfig);
|
|
201
|
+
const slug = workbench.slug ?? generateAppSlug();
|
|
202
|
+
const { applicationId } = await deployWorkbenchCoreApp({
|
|
203
|
+
appId,
|
|
204
|
+
interfaces: buildExposes(workbench, {
|
|
205
|
+
appName: workbench.name,
|
|
206
|
+
appTitle,
|
|
207
|
+
exposesAppView: workbench.entry !== undefined,
|
|
208
|
+
version
|
|
209
|
+
}),
|
|
210
|
+
isAutoUpdating,
|
|
211
|
+
isSingleton: workbench.isSingleton,
|
|
212
|
+
organizationId,
|
|
213
|
+
slug,
|
|
214
|
+
sourceDir,
|
|
215
|
+
title: appTitle,
|
|
216
|
+
version
|
|
217
|
+
});
|
|
218
|
+
logAppDeployed({
|
|
219
|
+
applicationId,
|
|
220
|
+
cliConfig,
|
|
221
|
+
created: !appId,
|
|
222
|
+
organizationId,
|
|
223
|
+
output,
|
|
224
|
+
title: appTitle
|
|
225
|
+
});
|
|
226
|
+
return {
|
|
227
|
+
applicationType: 'coreApp',
|
|
228
|
+
applicationVersion: version,
|
|
229
|
+
...exposes.length > 0 ? {
|
|
230
|
+
exposes
|
|
231
|
+
} : {},
|
|
232
|
+
...workbench.isSingleton === undefined ? {} : {
|
|
233
|
+
isSingleton: workbench.isSingleton
|
|
234
|
+
},
|
|
235
|
+
target: {
|
|
236
|
+
action: appId ? 'update' : 'create',
|
|
237
|
+
applicationId,
|
|
238
|
+
// A redeploy ignores the slug, so only a create reports the one it used.
|
|
239
|
+
...appId ? {} : {
|
|
240
|
+
slug
|
|
241
|
+
},
|
|
242
|
+
title: appTitle,
|
|
243
|
+
url: getCoreAppUrl(organizationId, applicationId)
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
168
247
|
// A real deploy has already exited if anything failed; landing here without a
|
|
169
|
-
// resolved application
|
|
170
|
-
if (!application
|
|
248
|
+
// resolved application means the deploy target was never resolved.
|
|
249
|
+
if (!application) return;
|
|
171
250
|
application = await syncApplicationTitle({
|
|
172
251
|
application,
|
|
173
252
|
manifest,
|
|
@@ -181,14 +260,18 @@ export function deployApp(options) {
|
|
|
181
260
|
version
|
|
182
261
|
});
|
|
183
262
|
logAppDeployed({
|
|
184
|
-
application,
|
|
263
|
+
applicationId: application.id,
|
|
185
264
|
cliConfig,
|
|
186
|
-
|
|
265
|
+
created: appCreated,
|
|
266
|
+
organizationId: application.organizationId,
|
|
267
|
+
output,
|
|
268
|
+
title: application.title
|
|
187
269
|
});
|
|
188
270
|
return {
|
|
189
271
|
applicationType: 'coreApp',
|
|
190
272
|
applicationVersion: version,
|
|
191
273
|
target: {
|
|
274
|
+
action: appCreated ? 'create' : 'update',
|
|
192
275
|
applicationId: application.id,
|
|
193
276
|
title: application.title ?? null,
|
|
194
277
|
url: getCoreAppUrl(application.organizationId, application.id)
|
|
@@ -209,7 +292,10 @@ export function deployApp(options) {
|
|
|
209
292
|
organizationId,
|
|
210
293
|
title
|
|
211
294
|
});
|
|
212
|
-
return
|
|
295
|
+
return {
|
|
296
|
+
application: null,
|
|
297
|
+
created: false
|
|
298
|
+
};
|
|
213
299
|
}
|
|
214
300
|
let application = await findUserApplication({
|
|
215
301
|
cliConfig,
|
|
@@ -223,8 +309,15 @@ export function deployApp(options) {
|
|
|
223
309
|
deployDebug('No user application found. Creating a new one');
|
|
224
310
|
application = await createUserApplication(organizationId, title);
|
|
225
311
|
deployDebug('User application created', application);
|
|
312
|
+
return {
|
|
313
|
+
application,
|
|
314
|
+
created: true
|
|
315
|
+
};
|
|
226
316
|
}
|
|
227
|
-
return
|
|
317
|
+
return {
|
|
318
|
+
application,
|
|
319
|
+
created: false
|
|
320
|
+
};
|
|
228
321
|
}
|
|
229
322
|
/** Syncs the application title from the manifest when it has changed. */ async function syncApplicationTitle({ application, manifest, output }) {
|
|
230
323
|
const titleUpdate = resolveTitleUpdate(manifest, application);
|
|
@@ -274,12 +367,16 @@ async function shipAppDeployment({ application, isAutoUpdating, manifest, source
|
|
|
274
367
|
}
|
|
275
368
|
spin.succeed();
|
|
276
369
|
}
|
|
277
|
-
export function logAppDeployed({
|
|
278
|
-
const url = getCoreAppUrl(
|
|
279
|
-
const named =
|
|
370
|
+
export function logAppDeployed({ applicationId, cliConfig, created, organizationId, output, title }) {
|
|
371
|
+
const url = getCoreAppUrl(organizationId, applicationId);
|
|
372
|
+
const named = title ? ` — "${title}"` : '';
|
|
280
373
|
output.log(`\nSuccess! Application deployed to ${styleText('cyan', url)}${named}`);
|
|
374
|
+
output.log(created ? 'Created a new application.' : 'Updated the existing application.');
|
|
281
375
|
if (getAppId(cliConfig)) return;
|
|
282
376
|
output.log(`\n════ ${styleText('bold', 'Next step:')} ════`);
|
|
377
|
+
if (created) {
|
|
378
|
+
output.log(styleText('yellow', '\nDeploying again without `deployment.appId` creates another new application.'));
|
|
379
|
+
}
|
|
283
380
|
output.log(styleText('bold', '\nAdd the deployment.appId to your sanity.cli.js or sanity.cli.ts file:'));
|
|
284
381
|
output.log(`
|
|
285
382
|
${styleText('dim', `app: {
|
|
@@ -289,7 +386,7 @@ ${styleText([
|
|
|
289
386
|
'bold',
|
|
290
387
|
'green'
|
|
291
388
|
], `deployment: {
|
|
292
|
-
appId: '${
|
|
389
|
+
appId: '${applicationId}',
|
|
293
390
|
}\n`)}`);
|
|
294
391
|
}
|
|
295
392
|
|