Skip to content

Commit 8eb9a36

Browse files
authored
Merge pull request #6291 from Shopify/jb-multi-env-unsupported
Refactor commands that don't support multiple environments to use ThemeCommand's run method
2 parents cd188b4 + 809d355 commit 8eb9a36

File tree

11 files changed

+93
-52
lines changed

11 files changed

+93
-52
lines changed

.changeset/slow-buckets-battle.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@shopify/theme': minor
3+
'@shopify/cli': minor
4+
---
5+
6+
Display warning when multiple environment flags are provided to profile, metafields pull, open, dev, or console theme commands

packages/cli/oclif.manifest.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5212,6 +5212,7 @@
52125212
"hiddenAliases": [
52135213
],
52145214
"id": "theme:console",
5215+
"multiEnvironmentsFlags": null,
52155216
"pluginAlias": "@shopify/cli",
52165217
"pluginName": "@shopify/cli",
52175218
"pluginType": "core",
@@ -5524,6 +5525,7 @@
55245525
"hiddenAliases": [
55255526
],
55265527
"id": "theme:dev",
5528+
"multiEnvironmentsFlags": null,
55275529
"pluginAlias": "@shopify/cli",
55285530
"pluginName": "@shopify/cli",
55295531
"pluginType": "core",
@@ -6020,6 +6022,7 @@
60206022
"hiddenAliases": [
60216023
],
60226024
"id": "theme:metafields:pull",
6025+
"multiEnvironmentsFlags": null,
60236026
"pluginAlias": "@shopify/cli",
60246027
"pluginName": "@shopify/cli",
60256028
"pluginType": "core",
@@ -6124,6 +6127,7 @@
61246127
"hiddenAliases": [
61256128
],
61266129
"id": "theme:open",
6130+
"multiEnvironmentsFlags": null,
61276131
"pluginAlias": "@shopify/cli",
61286132
"pluginName": "@shopify/cli",
61296133
"pluginType": "core",
@@ -6275,6 +6279,7 @@
62756279
"hiddenAliases": [
62766280
],
62776281
"id": "theme:profile",
6282+
"multiEnvironmentsFlags": null,
62786283
"pluginAlias": "@shopify/cli",
62796284
"pluginName": "@shopify/cli",
62806285
"pluginType": "core",
@@ -7011,6 +7016,7 @@
70117016
"hiddenAliases": [
70127017
],
70137018
"id": "theme:serve",
7019+
"multiEnvironmentsFlags": null,
70147020
"pluginAlias": "@shopify/cli",
70157021
"pluginName": "@shopify/cli",
70167022
"pluginType": "core",

packages/theme/src/cli/commands/theme/console.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import {themeFlags} from '../../flags.js'
2-
import ThemeCommand from '../../utilities/theme-command.js'
3-
import {ensureThemeStore} from '../../utilities/theme-store.js'
2+
import ThemeCommand, {RequiredFlags} from '../../utilities/theme-command.js'
43
import {ensureReplEnv, initializeRepl} from '../../services/console.js'
54
import {validateThemePassword} from '../../services/flags-validation.js'
65
import {globalFlags} from '@shopify/cli-kit/node/cli'
7-
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
6+
import {AdminSession} from '@shopify/cli-kit/node/session'
87
import {Flags} from '@oclif/core'
98
import {recordEvent} from '@shopify/cli-kit/node/analytics'
9+
import {InferredFlags} from '@oclif/core/interfaces'
10+
11+
type ConsoleFlags = InferredFlags<typeof Console.flags>
1012

1113
export default class Console extends ThemeCommand {
1214
static summary = 'Shopify Liquid REPL (read-eval-print loop) tool'
@@ -33,15 +35,12 @@ export default class Console extends ThemeCommand {
3335
}),
3436
}
3537

