@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip} from 'node:zlib'\n\nimport {exitCodes} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n deployInstallationConfig,\n getWorkbench,\n resolveInstallationId,\n summarizeInstallationConfig,\n} from '@sanity/workbench-cli/deploy'\nimport {pack} from 'tar-fs'\n\nimport {\n createDeployment,\n updateUserApplication,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {EXTERNAL_APP_NOT_SUPPORTED, NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {buildApp} from '../build/buildApp.js'\nimport {extractCoreAppManifest, resolveTitleUpdate} from '../manifest/extractCoreAppManifest.js'\nimport {type CoreAppManifest} from '../manifest/types.js'\nimport {createUserApplication} from './createUserApplication.js'\nimport {\n checkAppId,\n checkAppTarget,\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n type CheckReporter,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {findUserApplication} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\nimport {getCoreAppUrl} from './urlUtils.js'\n\nconst APP_PACKAGE = '@sanity/sdk-react'\n\nexport function deployApp(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({projectRoot, sourceDir}) => listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runAppDeployment,\n type: 'coreApp',\n })\n}\n\n/** Validates the deploy, syncs the title from the manifest, and ships the build. */\nasync function runAppDeployment(\n options: DeployAppOptions,\n reporter: CheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const organizationId = cliConfig.app?.organizationId\n const workbench = getWorkbench(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A singleton (the Media Library) persists its config instead of hosting an\n // application; anything with interfaces still ships one.\n const deploySingletonInstallationConfig = workbench?.deploySingletonInstallationConfig ?? false\n const deployApplication = !workbench || workbench.hasInterfaces\n\n // A federated app with no entry, view or service would ship a remote with\n // nothing to load — reported first so it fails before any prompt or API call.\n if (workbench) {\n try {\n workbench.assertDeployable()\n } catch (err) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: getErrorMessage(err),\n solution: 'Declare at least one entry, view, or service in the app',\n status: 'fail',\n })\n }\n }\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n // An application ships the SDK runtime; a media-library config stamps its\n // `sanity` version instead.\n let version: string | null = null\n if (deployApplication) {\n version = await checkPackageVersion(reporter, {moduleName: APP_PACKAGE, workDir})\n } else if (deploySingletonInstallationConfig) {\n version = await checkPackageVersion(reporter, {moduleName: 'sanity', workDir})\n }\n\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n checkAppId(reporter, {cliConfig})\n\n let application: UserApplicationResolved | null = null\n if (flags.external) {\n reporter.report({\n message: EXTERNAL_APP_NOT_SUPPORTED,\n solution: 'Remove the --external flag — apps deploy to Sanity hosting',\n status: 'fail',\n })\n } else if (deployApplication) {\n application = await resolveAppApplication(options, {dryRun, reporter})\n }\n\n await checkBuild(reporter, {\n build: () =>\n buildApp({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: flags.build\n ? undefined\n : 'Build skipped (--no-build) — validating existing output directory',\n successMessage: 'App built',\n })\n\n await verifyOutputDir({isWorkbenchApp: workbench !== null, reporter, sourceDir})\n\n // Manifests aren't strictly essential, so a failure warns and continues\n let manifest: CoreAppManifest | undefined\n try {\n manifest = await extractCoreAppManifest({workDir})\n } catch (err) {\n deployDebug('Error extracting app manifest', err)\n reporter.report({\n message: `Error extracting app manifest: ${getErrorMessage(err)}`,\n status: 'warn',\n })\n }\n\n // Resolve the installation in both modes so the report — dry-run and real —\n // shows whether the config is deployable; a missing one fails the deploy here.\n let installationId: string | undefined\n const configAppType = workbench?.installationConfig?.appType\n if (\n deploySingletonInstallationConfig &&\n organizationId &&\n workbench?.installationConfig &&\n configAppType\n ) {\n installationId = await resolveInstallationId({appType: configAppType, organizationId})\n reporter.report(\n installationId\n ? {message: summarizeInstallationConfig(workbench.installationConfig), status: 'pass'}\n : {\n exitCode: exitCodes.USAGE_ERROR,\n message: `No active \"${configAppType}\" installation for organization \"${organizationId}\"`,\n solution: 'Install the Media Library for the organization before deploying its config',\n status: 'fail',\n },\n )\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n if (installationId && version && configAppType) {\n await deployInstallationConfig({\n appType: configAppType,\n installationId,\n output,\n sourceDir,\n version,\n })\n }\n\n // A config-only singleton ships no application, only its installation config.\n if (!deployApplication) {\n if (installationId && version) {\n return {applicationType: 'coreApp', applicationVersion: version, installationId, target: null}\n }\n return\n }\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 application = await syncApplicationTitle({application, manifest, output})\n\n await shipAppDeployment({application, isAutoUpdating, manifest, sourceDir, version})\n\n logAppDeployed({application, cliConfig, output})\n\n return {\n applicationType: 'coreApp',\n applicationVersion: version,\n target: {\n applicationId: application.id,\n title: application.title ?? null,\n url: getCoreAppUrl(application.organizationId, application.id),\n },\n }\n}\n\n/**\n * Finds the application a real deploy targets, creating one when none is\n * configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveAppApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: CheckReporter},\n): Promise<UserApplicationResolved | null> {\n const {cliConfig, flags, output} = options\n const organizationId = cliConfig.app?.organizationId ?? ''\n // Create name from --title or `app.title` config; blank falls back to the prompt\n const title = flags.title?.trim() || cliConfig.app?.title?.trim() || undefined\n\n if (dryRun) {\n await checkAppTarget(reporter, {appId: getAppId(cliConfig), organizationId, title})\n return null\n }\n\n let application = await findUserApplication({\n cliConfig,\n organizationId,\n output,\n title,\n unattended: !!flags.yes,\n })\n deployDebug('User application found', application)\n\n if (!application) {\n deployDebug('No user application found. Creating a new one')\n application = await createUserApplication(organizationId, title)\n deployDebug('User application created', application)\n }\n\n return application\n}\n\n/** Syncs the application title from the manifest when it has changed. */\nasync function syncApplicationTitle({\n application,\n manifest,\n output,\n}: {\n application: UserApplicationResolved\n manifest: CoreAppManifest | undefined\n output: DeployAppOptions['output']\n}): Promise<UserApplicationResolved> {\n const titleUpdate = resolveTitleUpdate(manifest, application)\n if (!titleUpdate) return application\n\n deployDebug('Updating application title from manifest', titleUpdate)\n output.log(\n titleUpdate.from\n ? `Updating title from \"${titleUpdate.from}\" to \"${titleUpdate.to}\"`\n : `Setting application title to \"${titleUpdate.to}\"`,\n )\n const spin = spinner('Updating application title').start()\n try {\n const updated = await updateUserApplication({\n applicationId: application.id,\n appType: 'coreApp',\n body: {title: titleUpdate.to},\n })\n spin.succeed()\n return updated\n } catch (err) {\n spin.fail()\n const message = getErrorMessage(err)\n deployDebug('Error updating application title', {message})\n output.warn(`Error updating application title: ${message}`)\n return application\n }\n}\n\nasync function shipAppDeployment({\n application,\n isAutoUpdating,\n manifest,\n sourceDir,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n manifest: CoreAppManifest | undefined\n sourceDir: string\n version: string\n}): Promise<void> {\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying...').start()\n try {\n await createDeployment({\n applicationId: application.id,\n isApp: true,\n isAutoUpdating,\n manifest,\n tarball,\n version,\n })\n } catch (error) {\n spin.clear()\n throw error\n }\n spin.succeed()\n}\n\nexport function logAppDeployed({\n application,\n cliConfig,\n output,\n}: {\n application: UserApplicationResolved\n cliConfig: DeployAppOptions['cliConfig']\n output: DeployAppOptions['output']\n}): void {\n const url = getCoreAppUrl(application.organizationId, application.id)\n const named = application.title ? ` — \"${application.title}\"` : ''\n output.log(`\\nSuccess! Application deployed to ${styleText('cyan', url)}${named}`)\n\n if (getAppId(cliConfig)) return\n\n output.log(`\\n════ ${styleText('bold', 'Next step:')} ════`)\n output.log(\n styleText('bold', '\\nAdd the deployment.appId to your sanity.cli.js or sanity.cli.ts file:'),\n )\n output.log(`\n${styleText(\n 'dim',\n `app: {\n // your application config here…\n}`,\n)},\n${styleText(\n ['bold', 'green'],\n `deployment: {\n appId: '${application.id}',\n}\\n`,\n)}`)\n}\n"],"names":["basename","dirname","styleText","createGzip","exitCodes","spinner","deployInstallationConfig","getWorkbench","resolveInstallationId","summarizeInstallationConfig","pack","createDeployment","updateUserApplication","getAppId","EXTERNAL_APP_NOT_SUPPORTED","NO_ORGANIZATION_ID","getErrorMessage","buildApp","extractCoreAppManifest","resolveTitleUpdate","createUserApplication","checkAppId","checkAppTarget","checkAutoUpdates","checkBuild","checkPackageVersion","verifyOutputDir","deployDebug","listDeploymentFiles","runDeploy","findUserApplication","getCoreAppUrl","APP_PACKAGE","deployApp","options","listFiles","projectRoot","sourceDir","directory","run","runAppDeployment","type","reporter","cliConfig","flags","output","workDir","organizationId","app","workbench","dryRun","deploySingletonInstallationConfig","deployApplication","hasInterfaces","assertDeployable","err","report","exitCode","USAGE_ERROR","message","solution","status","isAutoUpdating","version","moduleName","application","external","resolveAppApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","undefined","successMessage","isWorkbenchApp","manifest","installationId","configAppType","installationConfig","appType","applicationType","applicationVersion","target","syncApplicationTitle","shipAppDeployment","logAppDeployed","applicationId","id","title","url","trim","appId","unattended","yes","titleUpdate","log","from","to","spin","start","updated","body","succeed","fail","warn","tarball","entries","pipe","isApp","error","clear","named"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAC3C,SAAQC,SAAS,QAAO,YAAW;AACnC,SAAQC,UAAU,QAAO,YAAW;AAEpC,SAAQC,SAAS,QAAO,mBAAkB;AAC1C,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,SACEC,wBAAwB,EACxBC,YAAY,EACZC,qBAAqB,EACrBC,2BAA2B,QACtB,+BAA8B;AACrC,SAAQC,IAAI,QAAO,SAAQ;AAE3B,SACEC,gBAAgB,EAChBC,qBAAqB,QAGhB,qCAAoC;AAC3C,SAAQC,QAAQ,QAAO,sBAAqB;AAC5C,SAAQC,0BAA0B,EAAEC,kBAAkB,QAAO,8BAA6B;AAC1F,SAAQC,eAAe,QAAO,gCAA+B;AAC7D,SAAQC,QAAQ,QAAO,uBAAsB;AAC7C,SAAQC,sBAAsB,EAAEC,kBAAkB,QAAO,wCAAuC;AAEhG,SAAQC,qBAAqB,QAAO,6BAA4B;AAChE,SACEC,UAAU,EACVC,cAAc,EACdC,gBAAgB,EAChBC,UAAU,EACVC,mBAAmB,EAEnBC,eAAe,QACV,oBAAmB;AAC1B,SAAQC,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,mBAAmB,QAAO,sBAAqB;AACvD,SAA2BC,SAAS,QAAO,oBAAmB;AAC9D,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,SAAQC,aAAa,QAAO,gBAAe;AAE3C,MAAMC,cAAc;AAEpB,OAAO,SAASC,UAAUC,OAAyB;IACjD,OAAOL,UAAUK,SAAS;QACxBC,WAAW,CAAC,EAACC,WAAW,EAAEC,SAAS,EAAC,GAAKT,oBAAoBS,WAAWD,YAAYE,SAAS;QAC7FC,KAAKC;QACLC,MAAM;IACR;AACF;AAEA,kFAAkF,GAClF,eAAeD,iBACbN,OAAyB,EACzBQ,QAAuB;IAEvB,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAER,SAAS,EAAC,GAAGH;IAC9C,MAAMY,UAAUZ,QAAQE,WAAW,CAACE,SAAS;IAC7C,MAAMS,iBAAiBJ,UAAUK,GAAG,EAAED;IACtC,MAAME,YAAY1C,aAAaoC;IAC/B,MAAMO,SAAS,CAAC,CAACN,KAAK,CAAC,UAAU;IAEjC,4EAA4E;IAC5E,yDAAyD;IACzD,MAAMO,oCAAoCF,WAAWE,qCAAqC;IAC1F,MAAMC,oBAAoB,CAACH,aAAaA,UAAUI,aAAa;IAE/D,0EAA0E;IAC1E,8EAA8E;IAC9E,IAAIJ,WAAW;QACb,IAAI;YACFA,UAAUK,gBAAgB;QAC5B,EAAE,OAAOC,KAAK;YACZb,SAASc,MAAM,CAAC;gBACdC,UAAUrD,UAAUsD,WAAW;gBAC/BC,SAAS3C,gBAAgBuC;gBACzBK,UAAU;gBACVC,QAAQ;YACV;QACF;IACF;IAEA,MAAMC,iBAAiBvC,iBAAiBmB,UAAU;QAACC;QAAWC;IAAK;IAEnE,0EAA0E;IAC1E,4BAA4B;IAC5B,IAAImB,UAAyB;IAC7B,IAAIX,mBAAmB;QACrBW,UAAU,MAAMtC,oBAAoBiB,UAAU;YAACsB,YAAYhC;YAAac;QAAO;IACjF,OAAO,IAAIK,mCAAmC;QAC5CY,UAAU,MAAMtC,oBAAoBiB,UAAU;YAACsB,YAAY;YAAUlB;QAAO;IAC9E;IAEAJ,SAASc,MAAM,CACbT,iBACI;QAACY,SAAS,CAAC,cAAc,EAAEZ,gBAAgB;QAAEc,QAAQ;IAAM,IAC3D;QACEF,SAAS5C;QACT6C,UAAU;QACVC,QAAQ;IACV;IAGNxC,WAAWqB,UAAU;QAACC;IAAS;IAE/B,IAAIsB,cAA8C;IAClD,IAAIrB,MAAMsB,QAAQ,EAAE;QAClBxB,SAASc,MAAM,CAAC;YACdG,SAAS7C;YACT8C,UAAU;YACVC,QAAQ;QACV;IACF,OAAO,IAAIT,mBAAmB;QAC5Ba,cAAc,MAAME,sBAAsBjC,SAAS;YAACgB;YAAQR;QAAQ;IACtE;IAEA,MAAMlB,WAAWkB,UAAU;QACzB0B,OAAO,IACLnD,SAAS;gBACPoD,oBAAoBP;gBACpBQ,kBAAkB;gBAClB3B;gBACAC;gBACA2B,QAAQlC;gBACRQ;gBACAC;YACF;QACF0B,YAAY5B,MAAMwB,KAAK,GACnBK,YACA;QACJC,gBAAgB;IAClB;IAEA,MAAMhD,gBAAgB;QAACiD,gBAAgB1B,cAAc;QAAMP;QAAUL;IAAS;IAE9E,wEAAwE;IACxE,IAAIuC;IACJ,IAAI;QACFA,WAAW,MAAM1D,uBAAuB;YAAC4B;QAAO;IAClD,EAAE,OAAOS,KAAK;QACZ5B,YAAY,iCAAiC4B;QAC7Cb,SAASc,MAAM,CAAC;YACdG,SAAS,CAAC,+BAA+B,EAAE3C,gBAAgBuC,MAAM;YACjEM,QAAQ;QACV;IACF;IAEA,4EAA4E;IAC5E,+EAA+E;IAC/E,IAAIgB;IACJ,MAAMC,gBAAgB7B,WAAW8B,oBAAoBC;IACrD,IACE7B,qCACAJ,kBACAE,WAAW8B,sBACXD,eACA;QACAD,iBAAiB,MAAMrE,sBAAsB;YAACwE,SAASF;YAAe/B;QAAc;QACpFL,SAASc,MAAM,CACbqB,iBACI;YAAClB,SAASlD,4BAA4BwC,UAAU8B,kBAAkB;YAAGlB,QAAQ;QAAM,IACnF;YACEJ,UAAUrD,UAAUsD,WAAW;YAC/BC,SAAS,CAAC,WAAW,EAAEmB,cAAc,iCAAiC,EAAE/B,eAAe,CAAC,CAAC;YACzFa,UAAU;YACVC,QAAQ;QACV;IAER;IAEA,iDAAiD;IACjD,IAAIX,QAAQ;IAEZ,IAAI2B,kBAAkBd,WAAWe,eAAe;QAC9C,MAAMxE,yBAAyB;YAC7B0E,SAASF;YACTD;YACAhC;YACAR;YACA0B;QACF;IACF;IAEA,8EAA8E;IAC9E,IAAI,CAACX,mBAAmB;QACtB,IAAIyB,kBAAkBd,SAAS;YAC7B,OAAO;gBAACkB,iBAAiB;gBAAWC,oBAAoBnB;gBAASc;gBAAgBM,QAAQ;YAAI;QAC/F;QACA;IACF;IAEA,8EAA8E;IAC9E,8EAA8E;IAC9E,IAAI,CAAClB,eAAe,CAACF,SAAS;IAE9BE,cAAc,MAAMmB,qBAAqB;QAACnB;QAAaW;QAAU/B;IAAM;IAEvE,MAAMwC,kBAAkB;QAACpB;QAAaH;QAAgBc;QAAUvC;QAAW0B;IAAO;IAElFuB,eAAe;QAACrB;QAAatB;QAAWE;IAAM;IAE9C,OAAO;QACLoC,iBAAiB;QACjBC,oBAAoBnB;QACpBoB,QAAQ;YACNI,eAAetB,YAAYuB,EAAE;YAC7BC,OAAOxB,YAAYwB,KAAK,IAAI;YAC5BC,KAAK3D,cAAckC,YAAYlB,cAAc,EAAEkB,YAAYuB,EAAE;QAC/D;IACF;AACF;AAEA;;;CAGC,GACD,eAAerB,sBACbjC,OAAyB,EACzB,EAACgB,MAAM,EAAER,QAAQ,EAA6C;IAE9D,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,MAAM,EAAC,GAAGX;IACnC,MAAMa,iBAAiBJ,UAAUK,GAAG,EAAED,kBAAkB;IACxD,iFAAiF;IACjF,MAAM0C,QAAQ7C,MAAM6C,KAAK,EAAEE,UAAUhD,UAAUK,GAAG,EAAEyC,OAAOE,UAAUlB;IAErE,IAAIvB,QAAQ;QACV,MAAM5B,eAAeoB,UAAU;YAACkD,OAAO/E,SAAS8B;YAAYI;YAAgB0C;QAAK;QACjF,OAAO;IACT;IAEA,IAAIxB,cAAc,MAAMnC,oBAAoB;QAC1Ca;QACAI;QACAF;QACA4C;QACAI,YAAY,CAAC,CAACjD,MAAMkD,GAAG;IACzB;IACAnE,YAAY,0BAA0BsC;IAEtC,IAAI,CAACA,aAAa;QAChBtC,YAAY;QACZsC,cAAc,MAAM7C,sBAAsB2B,gBAAgB0C;QAC1D9D,YAAY,4BAA4BsC;IAC1C;IAEA,OAAOA;AACT;AAEA,uEAAuE,GACvE,eAAemB,qBAAqB,EAClCnB,WAAW,EACXW,QAAQ,EACR/B,MAAM,EAKP;IACC,MAAMkD,cAAc5E,mBAAmByD,UAAUX;IACjD,IAAI,CAAC8B,aAAa,OAAO9B;IAEzBtC,YAAY,4CAA4CoE;IACxDlD,OAAOmD,GAAG,CACRD,YAAYE,IAAI,GACZ,CAAC,qBAAqB,EAAEF,YAAYE,IAAI,CAAC,MAAM,EAAEF,YAAYG,EAAE,CAAC,CAAC,CAAC,GAClE,CAAC,8BAA8B,EAAEH,YAAYG,EAAE,CAAC,CAAC,CAAC;IAExD,MAAMC,OAAO9F,QAAQ,8BAA8B+F,KAAK;IACxD,IAAI;QACF,MAAMC,UAAU,MAAMzF,sBAAsB;YAC1C2E,eAAetB,YAAYuB,EAAE;YAC7BR,SAAS;YACTsB,MAAM;gBAACb,OAAOM,YAAYG,EAAE;YAAA;QAC9B;QACAC,KAAKI,OAAO;QACZ,OAAOF;IACT,EAAE,OAAO9C,KAAK;QACZ4C,KAAKK,IAAI;QACT,MAAM7C,UAAU3C,gBAAgBuC;QAChC5B,YAAY,oCAAoC;YAACgC;QAAO;QACxDd,OAAO4D,IAAI,CAAC,CAAC,kCAAkC,EAAE9C,SAAS;QAC1D,OAAOM;IACT;AACF;AAEA,eAAeoB,kBAAkB,EAC/BpB,WAAW,EACXH,cAAc,EACdc,QAAQ,EACRvC,SAAS,EACT0B,OAAO,EAOR;IACC,MAAM2C,UAAUhG,KAAKT,QAAQoC,YAAY;QAACsE,SAAS;YAAC3G,SAASqC;SAAW;IAAA,GAAGuE,IAAI,CAACzG;IAEhF,MAAMgG,OAAO9F,QAAQ,gBAAgB+F,KAAK;IAC1C,IAAI;QACF,MAAMzF,iBAAiB;YACrB4E,eAAetB,YAAYuB,EAAE;YAC7BqB,OAAO;YACP/C;YACAc;YACA8B;YACA3C;QACF;IACF,EAAE,OAAO+C,OAAO;QACdX,KAAKY,KAAK;QACV,MAAMD;IACR;IACAX,KAAKI,OAAO;AACd;AAEA,OAAO,SAASjB,eAAe,EAC7BrB,WAAW,EACXtB,SAAS,EACTE,MAAM,EAKP;IACC,MAAM6C,MAAM3D,cAAckC,YAAYlB,cAAc,EAAEkB,YAAYuB,EAAE;IACpE,MAAMwB,QAAQ/C,YAAYwB,KAAK,GAAG,CAAC,IAAI,EAAExB,YAAYwB,KAAK,CAAC,CAAC,CAAC,GAAG;IAChE5C,OAAOmD,GAAG,CAAC,CAAC,mCAAmC,EAAE9F,UAAU,QAAQwF,OAAOsB,OAAO;IAEjF,IAAInG,SAAS8B,YAAY;IAEzBE,OAAOmD,GAAG,CAAC,CAAC,OAAO,EAAE9F,UAAU,QAAQ,cAAc,KAAK,CAAC;IAC3D2C,OAAOmD,GAAG,CACR9F,UAAU,QAAQ;IAEpB2C,OAAOmD,GAAG,CAAC,CAAC;AACd,EAAE9F,UACA,OACA,CAAC;;CAEF,CAAC,EACA;AACF,EAAEA,UACA;QAAC;QAAQ;KAAQ,EACjB,CAAC;UACO,EAAE+D,YAAYuB,EAAE,CAAC;GACxB,CAAC,GACD;AACH"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployApp.ts"],"sourcesContent":["import {basename, dirname} from 'node:path'\nimport {styleText} from 'node:util'\nimport {createGzip} from 'node:zlib'\n\nimport {exitCodes} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {\n buildExposes,\n deployConfig,\n deployCoreApp as deployWorkbenchCoreApp,\n getWorkbench,\n resolveInstallationId,\n summarizeConfig,\n} from '@sanity/workbench-cli/deploy'\nimport {pack} from 'tar-fs'\n\nimport {\n createDeployment,\n updateUserApplication,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {getAppId} from '../../util/appId.js'\nimport {EXTERNAL_APP_NOT_SUPPORTED, NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {buildApp} from '../build/buildApp.js'\nimport {extractCoreAppManifest, resolveTitleUpdate} from '../manifest/extractCoreAppManifest.js'\nimport {type CoreAppManifest} from '../manifest/types.js'\nimport {createUserApplication, generateAppSlug} from './createUserApplication.js'\nimport {\n checkAppId,\n checkAppTarget,\n checkAutoUpdates,\n checkBuild,\n checkPackageVersion,\n type DeployCheckReporter,\n verifyOutputDir,\n} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {listDeploymentFiles, reportExposes} from './deploymentPlan.js'\nimport {type DeployResult, runDeploy} from './deployRunner.js'\nimport {findUserApplication} from './findUserApplication.js'\nimport {type DeployAppOptions} from './types.js'\nimport {getCoreAppUrl} from './urlUtils.js'\n\nconst APP_PACKAGE = '@sanity/sdk-react'\n\nexport function deployApp(options: DeployAppOptions): Promise<void> {\n return runDeploy(options, {\n listFiles: ({projectRoot, sourceDir}) => listDeploymentFiles(sourceDir, projectRoot.directory),\n run: runAppDeployment,\n type: 'coreApp',\n })\n}\n\n/** Validates the deploy, syncs the title from the manifest, and ships the build. */\nasync function runAppDeployment(\n options: DeployAppOptions,\n reporter: DeployCheckReporter,\n): Promise<DeployResult | void> {\n const {cliConfig, flags, output, sourceDir} = options\n const workDir = options.projectRoot.directory\n const organizationId = cliConfig.app?.organizationId\n const appId = getAppId(cliConfig)\n const workbench = getWorkbench(cliConfig)\n const dryRun = !!flags['dry-run']\n\n // A singleton (the Media Library) persists its config instead of hosting an\n // application; anything that exposes a view or service still ships one.\n const deploySingletonConfig = workbench?.deploySingletonConfig ?? false\n const deployApplication = !workbench || workbench.hasInterfaces\n\n const appTitle = workbench\n ? flags.title?.trim() || cliConfig.app?.title?.trim() || workbench.name\n : ''\n\n // A federated app with no entry, view or service would ship a remote with\n // nothing to load — reported first so it fails before any prompt or API call.\n if (workbench) {\n try {\n workbench.assertDeployable()\n } catch (err) {\n reporter.report({\n exitCode: exitCodes.USAGE_ERROR,\n message: getErrorMessage(err),\n solution: 'Declare at least one entry, view, or service in the app',\n status: 'fail',\n })\n }\n }\n\n const isAutoUpdating = checkAutoUpdates(reporter, {cliConfig, flags})\n\n // An application ships the SDK runtime; a media-library config stamps its\n // `sanity` version instead.\n let version: string | null = null\n if (deployApplication) {\n version = await checkPackageVersion(reporter, {moduleName: APP_PACKAGE, workDir})\n } else if (deploySingletonConfig) {\n version = await checkPackageVersion(reporter, {moduleName: 'sanity', workDir})\n }\n\n reporter.report(\n organizationId\n ? {message: `Organization: ${organizationId}`, status: 'pass'}\n : {\n message: NO_ORGANIZATION_ID,\n solution: 'Add `app.organizationId` to sanity.cli.ts',\n status: 'fail',\n },\n )\n\n checkAppId(reporter, {cliConfig})\n\n let application: UserApplicationResolved | null = null\n let appCreated = false\n if (flags.external) {\n reporter.report({\n message: EXTERNAL_APP_NOT_SUPPORTED,\n solution: 'Remove the --external flag — apps deploy to Sanity hosting',\n status: 'fail',\n })\n } else if (deployApplication && workbench) {\n // Both modes, so a bad appId fails before the build rather than at the POST.\n await checkAppTarget(reporter, {\n appId,\n isWorkbenchApp: true,\n slug: workbench.slug,\n title: appTitle,\n })\n } else if (deployApplication) {\n ;({application, created: appCreated} = await resolveAppApplication(options, {dryRun, reporter}))\n }\n\n await checkBuild(reporter, {\n build: () =>\n buildApp({\n autoUpdatesEnabled: isAutoUpdating,\n calledFromDeploy: true,\n cliConfig,\n flags,\n outDir: sourceDir,\n output,\n workDir,\n }),\n skipReason: flags.build\n ? undefined\n : 'Build skipped (--no-build) — validating existing output directory',\n successMessage: 'App built',\n })\n\n await verifyOutputDir({isWorkbenchApp: workbench !== null, reporter, sourceDir})\n\n // Manifests aren't strictly essential, so a failure warns and continues\n let manifest: CoreAppManifest | undefined\n try {\n manifest = await extractCoreAppManifest({workDir})\n } catch (err) {\n deployDebug('Error extracting app manifest', err)\n reporter.report({\n message: `Error extracting app manifest: ${getErrorMessage(err)}`,\n status: 'warn',\n })\n }\n\n // Resolve the installation in both modes so the report — dry-run and real —\n // shows whether the config is deployable; a missing one fails the deploy here.\n let installationId: string | undefined\n let config: string | undefined\n const configAppType = workbench?.config?.appType\n if (deploySingletonConfig && organizationId && workbench?.config && configAppType) {\n installationId = await resolveInstallationId({appType: configAppType, organizationId})\n config = summarizeConfig(workbench.config)\n reporter.report(\n installationId\n ? {config, message: config, status: 'pass'}\n : {\n exitCode: exitCodes.USAGE_ERROR,\n message: `No active \"${configAppType}\" installation for organization \"${organizationId}\"`,\n solution: 'Install the Media Library for the organization before deploying its config',\n status: 'fail',\n },\n )\n }\n\n // Report the exposes deploying with the application, both modes.\n const exposes = deployApplication && workbench ? reportExposes(reporter, workbench) : []\n\n // Surface the app's explicit singleton flag when set, both modes.\n if (deployApplication && workbench?.isSingleton !== undefined) {\n reporter.report({\n isSingleton: workbench.isSingleton,\n message: `Singleton: ${workbench.isSingleton}`,\n status: 'pass',\n })\n }\n\n // Dry run stops here — everything below mutates.\n if (dryRun) return\n\n if (installationId && version && configAppType) {\n await deployConfig({\n appType: configAppType,\n installationId,\n output,\n sourceDir,\n version,\n })\n }\n\n // A config-only singleton ships no application, only its config.\n if (!deployApplication) {\n if (installationId && version) {\n return {\n applicationType: 'coreApp',\n applicationVersion: version,\n ...(config ? {config} : {}),\n installationId,\n target: null,\n }\n }\n return\n }\n\n // A real deploy already exited on a version-resolution failure; this narrows the type.\n if (!version) return\n\n // Workbench apps deploy to Brett; plain coreApps use user-applications.\n if (workbench && organizationId) {\n const appId = getAppId(cliConfig)\n const slug = workbench.slug ?? generateAppSlug()\n const {applicationId} = await deployWorkbenchCoreApp({\n appId,\n interfaces: buildExposes(workbench, {\n appName: workbench.name,\n appTitle,\n exposesAppView: workbench.entry !== undefined,\n version,\n }),\n isAutoUpdating,\n isSingleton: workbench.isSingleton,\n organizationId,\n slug,\n sourceDir,\n title: appTitle,\n version,\n })\n logAppDeployed({\n applicationId,\n cliConfig,\n created: !appId,\n organizationId,\n output,\n title: appTitle,\n })\n return {\n applicationType: 'coreApp',\n applicationVersion: version,\n ...(exposes.length > 0 ? {exposes} : {}),\n ...(workbench.isSingleton === undefined ? {} : {isSingleton: workbench.isSingleton}),\n target: {\n action: appId ? 'update' : 'create',\n applicationId,\n // A redeploy ignores the slug, so only a create reports the one it used.\n ...(appId ? {} : {slug}),\n title: appTitle,\n url: getCoreAppUrl(organizationId, applicationId),\n },\n }\n }\n\n // A real deploy has already exited if anything failed; landing here without a\n // resolved application means the deploy target was never resolved.\n if (!application) return\n\n application = await syncApplicationTitle({application, manifest, output})\n await shipAppDeployment({application, isAutoUpdating, manifest, sourceDir, version})\n logAppDeployed({\n applicationId: application.id,\n cliConfig,\n created: appCreated,\n organizationId: application.organizationId,\n output,\n title: application.title,\n })\n return {\n applicationType: 'coreApp',\n applicationVersion: version,\n target: {\n action: appCreated ? 'create' : 'update',\n applicationId: application.id,\n title: application.title ?? null,\n url: getCoreAppUrl(application.organizationId, application.id),\n },\n }\n}\n\n/**\n * Finds the application a real deploy targets, creating one when none is\n * configured. A dry run resolves and reports the target read-only instead.\n */\nasync function resolveAppApplication(\n options: DeployAppOptions,\n {dryRun, reporter}: {dryRun: boolean; reporter: DeployCheckReporter},\n): Promise<{application: UserApplicationResolved | null; created: boolean}> {\n const {cliConfig, flags, output} = options\n const organizationId = cliConfig.app?.organizationId ?? ''\n // Create name from --title or `app.title` config; blank falls back to the prompt\n const title = flags.title?.trim() || cliConfig.app?.title?.trim() || undefined\n\n if (dryRun) {\n await checkAppTarget(reporter, {appId: getAppId(cliConfig), organizationId, title})\n return {application: null, created: false}\n }\n\n let application = await findUserApplication({\n cliConfig,\n organizationId,\n output,\n title,\n unattended: !!flags.yes,\n })\n deployDebug('User application found', application)\n\n if (!application) {\n deployDebug('No user application found. Creating a new one')\n application = await createUserApplication(organizationId, title)\n deployDebug('User application created', application)\n return {application, created: true}\n }\n\n return {application, created: false}\n}\n\n/** Syncs the application title from the manifest when it has changed. */\nasync function syncApplicationTitle({\n application,\n manifest,\n output,\n}: {\n application: UserApplicationResolved\n manifest: CoreAppManifest | undefined\n output: DeployAppOptions['output']\n}): Promise<UserApplicationResolved> {\n const titleUpdate = resolveTitleUpdate(manifest, application)\n if (!titleUpdate) return application\n\n deployDebug('Updating application title from manifest', titleUpdate)\n output.log(\n titleUpdate.from\n ? `Updating title from \"${titleUpdate.from}\" to \"${titleUpdate.to}\"`\n : `Setting application title to \"${titleUpdate.to}\"`,\n )\n const spin = spinner('Updating application title').start()\n try {\n const updated = await updateUserApplication({\n applicationId: application.id,\n appType: 'coreApp',\n body: {title: titleUpdate.to},\n })\n spin.succeed()\n return updated\n } catch (err) {\n spin.fail()\n const message = getErrorMessage(err)\n deployDebug('Error updating application title', {message})\n output.warn(`Error updating application title: ${message}`)\n return application\n }\n}\n\nasync function shipAppDeployment({\n application,\n isAutoUpdating,\n manifest,\n sourceDir,\n version,\n}: {\n application: UserApplication\n isAutoUpdating: boolean\n manifest: CoreAppManifest | undefined\n sourceDir: string\n version: string\n}): Promise<void> {\n const tarball = pack(dirname(sourceDir), {entries: [basename(sourceDir)]}).pipe(createGzip())\n\n const spin = spinner('Deploying...').start()\n try {\n await createDeployment({\n applicationId: application.id,\n isApp: true,\n isAutoUpdating,\n manifest,\n tarball,\n version,\n })\n } catch (error) {\n spin.clear()\n throw error\n }\n spin.succeed()\n}\n\nexport function logAppDeployed({\n applicationId,\n cliConfig,\n created,\n organizationId,\n output,\n title,\n}: {\n applicationId: string\n cliConfig: DeployAppOptions['cliConfig']\n created: boolean\n organizationId: string\n output: DeployAppOptions['output']\n title: string | null\n}): void {\n const url = getCoreAppUrl(organizationId, applicationId)\n const named = title ? ` — \"${title}\"` : ''\n output.log(`\\nSuccess! Application deployed to ${styleText('cyan', url)}${named}`)\n output.log(created ? 'Created a new application.' : 'Updated the existing application.')\n\n if (getAppId(cliConfig)) return\n\n output.log(`\\n════ ${styleText('bold', 'Next step:')} ════`)\n if (created) {\n output.log(\n styleText(\n 'yellow',\n '\\nDeploying again without `deployment.appId` creates another new application.',\n ),\n )\n }\n output.log(\n styleText('bold', '\\nAdd the deployment.appId to your sanity.cli.js or sanity.cli.ts file:'),\n )\n output.log(`\n${styleText(\n 'dim',\n `app: {\n // your application config here…\n}`,\n)},\n${styleText(\n ['bold', 'green'],\n `deployment: {\n appId: '${applicationId}',\n}\\n`,\n)}`)\n}\n"],"names":["basename","dirname","styleText","createGzip","exitCodes","getErrorMessage","spinner","buildExposes","deployConfig","deployCoreApp","deployWorkbenchCoreApp","getWorkbench","resolveInstallationId","summarizeConfig","pack","createDeployment","updateUserApplication","getAppId","EXTERNAL_APP_NOT_SUPPORTED","NO_ORGANIZATION_ID","buildApp","extractCoreAppManifest","resolveTitleUpdate","createUserApplication","generateAppSlug","checkAppId","checkAppTarget","checkAutoUpdates","checkBuild","checkPackageVersion","verifyOutputDir","deployDebug","listDeploymentFiles","reportExposes","runDeploy","findUserApplication","getCoreAppUrl","APP_PACKAGE","deployApp","options","listFiles","projectRoot","sourceDir","directory","run","runAppDeployment","type","reporter","cliConfig","flags","output","workDir","organizationId","app","appId","workbench","dryRun","deploySingletonConfig","deployApplication","hasInterfaces","appTitle","title","trim","name","assertDeployable","err","report","exitCode","USAGE_ERROR","message","solution","status","isAutoUpdating","version","moduleName","application","appCreated","external","isWorkbenchApp","slug","created","resolveAppApplication","build","autoUpdatesEnabled","calledFromDeploy","outDir","skipReason","undefined","successMessage","manifest","installationId","config","configAppType","appType","exposes","isSingleton","applicationType","applicationVersion","target","applicationId","interfaces","appName","exposesAppView","entry","logAppDeployed","length","action","url","syncApplicationTitle","shipAppDeployment","id","unattended","yes","titleUpdate","log","from","to","spin","start","updated","body","succeed","fail","warn","tarball","entries","pipe","isApp","error","clear","named"],"mappings":"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"}
|
|
@@ -1,57 +1,15 @@
|
|
|
1
1
|
import { exitCodes, getLocalPackageVersion } from '@sanity/cli-core';
|
|
2
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
2
3
|
import { spinner } from '@sanity/cli-core/ux';
|
|
3
4
|
import { checkBuiltOutput } from '@sanity/workbench-cli/deploy';
|
|
4
5
|
import { resolveAppIdIssue } from '../../util/appId.js';
|
|
6
|
+
import { runStep } from '../../util/checks.js';
|
|
5
7
|
import { APP_ID_NOT_FOUND_IN_ORGANIZATION } from '../../util/errorMessages.js';
|
|
6
|
-
import { getErrorMessage } from '../../util/getErrorMessage.js';
|
|
7
8
|
import { getAutoUpdateIssueMessage, getAutoUpdateMigrationHint, resolveAutoUpdates } from '../build/shouldAutoUpdate.js';
|
|
8
9
|
import { checkDir } from './checkDir.js';
|
|
9
10
|
import { deployDebug } from './deployDebug.js';
|
|
10
|
-
import { resolveAppDeployTarget, resolveStudioDeployTarget } from './resolveDeployTarget.js';
|
|
11
|
+
import { resolveAppDeployTarget, resolveStudioDeployTarget, resolveWorkbenchApp, resolveWorkbenchStudio } from './resolveDeployTarget.js';
|
|
11
12
|
import { getCoreAppUrl } from './urlUtils.js';
|
|
12
|
-
export function createFailFastReporter(output) {
|
|
13
|
-
return {
|
|
14
|
-
report (check) {
|
|
15
|
-
// Fixes surface in both modes: appended after the message here, and in the
|
|
16
|
-
// dry-run report, so the problem and its fix never drift apart.
|
|
17
|
-
const text = check.solution ? `${check.message}: ${check.solution}` : check.message;
|
|
18
|
-
if (check.status === 'fail') {
|
|
19
|
-
output.error(text, {
|
|
20
|
-
exit: check.exitCode ?? 1
|
|
21
|
-
});
|
|
22
|
-
} else if (check.status === 'warn') {
|
|
23
|
-
output.warn(text);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
export function createCollectingReporter() {
|
|
29
|
-
const results = [];
|
|
30
|
-
return {
|
|
31
|
-
report (check) {
|
|
32
|
-
results.push(check);
|
|
33
|
-
},
|
|
34
|
-
results
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Runs a fallible step and turns a throw into a `fail` check. In a real deploy
|
|
39
|
-
* that fail exits (aborting the run); in a dry run it's recorded and `null`
|
|
40
|
-
* comes back so the caller can skip the rest of the step. `name` labels the
|
|
41
|
-
* step in debug logs.
|
|
42
|
-
*/ export async function runStep(reporter, name, work, formatError = getErrorMessage, solution) {
|
|
43
|
-
try {
|
|
44
|
-
return await work();
|
|
45
|
-
} catch (err) {
|
|
46
|
-
deployDebug(`${name} step failed`, err);
|
|
47
|
-
reporter.report({
|
|
48
|
-
message: formatError(err),
|
|
49
|
-
solution,
|
|
50
|
-
status: 'fail'
|
|
51
|
-
});
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
13
|
export async function checkPackageVersion(reporter, { moduleName, workDir }) {
|
|
56
14
|
const version = await getLocalPackageVersion(moduleName, workDir);
|
|
57
15
|
reporter.report(version ? {
|
|
@@ -115,13 +73,19 @@ export async function checkBuild(reporter, { build, skipReason, successMessage }
|
|
|
115
73
|
});
|
|
116
74
|
return;
|
|
117
75
|
}
|
|
118
|
-
await runStep(reporter,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
76
|
+
await runStep(reporter, {
|
|
77
|
+
debug: deployDebug,
|
|
78
|
+
formatError: (err)=>`Build failed: ${getErrorMessage(err)}`,
|
|
79
|
+
name: 'build',
|
|
80
|
+
solution: 'Fix the build error above, then retry',
|
|
81
|
+
work: async ()=>{
|
|
82
|
+
await build();
|
|
83
|
+
reporter.report({
|
|
84
|
+
message: successMessage,
|
|
85
|
+
status: 'pass'
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
});
|
|
125
89
|
}
|
|
126
90
|
/**
|
|
127
91
|
* The deploy directory must exist and hold the right build output: a federation
|
|
@@ -146,7 +110,7 @@ export async function checkBuild(reporter, { build, skipReason, successMessage }
|
|
|
146
110
|
* The single diagnosis for each app deploy-target verdict, shared by the
|
|
147
111
|
* dry-run report and the real deploy's unattended error paths so message,
|
|
148
112
|
* fix, and exit code can't drift between the two.
|
|
149
|
-
*/ export function describeAppTarget(resolution, { title } = {}) {
|
|
113
|
+
*/ export function describeAppTarget(resolution, { slug, title } = {}) {
|
|
150
114
|
switch(resolution.type){
|
|
151
115
|
case 'blocked':
|
|
152
116
|
{
|
|
@@ -164,6 +128,7 @@ export async function checkBuild(reporter, { build, skipReason, successMessage }
|
|
|
164
128
|
message: `Deploys to existing application "${title}" at ${url}`,
|
|
165
129
|
status: 'pass',
|
|
166
130
|
target: {
|
|
131
|
+
action: 'update',
|
|
167
132
|
applicationId: application.id,
|
|
168
133
|
title: application.title ?? null,
|
|
169
134
|
url
|
|
@@ -192,8 +157,17 @@ export async function checkBuild(reporter, { build, skipReason, successMessage }
|
|
|
192
157
|
{
|
|
193
158
|
if (title) {
|
|
194
159
|
return {
|
|
195
|
-
message: `Would create a new application "${title}"`,
|
|
196
|
-
status: 'pass'
|
|
160
|
+
message: `Would create a new application "${title}"${slug ? ` with slug "${slug}"` : ''}`,
|
|
161
|
+
status: 'pass',
|
|
162
|
+
target: {
|
|
163
|
+
action: 'create',
|
|
164
|
+
applicationId: null,
|
|
165
|
+
...slug ? {
|
|
166
|
+
slug
|
|
167
|
+
} : {},
|
|
168
|
+
title,
|
|
169
|
+
url: null
|
|
170
|
+
}
|
|
197
171
|
};
|
|
198
172
|
}
|
|
199
173
|
return {
|
|
@@ -208,13 +182,45 @@ export async function checkBuild(reporter, { build, skipReason, successMessage }
|
|
|
208
182
|
export function describeAppTargetError(err, organizationId) {
|
|
209
183
|
return err?.statusCode === 403 ? `You don’t have permission to view applications for the configured organization ID ("${organizationId}"). Verify the organization ID, or ask your organization’s admin for access.` : `Failed to resolve deploy target: ${getErrorMessage(err)}`;
|
|
210
184
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
185
|
+
/**
|
|
186
|
+
* Reports the app deploy target as a check and returns the resolved target;
|
|
187
|
+
* `isWorkbenchApp` selects the backend. Both modes run it — a real deploy uses
|
|
188
|
+
* it to reject a bad `appId` before building.
|
|
189
|
+
*/ export async function checkAppTarget(reporter, options) {
|
|
190
|
+
const { title } = options;
|
|
191
|
+
if (options.isWorkbenchApp) {
|
|
192
|
+
const { appId, slug } = options;
|
|
193
|
+
return runStep(reporter, {
|
|
194
|
+
debug: deployDebug,
|
|
195
|
+
name: 'target',
|
|
196
|
+
work: async ()=>{
|
|
197
|
+
const check = describeAppTarget(await resolveWorkbenchApp({
|
|
198
|
+
appId
|
|
199
|
+
}), {
|
|
200
|
+
slug,
|
|
201
|
+
title
|
|
202
|
+
});
|
|
203
|
+
reporter.report(check);
|
|
204
|
+
return check.target ?? null;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
const { appId, organizationId } = options;
|
|
209
|
+
return runStep(reporter, {
|
|
210
|
+
debug: deployDebug,
|
|
211
|
+
formatError: (err)=>describeAppTargetError(err, organizationId),
|
|
212
|
+
name: 'target',
|
|
213
|
+
work: async ()=>{
|
|
214
|
+
const check = describeAppTarget(await resolveAppDeployTarget({
|
|
215
|
+
appId,
|
|
216
|
+
organizationId
|
|
217
|
+
}), {
|
|
218
|
+
title
|
|
219
|
+
});
|
|
220
|
+
reporter.report(check);
|
|
221
|
+
return check.target ?? null;
|
|
222
|
+
}
|
|
223
|
+
});
|
|
218
224
|
}
|
|
219
225
|
/** Same contract as {@link describeAppTarget}, for the studio verdicts. */ export function describeStudioTarget(resolution, { isExternal, title }) {
|
|
220
226
|
const studioUrl = (host)=>isExternal ? host : `https://${host}.sanity.studio`;
|
|
@@ -233,6 +239,7 @@ export async function checkAppTarget(reporter, { appId, organizationId, title })
|
|
|
233
239
|
message: `Deploys to existing studio ${url}`,
|
|
234
240
|
status: 'pass',
|
|
235
241
|
target: {
|
|
242
|
+
action: 'update',
|
|
236
243
|
applicationId: resolution.application.id,
|
|
237
244
|
title: resolution.application.title ?? null,
|
|
238
245
|
url
|
|
@@ -265,20 +272,44 @@ export async function checkAppTarget(reporter, { appId, organizationId, title })
|
|
|
265
272
|
return {
|
|
266
273
|
message: isExternal ? `Would register external studio at ${resolution.appHost}${titled}` : `Would create studio hostname ${url}${titled} (name availability is checked on deploy)`,
|
|
267
274
|
status: 'pass',
|
|
275
|
+
// `title || null`, not `?? null`, so target.title tracks the same
|
|
276
|
+
// truthiness the message's `titled` suffix uses (an empty title is no title)
|
|
268
277
|
target: {
|
|
278
|
+
action: 'create',
|
|
269
279
|
applicationId: null,
|
|
270
|
-
title: null,
|
|
280
|
+
title: title || null,
|
|
271
281
|
url
|
|
272
282
|
}
|
|
273
283
|
};
|
|
274
284
|
}
|
|
275
285
|
}
|
|
276
286
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Reports the studio deploy target as a check and returns the resolved target;
|
|
289
|
+
* `isWorkbenchApp` selects the backend. Both modes run it — a real deploy uses
|
|
290
|
+
* the returned target to reject a bad `appId` before building and to report the
|
|
291
|
+
* studio URL from the resolved slug.
|
|
292
|
+
*/ export async function checkStudioTarget(reporter, options) {
|
|
293
|
+
const { title } = options;
|
|
294
|
+
const resolve = options.isWorkbenchApp ? resolveWorkbenchStudio({
|
|
295
|
+
appId: options.appId,
|
|
296
|
+
studioHost: options.studioHost
|
|
297
|
+
}) : resolveStudioDeployTarget(options);
|
|
298
|
+
// Workbench studios always deploy to Sanity hosting, never an external URL.
|
|
299
|
+
const isExternal = options.isWorkbenchApp ? false : options.isExternal;
|
|
300
|
+
return runStep(reporter, {
|
|
301
|
+
debug: deployDebug,
|
|
302
|
+
formatError: (err)=>`Failed to resolve deploy target: ${getErrorMessage(err)}`,
|
|
303
|
+
name: 'target',
|
|
304
|
+
work: async ()=>{
|
|
305
|
+
const check = describeStudioTarget(await resolve, {
|
|
306
|
+
isExternal,
|
|
307
|
+
title
|
|
308
|
+
});
|
|
309
|
+
reporter.report(check);
|
|
310
|
+
return check.target ?? null;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
282
313
|
}
|
|
283
314
|
|
|
284
315
|
//# sourceMappingURL=deployChecks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployChecks.ts"],"sourcesContent":["import {type CliConfig, exitCodes, getLocalPackageVersion, type Output} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {checkBuiltOutput} from '@sanity/workbench-cli/deploy'\n\nimport {resolveAppIdIssue} from '../../util/appId.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {getErrorMessage} from '../../util/getErrorMessage.js'\nimport {\n getAutoUpdateIssueMessage,\n getAutoUpdateMigrationHint,\n resolveAutoUpdates,\n} from '../build/shouldAutoUpdate.js'\nimport {checkDir} from './checkDir.js'\nimport {deployDebug} from './deployDebug.js'\nimport {\n type AppDeployTargetResolution,\n resolveAppDeployTarget,\n resolveStudioDeployTarget,\n type StudioDeployTargetResolution,\n} from './resolveDeployTarget.js'\nimport {type DeployFlags} from './types.js'\nimport {getCoreAppUrl} from './urlUtils.js'\n\ntype DeployCheckStatus = 'fail' | 'pass' | 'skip' | 'warn'\n\n/**\n * Where a deploy resolves to, computed once from the deploy-target verdict. The\n * dry-run report and its JSON both read this, so the human and machine outputs\n * can't drift.\n */\nexport interface DeployTarget {\n /** The application the deploy targets; `null` when a deploy would create one. */\n applicationId: string | null\n /** The application's title; `null` when it has none (or isn't created yet). */\n title: string | null\n /** Where the deployed studio/app is reachable; `null` when it can't be resolved yet. */\n url: string | null\n}\n\nexport interface DeployCheck {\n message: string\n status: DeployCheckStatus\n\n /** Exit code a real deploy uses when this check fails; defaults to 1 */\n exitCode?: number\n /** Actionable fix, shown under a failing or warning check */\n solution?: string\n /** Set on the deploy-target check with the resolved target both reporters read. */\n target?: DeployTarget\n /** Set on the package-version check with the version both reporters read. */\n version?: string\n}\n\n/**\n * Where deploy steps send their check outcomes — and the only place the deploy\n * mode lives. A real deploy fails fast: a `fail` prints and exits immediately,\n * which aborts the sequence. A dry run collects every outcome and never exits.\n * Steps just call `report`; they never know which mode is running.\n */\nexport interface CheckReporter {\n report(check: DeployCheck): void\n}\n\nexport function createFailFastReporter(output: Output): CheckReporter {\n return {\n report(check) {\n // Fixes surface in both modes: appended after the message here, and in the\n // dry-run report, so the problem and its fix never drift apart.\n const text = check.solution ? `${check.message}: ${check.solution}` : check.message\n if (check.status === 'fail') {\n output.error(text, {exit: check.exitCode ?? 1})\n } else if (check.status === 'warn') {\n output.warn(text)\n }\n },\n }\n}\n\nexport function createCollectingReporter(): CheckReporter & {results: DeployCheck[]} {\n const results: DeployCheck[] = []\n return {\n report(check) {\n results.push(check)\n },\n results,\n }\n}\n\n/**\n * Runs a fallible step and turns a throw into a `fail` check. In a real deploy\n * that fail exits (aborting the run); in a dry run it's recorded and `null`\n * comes back so the caller can skip the rest of the step. `name` labels the\n * step in debug logs.\n */\nexport async function runStep<T>(\n reporter: CheckReporter,\n name: string,\n work: () => Promise<T>,\n formatError: (err: unknown) => string = getErrorMessage,\n solution?: string,\n): Promise<T | null> {\n try {\n return await work()\n } catch (err) {\n deployDebug(`${name} step failed`, err)\n reporter.report({message: formatError(err), solution, status: 'fail'})\n return null\n }\n}\n\nexport async function checkPackageVersion(\n reporter: CheckReporter,\n {moduleName, workDir}: {moduleName: string; workDir: string},\n): Promise<string | null> {\n const version = await getLocalPackageVersion(moduleName, workDir)\n reporter.report(\n version\n ? {message: `Using ${moduleName} ${version}`, status: 'pass', version}\n : {\n message: `Failed to find installed ${moduleName} version`,\n solution: `Install ${moduleName} in this project`,\n status: 'fail',\n },\n )\n return version\n}\n\nexport function checkAutoUpdates(\n reporter: CheckReporter,\n {cliConfig, flags}: {cliConfig: CliConfig; flags: DeployFlags},\n): boolean {\n const {enabled, issue} = resolveAutoUpdates({cliConfig, flags})\n\n if (issue) {\n reporter.report({\n message: getAutoUpdateIssueMessage(issue),\n // A config conflict makes a real deploy refuse to run; deprecations only warn\n status: issue.type === 'conflicting-config' ? 'fail' : 'warn',\n })\n\n // The deprecated top-level config also gets the styled migration edit, the\n // same second warning the build/dev path prints through shouldAutoUpdate.\n if (issue.type === 'deprecated-config') {\n reporter.report({\n message: getAutoUpdateMigrationHint(cliConfig.autoUpdates),\n status: 'warn',\n })\n }\n }\n\n return enabled\n}\n\n/**\n * The dry-run form of the `app.id` config check a real deploy runs in\n * `findUserApplication`: a conflict fails (both `app.id` and `deployment.appId`\n * set), the deprecated config alone warns.\n */\nexport function checkAppId(reporter: CheckReporter, {cliConfig}: {cliConfig: CliConfig}): void {\n const issue = resolveAppIdIssue(cliConfig)\n if (issue === 'conflicting-config') {\n reporter.report({\n message: 'Both `app.id` (deprecated) and `deployment.appId` are set',\n solution: 'Remove `app.id` from sanity.cli.ts',\n status: 'fail',\n })\n } else if (issue === 'deprecated-config') {\n reporter.report({\n message: 'The `app.id` config is deprecated',\n solution: 'Move it to `deployment.appId` in sanity.cli.ts',\n status: 'warn',\n })\n }\n}\n\nexport async function checkBuild(\n reporter: CheckReporter,\n {\n build,\n skipReason,\n successMessage,\n }: {build: () => Promise<void>; skipReason: string | undefined; successMessage: string},\n): Promise<void> {\n if (skipReason) {\n reporter.report({message: skipReason, status: 'skip'})\n return\n }\n\n await runStep(\n reporter,\n 'build',\n async () => {\n await build()\n reporter.report({message: successMessage, status: 'pass'})\n },\n (err) => `Build failed: ${getErrorMessage(err)}`,\n 'Fix the build error above, then retry',\n )\n}\n\n/**\n * The deploy directory must exist and hold the right build output: a federation\n * remote for a workbench app, an `index.html` SPA otherwise.\n */\nexport async function verifyOutputDir({\n isWorkbenchApp,\n reporter,\n sourceDir,\n}: {\n isWorkbenchApp: boolean\n reporter: CheckReporter\n sourceDir: string\n}): Promise<void> {\n const spin = spinner('Verifying local content...').start()\n const verifyBuild = isWorkbenchApp ? checkBuiltOutput : checkDir\n try {\n await verifyBuild(sourceDir)\n spin.succeed()\n } catch (err) {\n spin.fail()\n deployDebug('Error checking directory', err)\n reporter.report({\n message: getErrorMessage(err),\n solution: 'Run the build first, or check the output directory',\n status: 'fail',\n })\n }\n}\n\n/**\n * The single diagnosis for each app deploy-target verdict, shared by the\n * dry-run report and the real deploy's unattended error paths so message,\n * fix, and exit code can't drift between the two.\n */\nexport function describeAppTarget(\n resolution: AppDeployTargetResolution,\n {title}: {title?: string} = {},\n): DeployCheck {\n switch (resolution.type) {\n case 'blocked': {\n return {message: `Deploy target not resolved — ${resolution.message}`, status: 'skip'}\n }\n case 'found': {\n const {application} = resolution\n const title = application.title ?? application.appHost\n const url = getCoreAppUrl(application.organizationId, application.id)\n return {\n message: `Deploys to existing application \"${title}\" at ${url}`,\n status: 'pass',\n target: {applicationId: application.id, title: application.title ?? null, url},\n }\n }\n case 'invalid': {\n return {\n message: APP_ID_NOT_FOUND_IN_ORGANIZATION,\n solution: 'Check `deployment.appId` matches an app in your organization',\n status: 'fail',\n }\n }\n case 'needs-input': {\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: `No \\`deployment.appId\\` configured (${resolution.existing.length} existing ${resolution.existing.length === 1 ? 'application' : 'applications'} to choose from)`,\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n status: 'fail',\n }\n }\n // Without --title, creating an app needs a prompt no unattended run can answer\n case 'would-create': {\n if (title) {\n return {message: `Would create a new application \"${title}\"`, status: 'pass'}\n }\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: 'No application to deploy to — creating one needs a title',\n solution:\n 'Pass `--title \"<name>\"` or set `app.title` in sanity.cli.ts to create one, or set `deployment.appId` to deploy to an existing app',\n status: 'fail',\n }\n }\n }\n}\n\nexport function describeAppTargetError(err: unknown, organizationId: string | undefined): string {\n return (err as {statusCode?: number})?.statusCode === 403\n ? `You don’t have permission to view applications for the configured organization ID (\"${organizationId}\"). Verify the organization ID, or ask your organization’s admin for access.`\n : `Failed to resolve deploy target: ${getErrorMessage(err)}`\n}\n\nexport async function checkAppTarget(\n reporter: CheckReporter,\n {\n appId,\n organizationId,\n title,\n }: {appId: string | undefined; organizationId: string | undefined; title?: string},\n): Promise<void> {\n await runStep(\n reporter,\n 'target',\n async () =>\n reporter.report(\n describeAppTarget(await resolveAppDeployTarget({appId, organizationId}), {title}),\n ),\n (err) => describeAppTargetError(err, organizationId),\n )\n}\n\n/** Same contract as {@link describeAppTarget}, for the studio verdicts. */\nexport function describeStudioTarget(\n resolution: StudioDeployTargetResolution,\n {isExternal, title}: {isExternal: boolean; title?: string},\n): DeployCheck {\n const studioUrl = (host: string) => (isExternal ? host : `https://${host}.sanity.studio`)\n\n switch (resolution.type) {\n case 'blocked': {\n return {message: `Deploy target not resolved — ${resolution.message}`, status: 'skip'}\n }\n case 'found': {\n const url = studioUrl(resolution.application.appHost)\n return {\n message: `Deploys to existing studio ${url}`,\n status: 'pass',\n target: {\n applicationId: resolution.application.id,\n title: resolution.application.title ?? null,\n url,\n },\n }\n }\n case 'invalid': {\n return {\n // A bad host is a usage error; other invalid targets exit 1\n exitCode: resolution.reason === 'invalid-host' ? exitCodes.USAGE_ERROR : 1,\n message: resolution.message,\n solution: 'Check `studioHost` and `deployment.appId` in sanity.cli.ts',\n status: 'fail',\n }\n }\n case 'needs-input': {\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: isExternal ? 'No external studio URL configured' : 'No studio hostname configured',\n solution: isExternal\n ? 'Set `studioHost` in sanity.cli.ts, or pass the full URL with --url'\n : 'Set `studioHost` in sanity.cli.ts, or pass a hostname with --url',\n status: 'fail',\n }\n }\n case 'would-create': {\n const url = studioUrl(resolution.appHost)\n const titled = title ? ` titled \"${title}\"` : ''\n return {\n message: isExternal\n ? `Would register external studio at ${resolution.appHost}${titled}`\n : `Would create studio hostname ${url}${titled} (name availability is checked on deploy)`,\n status: 'pass',\n target: {applicationId: null, title: null, url},\n }\n }\n }\n}\n\nexport async function checkStudioTarget(\n reporter: CheckReporter,\n options: {\n appId: string | undefined\n isExternal: boolean\n projectId: string | undefined\n studioHost: string | undefined\n title: string | undefined\n urlFlag: string | undefined\n },\n): Promise<void> {\n await runStep(\n reporter,\n 'target',\n async () =>\n reporter.report(\n describeStudioTarget(await resolveStudioDeployTarget(options), {\n isExternal: options.isExternal,\n title: options.title,\n }),\n ),\n (err) => `Failed to resolve deploy target: ${getErrorMessage(err)}`,\n )\n}\n"],"names":["exitCodes","getLocalPackageVersion","spinner","checkBuiltOutput","resolveAppIdIssue","APP_ID_NOT_FOUND_IN_ORGANIZATION","getErrorMessage","getAutoUpdateIssueMessage","getAutoUpdateMigrationHint","resolveAutoUpdates","checkDir","deployDebug","resolveAppDeployTarget","resolveStudioDeployTarget","getCoreAppUrl","createFailFastReporter","output","report","check","text","solution","message","status","error","exit","exitCode","warn","createCollectingReporter","results","push","runStep","reporter","name","work","formatError","err","checkPackageVersion","moduleName","workDir","version","checkAutoUpdates","cliConfig","flags","enabled","issue","type","autoUpdates","checkAppId","checkBuild","build","skipReason","successMessage","verifyOutputDir","isWorkbenchApp","sourceDir","spin","start","verifyBuild","succeed","fail","describeAppTarget","resolution","title","application","appHost","url","organizationId","id","target","applicationId","USAGE_ERROR","existing","length","describeAppTargetError","statusCode","checkAppTarget","appId","describeStudioTarget","isExternal","studioUrl","host","reason","titled","checkStudioTarget","options"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployChecks.ts"],"sourcesContent":["import {type CliConfig, exitCodes, getLocalPackageVersion} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {checkBuiltOutput, type DeployedExpose} from '@sanity/workbench-cli/deploy'\n\nimport {resolveAppIdIssue} from '../../util/appId.js'\nimport {type Check, type CheckReporter, runStep} from '../../util/checks.js'\nimport {APP_ID_NOT_FOUND_IN_ORGANIZATION} from '../../util/errorMessages.js'\nimport {\n getAutoUpdateIssueMessage,\n getAutoUpdateMigrationHint,\n resolveAutoUpdates,\n} from '../build/shouldAutoUpdate.js'\nimport {checkDir} from './checkDir.js'\nimport {deployDebug} from './deployDebug.js'\nimport {\n type AppDeployTargetResolution,\n resolveAppDeployTarget,\n resolveStudioDeployTarget,\n resolveWorkbenchApp,\n resolveWorkbenchStudio,\n type StudioDeployTargetResolution,\n} from './resolveDeployTarget.js'\nimport {type DeployFlags} from './types.js'\nimport {getCoreAppUrl} from './urlUtils.js'\n\n/**\n * Where a deploy resolves to, computed once from the deploy-target verdict. The\n * dry-run report and its JSON both read this, so the human and machine outputs\n * can't drift.\n */\nexport interface DeployTarget {\n /** Whether the deploy creates a new application/studio or updates an existing one. */\n action: 'create' | 'update'\n /** The application the deploy targets; `null` when a deploy would create one. */\n applicationId: string | null\n /** The application's title; `null` when it has none (or isn't created yet). */\n title: string | null\n /** Where the deployed studio/app is reachable; `null` when it can't be resolved yet. */\n url: string | null\n\n /**\n * Slug the deploy creates the application at. Omitted on redeploys, and in a\n * dry run when no slug is configured (it's generated on deploy).\n */\n slug?: string\n}\n\nexport interface DeployCheck extends Check {\n /** Set on the config check with its summary both reporters read. */\n config?: string\n /** Set on the exposes check with the workbench exposes both reporters read. */\n exposes?: DeployedExpose[]\n /** Set on the singleton check when the app declares the flag explicitly. */\n isSingleton?: boolean\n /** Set on the deploy-target check with the resolved target both reporters read. */\n target?: DeployTarget\n /** Set on the package-version check with the version both reporters read. */\n version?: string\n}\n\nexport type DeployCheckReporter = CheckReporter<DeployCheck>\n\nexport async function checkPackageVersion(\n reporter: DeployCheckReporter,\n {moduleName, workDir}: {moduleName: string; workDir: string},\n): Promise<string | null> {\n const version = await getLocalPackageVersion(moduleName, workDir)\n reporter.report(\n version\n ? {message: `Using ${moduleName} ${version}`, status: 'pass', version}\n : {\n message: `Failed to find installed ${moduleName} version`,\n solution: `Install ${moduleName} in this project`,\n status: 'fail',\n },\n )\n return version\n}\n\nexport function checkAutoUpdates(\n reporter: DeployCheckReporter,\n {cliConfig, flags}: {cliConfig: CliConfig; flags: DeployFlags},\n): boolean {\n const {enabled, issue} = resolveAutoUpdates({cliConfig, flags})\n\n if (issue) {\n reporter.report({\n message: getAutoUpdateIssueMessage(issue),\n // A config conflict makes a real deploy refuse to run; deprecations only warn\n status: issue.type === 'conflicting-config' ? 'fail' : 'warn',\n })\n\n // The deprecated top-level config also gets the styled migration edit, the\n // same second warning the build/dev path prints through shouldAutoUpdate.\n if (issue.type === 'deprecated-config') {\n reporter.report({\n message: getAutoUpdateMigrationHint(cliConfig.autoUpdates),\n status: 'warn',\n })\n }\n }\n\n return enabled\n}\n\n/**\n * The dry-run form of the `app.id` config check a real deploy runs in\n * `findUserApplication`: a conflict fails (both `app.id` and `deployment.appId`\n * set), the deprecated config alone warns.\n */\nexport function checkAppId(\n reporter: DeployCheckReporter,\n {cliConfig}: {cliConfig: CliConfig},\n): void {\n const issue = resolveAppIdIssue(cliConfig)\n if (issue === 'conflicting-config') {\n reporter.report({\n message: 'Both `app.id` (deprecated) and `deployment.appId` are set',\n solution: 'Remove `app.id` from sanity.cli.ts',\n status: 'fail',\n })\n } else if (issue === 'deprecated-config') {\n reporter.report({\n message: 'The `app.id` config is deprecated',\n solution: 'Move it to `deployment.appId` in sanity.cli.ts',\n status: 'warn',\n })\n }\n}\n\nexport async function checkBuild(\n reporter: DeployCheckReporter,\n {\n build,\n skipReason,\n successMessage,\n }: {build: () => Promise<void>; skipReason: string | undefined; successMessage: string},\n): Promise<void> {\n if (skipReason) {\n reporter.report({message: skipReason, status: 'skip'})\n return\n }\n\n await runStep(reporter, {\n debug: deployDebug,\n formatError: (err) => `Build failed: ${getErrorMessage(err)}`,\n name: 'build',\n solution: 'Fix the build error above, then retry',\n work: async () => {\n await build()\n reporter.report({message: successMessage, status: 'pass'})\n },\n })\n}\n\n/**\n * The deploy directory must exist and hold the right build output: a federation\n * remote for a workbench app, an `index.html` SPA otherwise.\n */\nexport async function verifyOutputDir({\n isWorkbenchApp,\n reporter,\n sourceDir,\n}: {\n isWorkbenchApp: boolean\n reporter: DeployCheckReporter\n sourceDir: string\n}): Promise<void> {\n const spin = spinner('Verifying local content...').start()\n const verifyBuild = isWorkbenchApp ? checkBuiltOutput : checkDir\n try {\n await verifyBuild(sourceDir)\n spin.succeed()\n } catch (err) {\n spin.fail()\n deployDebug('Error checking directory', err)\n reporter.report({\n message: getErrorMessage(err),\n solution: 'Run the build first, or check the output directory',\n status: 'fail',\n })\n }\n}\n\n/**\n * The single diagnosis for each app deploy-target verdict, shared by the\n * dry-run report and the real deploy's unattended error paths so message,\n * fix, and exit code can't drift between the two.\n */\nexport function describeAppTarget(\n resolution: AppDeployTargetResolution,\n {slug, title}: {slug?: string; title?: string} = {},\n): DeployCheck {\n switch (resolution.type) {\n case 'blocked': {\n return {message: `Deploy target not resolved — ${resolution.message}`, status: 'skip'}\n }\n case 'found': {\n const {application} = resolution\n const title = application.title ?? application.appHost\n const url = getCoreAppUrl(application.organizationId, application.id)\n return {\n message: `Deploys to existing application \"${title}\" at ${url}`,\n status: 'pass',\n target: {\n action: 'update',\n applicationId: application.id,\n title: application.title ?? null,\n url,\n },\n }\n }\n case 'invalid': {\n return {\n message: APP_ID_NOT_FOUND_IN_ORGANIZATION,\n solution: 'Check `deployment.appId` matches an app in your organization',\n status: 'fail',\n }\n }\n case 'needs-input': {\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: `No \\`deployment.appId\\` configured (${resolution.existing.length} existing ${resolution.existing.length === 1 ? 'application' : 'applications'} to choose from)`,\n solution: 'Add `deployment.appId` to sanity.cli.ts',\n status: 'fail',\n }\n }\n // Without --title, creating an app needs a prompt no unattended run can answer\n case 'would-create': {\n if (title) {\n return {\n message: `Would create a new application \"${title}\"${slug ? ` with slug \"${slug}\"` : ''}`,\n status: 'pass',\n target: {\n action: 'create',\n applicationId: null,\n ...(slug ? {slug} : {}),\n title,\n url: null,\n },\n }\n }\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: 'No application to deploy to — creating one needs a title',\n solution:\n 'Pass `--title \"<name>\"` or set `app.title` in sanity.cli.ts to create one, or set `deployment.appId` to deploy to an existing app',\n status: 'fail',\n }\n }\n }\n}\n\nexport function describeAppTargetError(err: unknown, organizationId: string | undefined): string {\n return (err as {statusCode?: number})?.statusCode === 403\n ? `You don’t have permission to view applications for the configured organization ID (\"${organizationId}\"). Verify the organization ID, or ask your organization’s admin for access.`\n : `Failed to resolve deploy target: ${getErrorMessage(err)}`\n}\n\n/**\n * Reports the app deploy target as a check and returns the resolved target;\n * `isWorkbenchApp` selects the backend. Both modes run it — a real deploy uses\n * it to reject a bad `appId` before building.\n */\nexport async function checkAppTarget(\n reporter: DeployCheckReporter,\n options:\n | {appId: string | undefined; isWorkbenchApp: true; slug?: string; title?: string}\n | {\n appId: string | undefined\n isWorkbenchApp?: false\n organizationId: string | undefined\n title?: string\n },\n): Promise<DeployTarget | null> {\n const {title} = options\n if (options.isWorkbenchApp) {\n const {appId, slug} = options\n return runStep(reporter, {\n debug: deployDebug,\n name: 'target',\n work: async () => {\n const check = describeAppTarget(await resolveWorkbenchApp({appId}), {slug, title})\n reporter.report(check)\n return check.target ?? null\n },\n })\n }\n\n const {appId, organizationId} = options\n return runStep(reporter, {\n debug: deployDebug,\n formatError: (err) => describeAppTargetError(err, organizationId),\n name: 'target',\n work: async () => {\n const check = describeAppTarget(await resolveAppDeployTarget({appId, organizationId}), {\n title,\n })\n reporter.report(check)\n return check.target ?? null\n },\n })\n}\n\n/** Same contract as {@link describeAppTarget}, for the studio verdicts. */\nexport function describeStudioTarget(\n resolution: StudioDeployTargetResolution,\n {isExternal, title}: {isExternal: boolean; title?: string},\n): DeployCheck {\n const studioUrl = (host: string) => (isExternal ? host : `https://${host}.sanity.studio`)\n\n switch (resolution.type) {\n case 'blocked': {\n return {message: `Deploy target not resolved — ${resolution.message}`, status: 'skip'}\n }\n case 'found': {\n const url = studioUrl(resolution.application.appHost)\n return {\n message: `Deploys to existing studio ${url}`,\n status: 'pass',\n target: {\n action: 'update',\n applicationId: resolution.application.id,\n title: resolution.application.title ?? null,\n url,\n },\n }\n }\n case 'invalid': {\n return {\n // A bad host is a usage error; other invalid targets exit 1\n exitCode: resolution.reason === 'invalid-host' ? exitCodes.USAGE_ERROR : 1,\n message: resolution.message,\n solution: 'Check `studioHost` and `deployment.appId` in sanity.cli.ts',\n status: 'fail',\n }\n }\n case 'needs-input': {\n return {\n exitCode: exitCodes.USAGE_ERROR,\n message: isExternal ? 'No external studio URL configured' : 'No studio hostname configured',\n solution: isExternal\n ? 'Set `studioHost` in sanity.cli.ts, or pass the full URL with --url'\n : 'Set `studioHost` in sanity.cli.ts, or pass a hostname with --url',\n status: 'fail',\n }\n }\n case 'would-create': {\n const url = studioUrl(resolution.appHost)\n const titled = title ? ` titled \"${title}\"` : ''\n return {\n message: isExternal\n ? `Would register external studio at ${resolution.appHost}${titled}`\n : `Would create studio hostname ${url}${titled} (name availability is checked on deploy)`,\n status: 'pass',\n // `title || null`, not `?? null`, so target.title tracks the same\n // truthiness the message's `titled` suffix uses (an empty title is no title)\n target: {action: 'create', applicationId: null, title: title || null, url},\n }\n }\n }\n}\n\n/**\n * Reports the studio deploy target as a check and returns the resolved target;\n * `isWorkbenchApp` selects the backend. Both modes run it — a real deploy uses\n * the returned target to reject a bad `appId` before building and to report the\n * studio URL from the resolved slug.\n */\nexport async function checkStudioTarget(\n reporter: DeployCheckReporter,\n options:\n | {\n appId: string | undefined\n isExternal: boolean\n isWorkbenchApp?: false\n projectId: string | undefined\n studioHost: string | undefined\n title: string | undefined\n urlFlag: string | undefined\n }\n | {\n appId: string | undefined\n isWorkbenchApp: true\n studioHost: string | undefined\n title?: string\n },\n): Promise<DeployTarget | null> {\n const {title} = options\n const resolve = options.isWorkbenchApp\n ? resolveWorkbenchStudio({appId: options.appId, studioHost: options.studioHost})\n : resolveStudioDeployTarget(options)\n // Workbench studios always deploy to Sanity hosting, never an external URL.\n const isExternal = options.isWorkbenchApp ? false : options.isExternal\n\n return runStep(reporter, {\n debug: deployDebug,\n formatError: (err) => `Failed to resolve deploy target: ${getErrorMessage(err)}`,\n name: 'target',\n work: async () => {\n const check = describeStudioTarget(await resolve, {isExternal, title})\n reporter.report(check)\n return check.target ?? null\n },\n })\n}\n"],"names":["exitCodes","getLocalPackageVersion","getErrorMessage","spinner","checkBuiltOutput","resolveAppIdIssue","runStep","APP_ID_NOT_FOUND_IN_ORGANIZATION","getAutoUpdateIssueMessage","getAutoUpdateMigrationHint","resolveAutoUpdates","checkDir","deployDebug","resolveAppDeployTarget","resolveStudioDeployTarget","resolveWorkbenchApp","resolveWorkbenchStudio","getCoreAppUrl","checkPackageVersion","reporter","moduleName","workDir","version","report","message","status","solution","checkAutoUpdates","cliConfig","flags","enabled","issue","type","autoUpdates","checkAppId","checkBuild","build","skipReason","successMessage","debug","formatError","err","name","work","verifyOutputDir","isWorkbenchApp","sourceDir","spin","start","verifyBuild","succeed","fail","describeAppTarget","resolution","slug","title","application","appHost","url","organizationId","id","target","action","applicationId","exitCode","USAGE_ERROR","existing","length","describeAppTargetError","statusCode","checkAppTarget","options","appId","check","describeStudioTarget","isExternal","studioUrl","host","reason","titled","checkStudioTarget","resolve","studioHost"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { format } from 'node:util';
|
|
2
1
|
import { CLIError } from '@oclif/core/errors';
|
|
3
|
-
import {
|
|
2
|
+
import { getErrorMessage } from '@sanity/cli-core/errors';
|
|
3
|
+
import { createCollectingReporter, createFailFastReporter } from '../../util/checks.js';
|
|
4
|
+
import { toStderrOutput } from '../../util/toStderrOutput.js';
|
|
4
5
|
import { deployDebug } from './deployDebug.js';
|
|
5
6
|
import { deploymentPlanToJson, isDeployable, renderDeploymentPlan } from './deploymentPlan.js';
|
|
6
7
|
/**
|
|
@@ -10,31 +11,41 @@ import { deploymentPlanToJson, isDeployable, renderDeploymentPlan } from './depl
|
|
|
10
11
|
*/ export async function runDeploy(options, spec) {
|
|
11
12
|
const { output } = options;
|
|
12
13
|
const json = !!options.flags.json;
|
|
14
|
+
const emitJson = (payload)=>output.log(JSON.stringify(payload, null, 2));
|
|
13
15
|
// The JSON payload owns stdout, so the run's progress logs go to stderr; only
|
|
14
|
-
// the final JSON.stringify writes to stdout.
|
|
16
|
+
// the final JSON.stringify writes to stdout.
|
|
15
17
|
const runOptions = json ? {
|
|
16
18
|
...options,
|
|
17
|
-
output:
|
|
18
|
-
...output,
|
|
19
|
-
log: (message = '', ...args)=>void process.stderr.write(`${format(message, ...args)}\n`)
|
|
20
|
-
}
|
|
19
|
+
output: toStderrOutput(output)
|
|
21
20
|
} : options;
|
|
22
21
|
try {
|
|
23
22
|
if (options.flags['dry-run']) {
|
|
24
23
|
const plan = await collectPlan(runOptions, spec);
|
|
25
|
-
if (json)
|
|
24
|
+
if (json) emitJson(deploymentPlanToJson(plan));
|
|
26
25
|
else renderDeploymentPlan(plan, output);
|
|
27
26
|
exitIfBlocked(plan, output);
|
|
28
27
|
return;
|
|
29
28
|
}
|
|
30
29
|
const result = await spec.run(runOptions, createFailFastReporter(runOptions.output));
|
|
31
|
-
if (json && result)
|
|
30
|
+
if (json && result) emitJson({
|
|
32
31
|
deployed: true,
|
|
33
32
|
...result
|
|
34
|
-
}
|
|
33
|
+
});
|
|
35
34
|
} catch (error) {
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const failure = normalizeFailure(error, spec.type);
|
|
36
|
+
// A blocked dry run reaches this catch too (its exit throws) and already
|
|
37
|
+
// printed its plan, so only a real deploy adds the {deployed: false} envelope.
|
|
38
|
+
if (json && !options.flags['dry-run']) {
|
|
39
|
+
emitJson({
|
|
40
|
+
deployed: false,
|
|
41
|
+
error: {
|
|
42
|
+
message: failure.message
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
output.error(failure.message, {
|
|
47
|
+
exit: failure.exit
|
|
48
|
+
});
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
/** Runs the step sequence read-only and gathers the plan a dry run reports. */ async function collectPlan(options, spec) {
|
|
@@ -42,7 +53,10 @@ import { deploymentPlanToJson, isDeployable, renderDeploymentPlan } from './depl
|
|
|
42
53
|
await spec.run(options, reporter);
|
|
43
54
|
const plan = {
|
|
44
55
|
checks: reporter.results,
|
|
56
|
+
config: reporter.results.find((check)=>check.config)?.config ?? null,
|
|
57
|
+
exposes: reporter.results.find((check)=>check.exposes)?.exposes ?? [],
|
|
45
58
|
files: [],
|
|
59
|
+
isSingleton: reporter.results.find((check)=>check.isSingleton !== undefined)?.isSingleton,
|
|
46
60
|
target: reporter.results.find((check)=>check.target)?.target ?? null,
|
|
47
61
|
type: spec.type,
|
|
48
62
|
version: reporter.results.find((check)=>check.version)?.version ?? null
|
|
@@ -58,21 +72,26 @@ import { deploymentPlanToJson, isDeployable, renderDeploymentPlan } from './depl
|
|
|
58
72
|
exit: failed?.exitCode ?? 1
|
|
59
73
|
});
|
|
60
74
|
}
|
|
61
|
-
function
|
|
62
|
-
const noun = type === 'coreApp' ? 'application' : 'studio';
|
|
75
|
+
/** The one failure diagnosis both the stderr message and the `--json` envelope read. */ function normalizeFailure(error, type) {
|
|
63
76
|
// Ctrl+C on an interactive prompt isn't a real failure
|
|
64
77
|
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
65
|
-
|
|
66
|
-
exit: 1
|
|
67
|
-
|
|
68
|
-
|
|
78
|
+
return {
|
|
79
|
+
exit: 1,
|
|
80
|
+
message: 'Deployment cancelled by user'
|
|
81
|
+
};
|
|
69
82
|
}
|
|
70
|
-
// A failed check already carries its own message and exit code
|
|
71
|
-
if (error instanceof CLIError)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
// A failed check already carries its own message and exit code
|
|
84
|
+
if (error instanceof CLIError) {
|
|
85
|
+
return {
|
|
86
|
+
exit: error.oclif?.exit ?? 1,
|
|
87
|
+
message: error.message
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
deployDebug(`Error deploying ${type === 'coreApp' ? 'application' : 'studio'}`, error);
|
|
91
|
+
return {
|
|
92
|
+
exit: 1,
|
|
93
|
+
message: `Error deploying ${type === 'coreApp' ? 'application' : 'studio'}: ${getErrorMessage(error)}`
|
|
94
|
+
};
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
//# sourceMappingURL=deployRunner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/deploy/deployRunner.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/deploy/deployRunner.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {type Output} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {type DeployedExpose} from '@sanity/workbench-cli/deploy'\n\nimport {createCollectingReporter, createFailFastReporter} from '../../util/checks.js'\nimport {toStderrOutput} from '../../util/toStderrOutput.js'\nimport {type DeployCheck, type DeployCheckReporter, type DeployTarget} from './deployChecks.js'\nimport {deployDebug} from './deployDebug.js'\nimport {\n type DeploymentFile,\n type DeploymentPlan,\n deploymentPlanToJson,\n isDeployable,\n renderDeploymentPlan,\n} from './deploymentPlan.js'\nimport {type DeployAppOptions} from './types.js'\n\n/** What a real deploy produced — the payload `--json` reports. */\nexport interface DeployResult {\n applicationType: 'coreApp' | 'studio'\n /** Installed framework version the deploy used (`sanity` or `@sanity/sdk-react`). */\n applicationVersion: string\n /**\n * The deployed application/studio, in the same shape the dry-run plan reports\n * so the two modes can't drift; `null` for a config-only singleton deploy.\n */\n target: DeployTarget | null\n\n /** Media-library config summary, when a singleton config deployed. */\n config?: string\n /** Workbench views and services registered with the deploy. */\n exposes?: DeployedExpose[]\n /** Set when a media-library singleton deployed its config. */\n installationId?: string\n /** The app's explicit `isSingleton` flag; omitted when the app doesn't set it. */\n isSingleton?: boolean\n}\n\n/**\n * The parts of a deploy that differ between core apps and studios. The shared\n * sequence — mode selection, error handling, the dry-run plan, `--json` — lives\n * in `runDeploy`.\n */\nexport interface DeploySpec {\n /** Files a real deploy would upload, listed only for the dry-run plan. */\n listFiles: (options: DeployAppOptions) => Promise<DeploymentFile[]>\n /** The step sequence; every step reports through `reporter`. */\n run: (options: DeployAppOptions, reporter: DeployCheckReporter) => Promise<DeployResult | void>\n type: 'coreApp' | 'studio'\n}\n\n/**\n * Runs a deploy in the mode the flags select: a real deploy fails fast and\n * mutates, `--dry-run` drives the same `run` sequence read-only and renders a\n * plan, and `--json` emits the same information as machine-readable JSON.\n */\nexport async function runDeploy(options: DeployAppOptions, spec: DeploySpec): Promise<void> {\n const {output} = options\n const json = !!options.flags.json\n const emitJson = (payload: unknown) => output.log(JSON.stringify(payload, null, 2))\n\n // The JSON payload owns stdout, so the run's progress logs go to stderr; only\n // the final JSON.stringify writes to stdout.\n const runOptions = json ? {...options, output: toStderrOutput(output)} : options\n\n try {\n if (options.flags['dry-run']) {\n const plan = await collectPlan(runOptions, spec)\n if (json) emitJson(deploymentPlanToJson(plan))\n else renderDeploymentPlan(plan, output)\n exitIfBlocked(plan, output)\n return\n }\n\n const result = await spec.run(runOptions, createFailFastReporter(runOptions.output))\n if (json && result) emitJson({deployed: true, ...result})\n } catch (error) {\n const failure = normalizeFailure(error, spec.type)\n // A blocked dry run reaches this catch too (its exit throws) and already\n // printed its plan, so only a real deploy adds the {deployed: false} envelope.\n if (json && !options.flags['dry-run']) {\n emitJson({deployed: false, error: {message: failure.message}})\n }\n output.error(failure.message, {exit: failure.exit})\n }\n}\n\n/** Runs the step sequence read-only and gathers the plan a dry run reports. */\nasync function collectPlan(options: DeployAppOptions, spec: DeploySpec): Promise<DeploymentPlan> {\n const reporter = createCollectingReporter<DeployCheck>()\n await spec.run(options, reporter)\n const plan: DeploymentPlan = {\n checks: reporter.results,\n config: reporter.results.find((check) => check.config)?.config ?? null,\n exposes: reporter.results.find((check) => check.exposes)?.exposes ?? [],\n files: [],\n isSingleton: reporter.results.find((check) => check.isSingleton !== undefined)?.isSingleton,\n target: reporter.results.find((check) => check.target)?.target ?? null,\n type: spec.type,\n version: reporter.results.find((check) => check.version)?.version ?? null,\n }\n // A blocked deploy uploads nothing, so only enumerate files for a deployable plan.\n if (isDeployable(plan)) plan.files = await spec.listFiles(options)\n return plan\n}\n\n/** Exits like a real (fail-fast) deploy would, on the first failing check's exit code. */\nfunction exitIfBlocked(plan: DeploymentPlan, output: Output): void {\n if (isDeployable(plan)) return\n const failed = plan.checks.find((check) => check.status === 'fail')\n output.error('Deploy blocked by failing checks.', {exit: failed?.exitCode ?? 1})\n}\n\n/** The one failure diagnosis both the stderr message and the `--json` envelope read. */\nfunction normalizeFailure(\n error: unknown,\n type: 'coreApp' | 'studio',\n): {exit: number; message: string} {\n // Ctrl+C on an interactive prompt isn't a real failure\n if (error instanceof Error && error.name === 'ExitPromptError') {\n return {exit: 1, message: 'Deployment cancelled by user'}\n }\n // A failed check already carries its own message and exit code\n if (error instanceof CLIError) {\n return {exit: error.oclif?.exit ?? 1, message: error.message}\n }\n deployDebug(`Error deploying ${type === 'coreApp' ? 'application' : 'studio'}`, error)\n return {\n exit: 1,\n message: `Error deploying ${type === 'coreApp' ? 'application' : 'studio'}: ${getErrorMessage(error)}`,\n }\n}\n"],"names":["CLIError","getErrorMessage","createCollectingReporter","createFailFastReporter","toStderrOutput","deployDebug","deploymentPlanToJson","isDeployable","renderDeploymentPlan","runDeploy","options","spec","output","json","flags","emitJson","payload","log","JSON","stringify","runOptions","plan","collectPlan","exitIfBlocked","result","run","deployed","error","failure","normalizeFailure","type","message","exit","reporter","checks","results","config","find","check","exposes","files","isSingleton","undefined","target","version","listFiles","failed","status","exitCode","Error","name","oclif"],"mappings":"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"}
|