Skip to content

Commit 9d6380a

Browse files
Avoid duplicate prompts after environment confirmation
1 parent cb5f151 commit 9d6380a

6 files changed

Lines changed: 101 additions & 9 deletions

File tree

‎packages/cli/README.md‎

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10920,6 +10920,11 @@ DESCRIPTION
1092010920
"message": {
1092110921
"type": "string"
1092210922
},
10923+
"code": {
10924+
"type": "string",
10925+
"minLength": 1,
10926+
"description": "A stable error code, included only when known."
10927+
},
1092310928
"tryMessage": {
1092410929
"type": "string"
1092510930
},
@@ -10960,7 +10965,9 @@ DESCRIPTION
1096010965
"additionalProperties": false
1096110966
}
1096210967
},
10963-
"details": {}
10968+
"details": {
10969+
"description": "Selected domain details, preserving native API payloads such as GraphQL errors, extensions, and data."
10970+
}
1096410971
},
1096510972
"required": [
1096610973
"type",
@@ -10978,6 +10985,9 @@ DESCRIPTION
1097810985
"message": {
1097910986
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1098010987
},
10988+
"code": {
10989+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
10990+
},
1098110991
"tryMessage": {
1098210992
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1098310993
},
@@ -11010,6 +11020,9 @@ DESCRIPTION
1101011020
"message": {
1101111021
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1101211022
},
11023+
"code": {
11024+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
11025+
},
1101311026
"tryMessage": {
1101411027
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1101511028
},
@@ -11319,6 +11332,11 @@ DESCRIPTION
1131911332
"message": {
1132011333
"type": "string"
1132111334
},
11335+
"code": {
11336+
"type": "string",
11337+
"minLength": 1,
11338+
"description": "A stable error code, included only when known."
11339+
},
1132211340
"tryMessage": {
1132311341
"type": "string"
1132411342
},
@@ -11359,7 +11377,9 @@ DESCRIPTION
1135911377
"additionalProperties": false
1136011378
}
1136111379
},
11362-
"details": {}
11380+
"details": {
11381+
"description": "Selected domain details, preserving native API payloads such as GraphQL errors, extensions, and data."
11382+
}
1136311383
},
1136411384
"required": [
1136511385
"type",
@@ -11377,6 +11397,9 @@ DESCRIPTION
1137711397
"message": {
1137811398
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1137911399
},
11400+
"code": {
11401+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
11402+
},
1138011403
"tryMessage": {
1138111404
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1138211405
},
@@ -11409,6 +11432,9 @@ DESCRIPTION
1140911432
"message": {
1141011433
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1141111434
},
11435+
"code": {
11436+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
11437+
},
1141211438
"tryMessage": {
1141311439
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1141411440
},
@@ -11756,6 +11782,11 @@ DESCRIPTION
1175611782
"message": {
1175711783
"type": "string"
1175811784
},
11785+
"code": {
11786+
"type": "string",
11787+
"minLength": 1,
11788+
"description": "A stable error code, included only when known."
11789+
},
1175911790
"tryMessage": {
1176011791
"type": "string"
1176111792
},
@@ -11796,7 +11827,9 @@ DESCRIPTION
1179611827
"additionalProperties": false
1179711828
}
1179811829
},
11799-
"details": {}
11830+
"details": {
11831+
"description": "Selected domain details, preserving native API payloads such as GraphQL errors, extensions, and data."
11832+
}
1180011833
},
1180111834
"required": [
1180211835
"type",
@@ -11814,6 +11847,9 @@ DESCRIPTION
1181411847
"message": {
1181511848
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1181611849
},
11850+
"code": {
11851+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
11852+
},
1181711853
"tryMessage": {
1181811854
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1181911855
},
@@ -11846,6 +11882,9 @@ DESCRIPTION
1184611882
"message": {
1184711883
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1184811884
},
11885+
"code": {
11886+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
11887+
},
1184911888
"tryMessage": {
1185011889
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1185111890
},
@@ -12196,6 +12235,11 @@ DESCRIPTION
1219612235
"message": {
1219712236
"type": "string"
1219812237
},
12238+
"code": {
12239+
"type": "string",
12240+
"minLength": 1,
12241+
"description": "A stable error code, included only when known."
12242+
},
1219912243
"tryMessage": {
1220012244
"type": "string"
1220112245
},
@@ -12236,7 +12280,9 @@ DESCRIPTION
1223612280
"additionalProperties": false
1223712281
}
1223812282
},
12239-
"details": {}
12283+
"details": {
12284+
"description": "Selected domain details, preserving native API payloads such as GraphQL errors, extensions, and data."
12285+
}
1224012286
},
1224112287
"required": [
1224212288
"type",
@@ -12254,6 +12300,9 @@ DESCRIPTION
1225412300
"message": {
1225512301
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1225612302
},
12303+
"code": {
12304+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
12305+
},
1225712306
"tryMessage": {
1225812307
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1225912308
},
@@ -12286,6 +12335,9 @@ DESCRIPTION
1228612335
"message": {
1228712336
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/message"
1228812337
},
12338+
"code": {
12339+
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/code"
12340+
},
1228912341
"tryMessage": {
1229012342
"$ref": "#/definitions/ThemeEnvironment/anyOf/1/properties/error/anyOf/0/properties/tryMessage"
1229112343
},

‎packages/cli/oclif.manifest.json‎

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

‎packages/theme/src/cli/commands/theme/duplicate.test.ts‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import {withCapturedStandardStreams} from '@shopify/cli-kit/node/testing/output'
88
import {outputWarn} from '@shopify/cli-kit/node/output'
99
import {runWithCommandEventsForCommand} from '@shopify/cli-kit/node/command-events'
1010
import {loadEnvironment} from '@shopify/cli-kit/node/environments'
11-
import {describe, expect, test, vi} from 'vitest'
11+
import {afterEach, describe, expect, test, vi} from 'vitest'
1212

1313
vi.mock('@shopify/cli-kit/node/session')
1414
vi.mock('@shopify/cli-kit/node/themes/api')
1515
vi.mock('@shopify/cli-kit/node/environments')
1616
vi.mock('../../utilities/theme-selector.js')
1717

18+
afterEach(() => vi.unstubAllEnvs())
19+
1820
const originalTheme = {id: 1, name: 'Original', role: 'unpublished', processing: false, createdAtRuntime: false}
1921
const copiedTheme = {...originalTheme, id: 2, name: 'Copy'}
2022
const session = {token: 'token', storeFqdn: 'test.myshopify.com'}
@@ -106,6 +108,28 @@ describe('theme duplicate JSON output', () => {
106108
})
107109
})
108110

