Skip to content

Commit

Permalink
feat(cli): add unattended mode flag to sanity undeploy
Browse files Browse the repository at this point in the history
  • Loading branch information
nkgentile committed Dec 22, 2024
1 parent ddb65fa commit 84431a2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {beforeEach, describe, expect, it, type Mock, vi} from 'vitest'

import {type UserApplication} from '../helpers'
import * as _helpers from '../helpers'
import undeployStudioAction from '../undeployAction'
import undeployStudioAction, {type UndeployStudioActionFlags} from '../undeployAction'

// Mock dependencies
vi.mock('../helpers')
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('undeployStudioAction', () => {
it('does nothing if there is no user application', async () => {
helpers.getUserApplication.mockResolvedValueOnce(null)

await undeployStudioAction({} as CliCommandArguments<Record<string, unknown>>, mockContext)
await undeployStudioAction({} as CliCommandArguments<UndeployStudioActionFlags>, mockContext)

expect(mockContext.output.print).toHaveBeenCalledWith(
'Your project has not been assigned a studio hostname',
Expand All @@ -75,7 +75,7 @@ describe('undeployStudioAction', () => {
true,
) // User confirms

await undeployStudioAction({} as CliCommandArguments<Record<string, unknown>>, mockContext)
await undeployStudioAction({} as CliCommandArguments<UndeployStudioActionFlags>, mockContext)

expect(mockContext.prompt.single).toHaveBeenCalledWith({
type: 'confirm',
Expand All @@ -97,7 +97,7 @@ describe('undeployStudioAction', () => {
false,
) // User cancels

await undeployStudioAction({} as CliCommandArguments<Record<string, unknown>>, mockContext)
await undeployStudioAction({} as CliCommandArguments<UndeployStudioActionFlags>, mockContext)

expect(mockContext.prompt.single).toHaveBeenCalledWith({
type: 'confirm',
Expand All @@ -107,6 +107,32 @@ describe('undeployStudioAction', () => {
expect(helpers.deleteUserApplication).not.toHaveBeenCalled()
})

it(`if running in unattended mode, it doesn't prompt the user for confirmation`, async () => {
helpers.getUserApplication.mockResolvedValueOnce(mockApplication)
helpers.deleteUserApplication.mockResolvedValueOnce(undefined)
;(mockContext.prompt.single as Mock<typeof mockContext.prompt.single>).mockResolvedValueOnce(
true,
) // User confirms

await undeployStudioAction(
{extOptions: {yes: true}} as CliCommandArguments<UndeployStudioActionFlags>,
mockContext,
)

expect(mockContext.prompt.single).not.toHaveBeenCalledWith({
type: 'confirm',
default: false,
message: expect.stringContaining('undeploy'),
})
expect(helpers.deleteUserApplication).toHaveBeenCalledWith({
client: expect.anything(),
applicationId: 'app-id',
})
expect(mockContext.output.print).toHaveBeenCalledWith(
expect.stringContaining('Studio undeploy scheduled.'),
)
})

it('handles errors during the undeploy process', async () => {
const errorMessage = 'Example error'
helpers.getUserApplication.mockResolvedValueOnce(mockApplication)
Expand All @@ -116,7 +142,7 @@ describe('undeployStudioAction', () => {
) // User confirms

await expect(

Check failure on line 144 in packages/sanity/src/_internal/cli/actions/deploy/__tests__/undeployAction.test.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 20)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > handles errors during the undeploy process

AssertionError: expected [Function] to throw error including 'Example error' but got 'Cannot read properties of undefined (…' Expected: "Example error" Received: "Cannot read properties of undefined (reading 'yes')" ❯ actions/deploy/__tests__/undeployAction.test.ts:144:5

Check failure on line 144 in packages/sanity/src/_internal/cli/actions/deploy/__tests__/undeployAction.test.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 18)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > handles errors during the undeploy process

AssertionError: expected [Function] to throw error including 'Example error' but got 'Cannot read properties of undefined (…' Expected: "Example error" Received: "Cannot read properties of undefined (reading 'yes')" ❯ actions/deploy/__tests__/undeployAction.test.ts:144:5
undeployStudioAction({} as CliCommandArguments<Record<string, unknown>>, mockContext),
undeployStudioAction({} as CliCommandArguments<UndeployStudioActionFlags>, mockContext),
).rejects.toThrow(errorMessage)

expect(mockContext.output.spinner('').fail).toHaveBeenCalled()
Expand Down
31 changes: 23 additions & 8 deletions packages/sanity/src/_internal/cli/actions/deploy/undeployAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import {deleteUserApplication, getUserApplication} from './helpers'

const debug = debugIt.extend('undeploy')

export interface UndeployStudioActionFlags {
yes?: boolean
y?: boolean
}

export default async function undeployStudioAction(
_: CliCommandArguments<Record<string, unknown>>,
args: CliCommandArguments<UndeployStudioActionFlags>,
context: CliCommandContext,
): Promise<void> {
const {apiClient, chalk, output, prompt, cliConfig} = context
const flags = args.extOptions

const client = apiClient({
requireUser: true,
Expand Down Expand Up @@ -37,16 +43,25 @@ export default async function undeployStudioAction(
output.print('')

const url = `https://${chalk.yellow(userApplication.appHost)}.sanity.studio`
const shouldUndeploy = await prompt.single({
type: 'confirm',
default: false,
message: `This will undeploy ${url} and make it unavailable for your users.

/**
* Unattended mode means that if there are any prompts it will use `YES` for them but will no change anything that doesn't have a prompt
*/
const unattendedMode = Boolean(flags.yes || flags.y)

Check failure on line 50 in packages/sanity/src/_internal/cli/actions/deploy/undeployAction.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 20)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > prompts the user for confirmation and undeploys if confirmed

TypeError: Cannot read properties of undefined (reading 'yes') ❯ Module.undeployStudioAction [as default] actions/deploy/undeployAction.ts:50:40 ❯ actions/deploy/__tests__/undeployAction.test.ts:78:5

Check failure on line 50 in packages/sanity/src/_internal/cli/actions/deploy/undeployAction.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 20)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > does not undeploy if the user cancels the prompt

TypeError: Cannot read properties of undefined (reading 'yes') ❯ Module.undeployStudioAction [as default] actions/deploy/undeployAction.ts:50:40 ❯ actions/deploy/__tests__/undeployAction.test.ts:100:5

Check failure on line 50 in packages/sanity/src/_internal/cli/actions/deploy/undeployAction.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 18)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > prompts the user for confirmation and undeploys if confirmed

TypeError: Cannot read properties of undefined (reading 'yes') ❯ Module.undeployStudioAction [as default] actions/deploy/undeployAction.ts:50:40 ❯ actions/deploy/__tests__/undeployAction.test.ts:78:5

Check failure on line 50 in packages/sanity/src/_internal/cli/actions/deploy/undeployAction.ts

View workflow job for this annotation

GitHub Actions / Test (ubuntu-latest / node 18)

actions/deploy/__tests__/undeployAction.test.ts > undeployStudioAction > does not undeploy if the user cancels the prompt

TypeError: Cannot read properties of undefined (reading 'yes') ❯ Module.undeployStudioAction [as default] actions/deploy/undeployAction.ts:50:40 ❯ actions/deploy/__tests__/undeployAction.test.ts:100:5

// If it is in unattended mode, we don't want to prompt
if (!unattendedMode) {
const shouldUndeploy = await prompt.single({
type: 'confirm',
default: false,
message: `This will undeploy ${url} and make it unavailable for your users.
The hostname will be available for anyone to claim.
Are you ${chalk.red('sure')} you want to undeploy?`.trim(),
})
})

if (!shouldUndeploy) {
return
if (!shouldUndeploy) {
return
}
}

spinner = output.spinner('Undeploying studio').start()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,23 @@ import {
type CliCommandDefinition,
} from '@sanity/cli'

import {type UndeployStudioActionFlags} from '../../actions/deploy/undeployAction'

const helpText = `
Options
-y, --yes Unattended mode, answers "yes" to any "yes/no" prompt and otherwise uses defaults
Examples
sanity undeploy
sanity undeploy --yes
`

const undeployCommand: CliCommandDefinition = {
name: 'undeploy',
signature: '',
description: 'Removes the deployed Sanity Studio from Sanity hosting',
action: async (
args: CliCommandArguments<Record<string, unknown>>,
args: CliCommandArguments<UndeployStudioActionFlags>,
context: CliCommandContext,
) => {
const mod = await import('../../actions/deploy/undeployAction')
Expand Down

0 comments on commit 84431a2

Please sign in to comment.