Skip to content

Commit

Permalink
fix: save appId to experimental config instead
Browse files Browse the repository at this point in the history
  • Loading branch information
cngonzalez committed Feb 13, 2025
1 parent f1ed838 commit 4672654
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 53 deletions.
2 changes: 1 addition & 1 deletion packages/@sanity/cli/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ export interface CliConfig {
*/
__experimental_coreAppConfiguration?: {
appLocation?: string
appHost?: string
appId?: string
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ describe('deployStudioAction', () => {
},
// eslint-disable-next-line camelcase
__experimental_coreAppConfiguration: {
appHost: 'core-app-host',
appId: 'core-app-id',
},
} as CliConfig,
}
Expand Down
42 changes: 25 additions & 17 deletions packages/sanity/src/_internal/cli/actions/deploy/deployAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {shouldAutoUpdate} from '../../util/shouldAutoUpdate'
import buildSanityStudio, {type BuildSanityStudioCommandFlags} from '../build/buildAction'
import {extractManifestSafe} from '../manifest/extractManifestAction'
import {
type BaseConfigOptions,
checkDir,
createDeployment,
debug,
Expand Down Expand Up @@ -37,11 +38,10 @@ export default async function deployStudioAction(

const installedSanityVersion = await getInstalledSanityVersion()
const configStudioHost = cliConfig && 'studioHost' in cliConfig && cliConfig.studioHost
const appHost =
const appId =
cliConfig &&
'__experimental_coreAppConfiguration' in cliConfig &&
cliConfig.__experimental_coreAppConfiguration?.appHost
const hostValue = isCoreApp ? appHost : configStudioHost
cliConfig.__experimental_coreAppConfiguration?.appId

const client = apiClient({
requireUser: true,
Expand Down Expand Up @@ -81,19 +81,27 @@ export default async function deployStudioAction(
let userApplication: UserApplication

try {
// If the user has provided a studioHost in the config, use that
if (hostValue) {
userApplication = await getOrCreateUserApplicationFromConfig({
client,
context,
spinner,
appHost: hostValue,
})
const configParams: BaseConfigOptions & {
appHost?: string
appId?: string
} = {
client,
context,
spinner,
}

if (isCoreApp && appId) {
configParams.appId = appId
} else if (configStudioHost) {
configParams.appHost = configStudioHost
}
// If the user has provided a studioHost / appId in the config, use that
if (configStudioHost || appId) {
userApplication = await getOrCreateUserApplicationFromConfig(configParams)
} else {
const createAppParams = {client, context, spinner}
userApplication = isCoreApp
? await getOrCreateCoreApplication(createAppParams)
: await getOrCreateStudio(createAppParams)
? await getOrCreateCoreApplication({client, context, spinner})
: await getOrCreateStudio({client, context, spinner})
}
} catch (err) {
if (err.message) {
Expand Down Expand Up @@ -165,14 +173,14 @@ export default async function deployStudioAction(
`\nSuccess! ${isCoreApp ? 'Application deployed' : `Studio deployed to ${chalk.cyan(location)}`}`,
)

if (!hostValue) {
if ((isCoreApp && !appId) || (!isCoreApp && !configStudioHost)) {
output.print(
`\nAdd ${chalk.cyan(isCoreApp ? `appHost: '${userApplication.appHost}'` : `studioHost: '${userApplication.appHost}'`)}`,
`\nAdd ${chalk.cyan(isCoreApp ? `appId: '${userApplication.id}'` : `studioHost: '${userApplication.appHost}'`)}`,
)
output.print(
`to ${isCoreApp ? '__experimental_coreAppConfiguration' : 'defineCliConfig root properties'} in sanity.cli.js or sanity.cli.ts`,
)
output.print(`to avoid prompting ${isCoreApp ? 'for hostname' : ''} on next deploy.`)
output.print(`to avoid prompting ${isCoreApp ? '' : 'for hostname'} on next deploy.`)
}
} catch (err) {
spinner.fail()
Expand Down
110 changes: 76 additions & 34 deletions packages/sanity/src/_internal/cli/actions/deploy/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,25 @@ export interface GetUserApplicationsOptions {

export interface GetUserApplicationOptions extends GetUserApplicationsOptions {
appHost?: string
appId?: string
}
export async function getUserApplication({
client,
appHost,
organizationId,
appId,
}: GetUserApplicationOptions): Promise<UserApplication | null> {
let query
if (organizationId) {
query = {organizationId, appHost: appHost ?? '', appType: 'coreApp'}
let uri = '/user-applications'
if (appId) {
uri = `/user-applications/${appId}`
query = {appType: 'coreApp'}
} else if (appHost) {
query = {appHost}
} else {
query = {default: 'true'}
}
try {
return await client.request({
uri: '/user-applications',
query,
})
return await client.request({uri, query})
} catch (e) {
if (e?.statusCode === 404) {
return null
Expand Down Expand Up @@ -336,31 +336,32 @@ export async function getOrCreateCoreApplication({
const response = await promise

spinner.succeed()
// Placeholder -- not sure what ultimate URL will be
output.print(`Deployed application to CORE`)

return response
}

/**
* This function handles the logic for managing user applications when
* studioHost is provided in the CLI config.
*
* @internal
*/
export async function getOrCreateUserApplicationFromConfig({
export interface BaseConfigOptions {
client: SanityClient
context: Pick<CliCommandContext, 'output' | 'prompt' | 'cliConfig'>
spinner: ReturnType<CliOutputter['spinner']>
}

interface StudioConfigOptions extends BaseConfigOptions {
appHost: string
}

interface CoreAppConfigOptions extends BaseConfigOptions {
appId?: string
}

async function getOrCreateStudioFromConfig({
client,
context,
spinner,
appHost,
}: GetOrCreateUserApplicationOptions & {
appHost: string
}): Promise<UserApplication> {
const {output, cliConfig} = context
const isCoreApp = cliConfig && '__experimental_coreAppConfiguration' in cliConfig
const organizationId = isCoreApp ? cliConfig?.api?.organizationId : undefined
}: StudioConfigOptions): Promise<UserApplication> {
const {output} = context
// if there is already an existing user-app, then just return it
const existingUserApplication = await getUserApplication({client, appHost, organizationId})
const existingUserApplication = await getUserApplication({client, appHost})

// Complete the spinner so prompt can properly work
spinner.succeed()
Expand All @@ -369,14 +370,6 @@ export async function getOrCreateUserApplicationFromConfig({
return existingUserApplication
}

// core apps cannot arbitrarily create their own appHosts,
// so send the user to a get or create function
if (isCoreApp) {
output.print('The appHost provided in your configuration is not recognized.')
output.print('Checking existing applications...')
getOrCreateCoreApplication({client, context, spinner})
}

output.print('Your project has not been assigned a studio hostname.')
output.print(`Creating https://${appHost}.sanity.studio`)
output.print('')
Expand All @@ -389,20 +382,69 @@ export async function getOrCreateUserApplicationFromConfig({
type: 'studio',
})
spinner.succeed()

return response
} catch (e) {
spinner.fail()
// if the name is taken, it should return a 409 so we relay to the user
if ([402, 409].includes(e?.statusCode)) {
throw new Error(e?.response?.body?.message || 'Bad request') // just in case
}

debug('Error creating user application from config', e)
// otherwise, it's a fatal error
throw e
}
}

async function getOrCreateCoreAppFromConfig({
client,
context,
spinner,
appId,
}: CoreAppConfigOptions): Promise<UserApplication> {
const {output} = context
const organizationId = context.cliConfig?.api?.organizationId

if (appId) {
const existingUserApplication = await getUserApplication({client, appId, organizationId})
spinner.succeed()

if (existingUserApplication) {
return existingUserApplication
}
}

// core apps cannot arbitrarily create ids or hosts, so send them to create option
output.print('The appId provided in your configuration is not recognized.')
output.print('Checking existing applications...')
return getOrCreateCoreApplication({client, context, spinner})
}

/**
* This function handles the logic for managing user applications when
* studioHost or appId is provided in the CLI config.
*
* @internal
*/
export async function getOrCreateUserApplicationFromConfig(
options: BaseConfigOptions & {
appHost?: string
appId?: string
},
): Promise<UserApplication> {
const {context} = options
const isCoreApp = context.cliConfig && '__experimental_coreAppConfiguration' in context.cliConfig

if (isCoreApp) {
return getOrCreateCoreAppFromConfig(options)
}

if (!options.appHost) {
throw new Error('Studio host was detected, but is invalid')
}

return getOrCreateStudioFromConfig({...options, appHost: options.appHost})
}

export interface CreateDeploymentOptions {
client: SanityClient
applicationId: string
Expand Down

0 comments on commit 4672654

Please sign in to comment.