111+
test.each([['staging'], ['staging', 'production']])(
112+
'does not prompt for CI environments: %j',
113+
async (...environments) => {
114+
vi.stubEnv('CI', '1')
115+
vi.mocked(ensureAuthenticatedThemes).mockResolvedValue(session)
116+
vi.mocked(loadEnvironment).mockResolvedValue({store: session.storeFqdn, password: 'token', theme: '1'})
117+
vi.mocked(findThemeById).mockResolvedValue(originalTheme)
118+
vi.mocked(themeDuplicate).mockResolvedValue({theme: copiedTheme, userErrors: []})
119+
const config = new Config({root: __dirname})
120+
await config.load()
121+
const argv = ['--json', ...environments.flatMap((environment) => ['-e', environment])]
122+
123+
await withCapturedStandardStreams(async ({stdout}) => {
124+
await runWithCommandEventsForCommand(argv, () => new Duplicate(argv, config).run())
125+
expect(JSON.parse(stdout())).toEqual({
126+
environments: environments.map((environment) => ({environment, result: publicResult})),
127+
})
128+
})
129+
expect(themeDuplicate).toHaveBeenCalledTimes(environments.length)
130+
},
131+
)
132+
109133
test('does not write a result when the API throws', async () => {
110134
vi.mocked(findThemeById).mockResolvedValue(originalTheme)
111135
vi.mocked(themeDuplicate).mockRejectedValue(new Error('Network failure'))

‎packages/theme/src/cli/commands/theme/duplicate.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {globalFlags, jsonFlag, requiredIfNonInteractive} from '@shopify/cli-kit/
99
import {AdminSession} from '@shopify/cli-kit/node/session'
1010
import {isCI} from '@shopify/cli-kit/node/system'
1111
import type {OutputFlags} from '@oclif/core/interfaces'
12-
import type {NonTTYFlagRequirement} from '@shopify/cli-kit/node/base-command'
12+
import type {FlagOutput, NonTTYFlagRequirement} from '@shopify/cli-kit/node/base-command'
1313

1414
export default class Duplicate extends ThemeCommand {
1515
static get jsonOutputSchema() {
@@ -68,8 +68,12 @@ Successful JSON results include \`status\`, \`changed\`, and explicit \`original
6868

6969
async command(flags: OutputFlags<typeof Duplicate.flags>, adminSession: AdminSession, multiEnvironment = false) {
7070
configureCLIEnvironment(flags)
71-
const result = await duplicate(adminSession, flags.theme, flags)
71+
const result = await duplicate(adminSession, flags.theme, flags, multiEnvironment)
7272
if (flags.json && multiEnvironment) return themeDuplicateJsonResult(result)
7373
renderThemeDuplicateResult(result, flags.json ? 'json' : 'text')
7474
}
75+
76+
protected requiresEnvironmentConfirmation(flags: FlagOutput): boolean {
77+
return !isCI() && super.requiresEnvironmentConfirmation(flags)
78+
}
7579
}

‎packages/theme/src/cli/services/duplicate.test.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,17 @@ describe('duplicate', () => {
108108
expect(renderSuccess).toHaveBeenCalled()
109109
})
110110

111+
test('does not prompt again after environment batch confirmation', async () => {
112+
vi.mocked(isCI).mockReturnValue(false)
113+
vi.mocked(findThemeById).mockResolvedValue(theme)
114+
vi.mocked(themeDuplicate).mockResolvedValue({theme: duplicatedTheme, userErrors: []})
115+
116+
await executeDuplicate(session, '1', {}, true)
117+
118+
expect(renderConfirmationPrompt).not.toHaveBeenCalled()
119+
expect(themeDuplicate).toHaveBeenCalledWith(1, undefined, session)
120+
})
121+
111122
test('does not prompt for confirmation in CI environment', async () => {
112123
// Given
113124
vi.mocked(isCI).mockReturnValue(true)

‎packages/theme/src/cli/services/duplicate.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ export async function duplicate(
1515
adminSession: AdminSession,
1616
themeId: string | undefined,
1717
flags: DuplicateOptions,
18+
multiEnvironment = false,
1819
): Promise<ThemeDuplicateResult> {
1920
const {name, force} = flags
20-
const noPrompts = isCI() || force
21+
const noPrompts = isCI() || Boolean(force) || multiEnvironment
2122

2223
if (noPrompts && !themeId) {
2324
return {status: 'missing-theme-id'}

0 commit comments

Comments
 (0)