36-
async run() {
37-
const {flags} = await this.parse(Console)
38-
39-
validateThemePassword(flags.password)
38+
static multiEnvironmentsFlags: RequiredFlags = null
4039

41-
const store = ensureThemeStore(flags)
40+
async command(flags: ConsoleFlags, adminSession: AdminSession) {
4241
const {url, password: themeAccessPassword} = flags
4342

44-
const adminSession = await ensureAuthenticatedThemes(store, themeAccessPassword)
43+
validateThemePassword(themeAccessPassword)
4544

4645
const {themeId, storePassword} = await ensureReplEnv(adminSession, flags['store-password'])
4746

packages/theme/src/cli/commands/theme/dev.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {themeFlags} from '../../flags.js'
2-
import {ensureThemeStore} from '../../utilities/theme-store.js'
3-
import ThemeCommand, {FlagValues} from '../../utilities/theme-command.js'
2+
import ThemeCommand, {RequiredFlags} from '../../utilities/theme-command.js'
43
import {dev} from '../../services/dev.js'
54
import {DevelopmentThemeManager} from '../../utilities/development-theme-manager.js'
65
import {findOrSelectTheme} from '../../utilities/theme-selector.js'
@@ -10,10 +9,13 @@ import {validateThemePassword} from '../../services/flags-validation.js'
109
import {Flags} from '@oclif/core'
1110
import {globalFlags} from '@shopify/cli-kit/node/cli'
1211
import {Theme} from '@shopify/cli-kit/node/themes/types'
13-
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
1412
import {recordEvent} from '@shopify/cli-kit/node/analytics'
13+
import {AdminSession} from '@shopify/cli-kit/node/session'
14+
import {InferredFlags} from '@oclif/core/interfaces'
1515
import type {ErrorOverlayMode, LiveReload} from '../../utilities/theme-environment/types.js'
1616

17+
type DevFlags = InferredFlags<typeof Dev.flags>
18+
1719
export default class Dev extends ThemeCommand {
1820
static summary =
1921
'Uploads the current theme as a development theme to the connected store, then prints theme editor and preview URLs to your terminal. While running, changes will push to the store in real time.'
@@ -124,30 +126,27 @@ You can run this command only in a directory that matches the [default Shopify t
124126
}),
125127
}
126128

127-
async run(): Promise<void> {
128-
const parsed = await this.parse(Dev)
129-
let flags = parsed.flags as typeof parsed.flags & FlagValues
130-
const {ignore = [], only = []} = flags
131-
132-
validateThemePassword(flags.password)
129+
static multiEnvironmentsFlags: RequiredFlags = null
133130

134-
const store = ensureThemeStore(flags)
135-
const adminSession = await ensureAuthenticatedThemes(store, flags.password)
131+
async command(devFlags: DevFlags, adminSession: AdminSession) {
132+
const {ignore = [], only = []} = devFlags
136133

134+
validateThemePassword(devFlags.password)
137135
recordEvent('theme-command:dev:single-env:authenticated')
138136

139137
let theme: Theme
138+
let flags
140139

141-
if (flags.theme) {
142-
const filter = {filter: {theme: flags.theme}}
140+
if (devFlags.theme) {
141+
const filter = {filter: {theme: devFlags.theme}}
143142
theme = await findOrSelectTheme(adminSession, filter)
144143

145-
flags = {...flags, theme: theme.id.toString()}
144+
flags = {...devFlags, theme: theme.id.toString(), store: adminSession.storeFqdn}
146145
} else {
147146
theme = await new DevelopmentThemeManager(adminSession).findOrCreate()
148-
const overwriteJson = flags['theme-editor-sync'] && theme.createdAtRuntime
147+
const overwriteJson = devFlags['theme-editor-sync'] && theme.createdAtRuntime
149148

150-
flags = {...flags, theme: theme.id.toString(), 'overwrite-json': overwriteJson}
149+
flags = {...devFlags, theme: theme.id.toString(), store: adminSession.storeFqdn, 'overwrite-json': overwriteJson}
151150
}
152151

153152
const confirmed = await ensureLiveThemeConfirmed(theme, 'start development mode')
@@ -158,7 +157,7 @@ You can run this command only in a directory that matches the [default Shopify t
158157
await dev({
159158
adminSession,
160159
directory: flags.path,
161-
store,
160+
store: flags.store,
162161
password: flags.password,
163162
storePassword: flags['store-password'],
164163
theme,

packages/theme/src/cli/commands/theme/language-server.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ export default class LanguageServer extends ThemeCommand {
1313
...globalFlags,
1414
}
1515

16-
async run(): Promise<void> {
17-
await this.parse(LanguageServer)
16+
async command() {
1817
startServer()
1918
}
2019
}

packages/theme/src/cli/commands/theme/metafields/pull.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import {themeFlags} from '../../../flags.js'
22
import {metafieldsPull, MetafieldsPullFlags} from '../../../services/metafields-pull.js'
3-
import ThemeCommand from '../../../utilities/theme-command.js'
3+
import ThemeCommand, {RequiredFlags} from '../../../utilities/theme-command.js'
44
import {globalFlags} from '@shopify/cli-kit/node/cli'
55
import {Flags} from '@oclif/core'
6+
import {InferredFlags} from '@oclif/core/interfaces'
7+
8+
type MetafieldsFlags = InferredFlags<typeof MetafieldsPull.flags>
69

710
export default class MetafieldsPull extends ThemeCommand {
811
static summary = 'Download metafields definitions from your shop into a local file.'
@@ -24,8 +27,9 @@ If the metafields file already exists, it will be overwritten.`
2427
}),
2528
}
2629

27-
async run(): Promise<void> {
28-
const {flags} = await this.parse(MetafieldsPull)
30+
static multiEnvironmentsFlags: RequiredFlags = null
31+
32+
async command(flags: MetafieldsFlags) {
2933
const args: MetafieldsPullFlags = {
3034
path: flags.path,
3135
password: flags.password,

packages/theme/src/cli/commands/theme/open.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import {ensureThemeStore} from '../../utilities/theme-store.js'
2-
import ThemeCommand from '../../utilities/theme-command.js'
1+
import ThemeCommand, {RequiredFlags} from '../../utilities/theme-command.js'
32
import {themeFlags} from '../../flags.js'
43
import {open} from '../../services/open.js'
54
import {Flags} from '@oclif/core'
65
import {globalFlags} from '@shopify/cli-kit/node/cli'
7-
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
6+
import {AdminSession} from '@shopify/cli-kit/node/session'
7+
import {InferredFlags} from '@oclif/core/interfaces'
88

9+
type OpenFlags = InferredFlags<typeof Open.flags>
910
export default class Open extends ThemeCommand {
1011
static summary = 'Opens the preview of your remote theme.'
1112

@@ -43,11 +44,9 @@ export default class Open extends ThemeCommand {
4344
}),
4445
}
4546

46-
async run(): Promise<void> {
47-
const {flags} = await this.parse(Open)
48-
const store = ensureThemeStore(flags)
49-
const adminSession = await ensureAuthenticatedThemes(store, flags.password)
47+
static multiEnvironmentsFlags: RequiredFlags = null
5048

49+
async command(flags: OpenFlags, adminSession: AdminSession) {
5150
await open(adminSession, flags)
5251
}
5352
}

packages/theme/src/cli/commands/theme/package.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {themeFlags} from '../../flags.js'
22
import ThemeCommand from '../../utilities/theme-command.js'
33
import {packageTheme} from '../../services/package.js'
44
import {globalFlags} from '@shopify/cli-kit/node/cli'
5+
import {InferredFlags} from '@oclif/core/interfaces'
56

7+
type PackageFlags = InferredFlags<typeof Package.flags>
68
export default class Package extends ThemeCommand {
79
static summary = 'Package your theme into a .zip file, ready to upload to the Online Store.'
810

@@ -21,8 +23,7 @@ export default class Package extends ThemeCommand {
2123
path: themeFlags.path,
2224
}
2325

24-
async run(): Promise<void> {
25-
const {flags} = await this.parse(Package)
26+
async command(flags: PackageFlags) {
2627
await packageTheme(flags.path)
2728
}
2829
}

packages/theme/src/cli/commands/theme/profile.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
import {themeFlags} from '../../flags.js'
2-
import ThemeCommand from '../../utilities/theme-command.js'
2+
import ThemeCommand, {RequiredFlags} from '../../utilities/theme-command.js'
33
import {profile} from '../../services/profile.js'
4-
import {ensureThemeStore} from '../../utilities/theme-store.js'
54
import {findOrSelectTheme} from '../../utilities/theme-selector.js'
65
import {renderTasksToStdErr} from '../../utilities/theme-ui.js'
76
import {validateThemePassword} from '../../services/flags-validation.js'
8-
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
97
import {Flags} from '@oclif/core'
108
import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli'
119
import {Task} from '@shopify/cli-kit/node/ui'
10+
import {InferredFlags} from '@oclif/core/interfaces'
11+
import {AdminSession} from '@shopify/cli-kit/node/session'
1212

13+
type ProfileFlags = InferredFlags<typeof Profile.flags>
1314
export default class Profile extends ThemeCommand {
1415
static summary = 'Profile the Liquid rendering of a theme page.'
1516

@@ -41,15 +42,13 @@ export default class Profile extends ThemeCommand {
4142
...jsonFlag,
4243
}
4344

44-
async run(): Promise<void> {
45-
const {flags} = await this.parse(Profile)
45+
static multiEnvironmentsFlags: RequiredFlags = null
4646

47-
validateThemePassword(flags.password)
48-
49-
const store = ensureThemeStore(flags)
47+
async command(flags: ProfileFlags, adminSession: AdminSession) {
5048
const {password: themeAccessPassword} = flags
5149

52-
const adminSession = await ensureAuthenticatedThemes(store, themeAccessPassword)
50+
validateThemePassword(themeAccessPassword)
51+
5352
let filter
5453
if (flags.theme) {
5554
filter = {filter: {theme: flags.theme}}
@@ -60,7 +59,7 @@ export default class Profile extends ThemeCommand {
6059

6160
const tasks: Task[] = [
6261
{
63-
title: `Generating Liquid profile for ${store + flags.url}`,
62+
title: `Generating Liquid profile for ${adminSession.storeFqdn} ${flags.url}`,
6463
task: async () => {
6564
await profile(
6665
adminSession,

packages/theme/src/cli/utilities/theme-command.test.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {describe, vi, expect, test, beforeEach} from 'vitest'
44
import {Config, Flags} from '@oclif/core'
55
import {AdminSession, ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
66
import {loadEnvironment} from '@shopify/cli-kit/node/environments'
7-
import {renderConcurrent, renderConfirmationPrompt, renderError} from '@shopify/cli-kit/node/ui'
87
import {fileExistsSync} from '@shopify/cli-kit/node/fs'
98
import {AbortError} from '@shopify/cli-kit/node/error'
109
import {resolvePath} from '@shopify/cli-kit/node/path'
10+
import {renderConcurrent, renderConfirmationPrompt, renderError, renderWarning} from '@shopify/cli-kit/node/ui'
1111
import type {Writable} from 'stream'
1212

1313
vi.mock('@shopify/cli-kit/node/session')
@@ -120,6 +120,10 @@ class TestUnauthenticatedThemeCommand extends ThemeCommand {
120120
}
121121
}
122122

123+
class TestNoMultiEnvThemeCommand extends TestThemeCommand {
124+
static multiEnvironmentsFlags: RequiredFlags = null
125+
}
126+
123127
describe('ThemeCommand', () => {
124128
let mockSession: AdminSession
125129

@@ -227,6 +231,31 @@ describe('ThemeCommand', () => {
227231
await expect(command.run()).rejects.toThrow(AbortError)
228232
expect(fileExistsSync).toHaveBeenCalledWith('current/working/directory')
229233
})
234+
235+
test('multiple environments provided - displays warning if not allowed', async () => {
236+
// Given
237+
const environmentConfig = {store: 'store.myshopify.com'}
238+
vi.mocked(loadEnvironment).mockResolvedValue(environmentConfig)
239+
vi.mocked(ensureAuthenticatedThemes).mockResolvedValue(mockSession)
240+
241+
vi.mocked(renderConcurrent).mockResolvedValue(undefined)
242+
243+
await CommandConfig.load()
244+
const command = new TestNoMultiEnvThemeCommand(
245+
['--environment', 'development', '--environment', 'staging'],
246+
CommandConfig,
247+
)
248+
249+
// When
250+
await command.run()
251+
252+
// Then
253+
expect(renderWarning).toHaveBeenCalledWith(
254+
expect.objectContaining({
255+
body: 'This command does not support multiple environments.',
256+
}),
257+
)
258+
})
230259
})
231260

232261
describe('multi environment', () => {

0 commit comments

Comments
 (0)