Skip to content

Commit 74fc99c

Browse files
committed
Only create sessions for commands that require auth
Previously, all commands run with multiple environments attempted to create a session object with store URL and password. This commit ensures that commands that do no accept a password and don't need to be authenticated will not try to create a session
1 parent 3461431 commit 74fc99c

File tree

3 files changed

+76
-14
lines changed

3 files changed

+76
-14
lines changed

.changeset/large-otters-dream.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@shopify/cli-kit': minor
3+
'@shopify/theme': minor
4+
---
5+
6+
Commands that don't require authentication should not create sessions when ran with multiple environments

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,32 @@ class TestThemeCommandWithUnionFlags extends TestThemeCommand {
8585
}
8686
}
8787

88+
class TestUnauthenticatedThemeCommand extends ThemeCommand {
89+
static flags = {
90+
environment: Flags.string({
91+
multiple: true,
92+
default: [],
93+
env: 'SHOPIFY_FLAG_ENVIRONMENT',
94+
}),
95+
store: Flags.string({
96+
env: 'SHOPIFY_FLAG_STORE',
97+
}),
98+
}
99+
100+
static multiEnvironmentsFlags: RequiredFlags = ['store']
101+
102+
commandCalls: {flags: any; session: AdminSession; multiEnvironment?: boolean; context?: any}[] = []
103+
104+
async command(
105+
flags: any,
106+
session: AdminSession,
107+
multiEnvironment?: boolean,
108+
context?: {stdout?: Writable; stderr?: Writable},
109+
): Promise<void> {
110+
this.commandCalls.push({flags, session, multiEnvironment, context})
111+
}
112+
}
113+
88114
describe('ThemeCommand', () => {
89115
let mockSession: AdminSession
90116

@@ -552,5 +578,31 @@ describe('ThemeCommand', () => {
552578
expect(liveEnvFlags?.live).toEqual(true)
553579
expect(liveEnvFlags?.['no-color']).toEqual(true)
554580
})
581+
582+
test('commands will only create a session object if the password flag is supported', async () => {
583+
// Given
584+
vi.mocked(loadEnvironment)
585+
.mockResolvedValueOnce({store: 'store1.myshopify.com'})
586+
.mockResolvedValueOnce({store: 'store2.myshopify.com'})
587+
588+
vi.mocked(renderConcurrent).mockImplementation(async ({processes}) => {
589+
for (const process of processes) {
590+
// eslint-disable-next-line no-await-in-loop
591+
await process.action({} as Writable, {} as Writable, {} as any)
592+
}
593+
})
594+
595+
await CommandConfig.load()
596+
const command = new TestUnauthenticatedThemeCommand(
597+
['--environment', 'store1', '--environment', 'store2'],
598+
CommandConfig,
599+
)
600+
601+
// When
602+
await command.run()
603+
604+
// Then
605+
expect(ensureAuthenticatedThemes).not.toHaveBeenCalled()
606+
})
555607
})
556608
})

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

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default abstract class ThemeCommand extends Command {
6767

6868
async command(
6969
_flags: FlagValues,
70-
_session: AdminSession,
70+
_session?: AdminSession,
7171
_multiEnvironment = false,
7272
_context?: {stdout?: Writable; stderr?: Writable},
7373
): Promise<void> {}
@@ -84,18 +84,17 @@ export default abstract class ThemeCommand extends Command {
8484
}
8585
const requiredFlags = klass.multiEnvironmentsFlags
8686
const {flags} = await this.parse(klass)
87-
87+
const commandRequiresAuth = 'password' in klass.flags
8888
const environments = (Array.isArray(flags.environment) ? flags.environment : [flags.environment]).filter(Boolean)
8989

9090
// Single environment or no environment
9191
if (environments.length <= 1) {
92-
const session = await this.createSession(flags)
92+
const session = commandRequiresAuth ? await this.createSession(flags) : undefined
9393
const commandName = this.constructor.name.toLowerCase()
9494

9595
recordEvent(`theme-command:${commandName}:single-env:authenticated`)
9696

9797
await this.command(flags, session)
98-
await this.logAnalyticsData(session)
9998
return
10099
}
101100

@@ -112,7 +111,7 @@ export default abstract class ThemeCommand extends Command {
112111
}
113112

114113
const environmentsMap = await this.loadEnvironments(environments, flags, flagsWithoutDefaults)
115-
const validationResults = await this.validateEnvironments(environmentsMap, requiredFlags)
114+
const validationResults = await this.validateEnvironments(environmentsMap, requiredFlags, commandRequiresAuth)
116115

117116
const commandAllowsForceFlag = 'force' in klass.flags
118117

@@ -160,13 +159,15 @@ export default abstract class ThemeCommand extends Command {
160159
* Split environments into valid and invalid based on flags
161160
* @param environmentMap - The map of environments to validate
162161
* @param requiredFlags - The required flags to check for
162+
* @param requiresAuth - Whether the command requires authentication
163163
* @returns An object containing valid and invalid environment arrays
164164
*/
165165
private async validateEnvironments(
166166
environmentMap: Map<EnvironmentName, FlagValues>,
167167
requiredFlags: Exclude<RequiredFlags, null>,
168+
requiresAuth: boolean,
168169
) {
169-
const valid: {environment: EnvironmentName; flags: FlagValues}[] = []
170+
const valid: {environment: EnvironmentName; flags: FlagValues; requiresAuth: boolean}[] = []
170171
const invalid: {environment: EnvironmentName; reason: string}[] = []
171172

172173
for (const [environmentName, environmentFlags] of environmentMap) {
@@ -176,8 +177,7 @@ export default abstract class ThemeCommand extends Command {
176177
invalid.push({environment: environmentName, reason: `Missing flags: ${missingFlagsText}`})
177178
continue
178179
}
179-
180-
valid.push({environment: environmentName, flags: environmentFlags})
180+
valid.push({environment: environmentName, flags: environmentFlags, requiresAuth})
181181
}
182182

183183
return {valid, invalid}
@@ -194,7 +194,7 @@ export default abstract class ThemeCommand extends Command {
194194
commandName: string,
195195
requiredFlags: Exclude<RequiredFlags, null>,
196196
validationResults: {
197-
valid: {environment: string; flags: FlagValues}[]
197+
valid: {environment: string; flags: FlagValues; requiresAuth: boolean}[]
198198
invalid: {environment: string; reason: string}[]
199199
},
200200
) {
@@ -234,7 +234,9 @@ export default abstract class ThemeCommand extends Command {
234234
* Run the command in each valid environment concurrently
235235
* @param validEnvironments - The valid environments to run the command in
236236
*/
237-
private async runConcurrent(validEnvironments: {environment: EnvironmentName; flags: FlagValues}[]) {
237+
private async runConcurrent(
238+
validEnvironments: {environment: EnvironmentName; flags: FlagValues; requiresAuth: boolean}[],
239+
) {
238240
const abortController = new AbortController()
239241

240242
const stores = validEnvironments.map((env) => env.flags.store as string)
@@ -245,13 +247,13 @@ export default abstract class ThemeCommand extends Command {
245247
for (const runGroup of runGroups) {
246248
// eslint-disable-next-line no-await-in-loop
247249
await renderConcurrent({
248-
processes: runGroup.map(({environment, flags}) => ({
250+
processes: runGroup.map(({environment, flags, requiresAuth}) => ({
249251
prefix: environment,
250252
action: async (stdout: Writable, stderr: Writable, _signal) => {
251253
try {
252254
const store = flags.store as string
253255
await useThemeStoreContext(store, async () => {
254-
const session = await this.createSession(flags)
256+
const session = requiresAuth ? await this.createSession(flags) : undefined
255257

256258
const commandName = this.constructor.name.toLowerCase()
257259
recordEvent(`theme-command:${commandName}:multi-env:authenticated`)
@@ -280,8 +282,10 @@ export default abstract class ThemeCommand extends Command {
280282
* @param environments - The environments to group
281283
* @returns The environment groups
282284
*/
283-
private createSequentialGroups(environments: {environment: EnvironmentName; flags: FlagValues}[]) {
284-
const groups: {environment: EnvironmentName; flags: FlagValues}[][] = []
285+
private createSequentialGroups(
286+
environments: {environment: EnvironmentName; flags: FlagValues; requiresAuth: boolean}[],
287+
) {
288+
const groups: {environment: EnvironmentName; flags: FlagValues; requiresAuth: boolean}[][] = []
285289

286290
environments.forEach((environment) => {
287291
const groupWithoutStore = groups.find((arr) => !arr.some((env) => env.flags.store === environment.flags.store))

0 commit comments

Comments
 (0)