From 947025f6bb2307469d5e3fff2a6b2f99654151c9 Mon Sep 17 00:00:00 2001 From: Guilherme Carreiro Date: Fri, 23 Jan 2026 11:13:24 +0100 Subject: [PATCH] =?UTF-8?q?Fix=20the=20default=20environments=20infrastruc?= =?UTF-8?q?ture=20so=20it=20doesn=E2=80=99t=20fail=20when=20running=20comm?= =?UTF-8?q?ands=20that=20don=E2=80=99t=20require=20authentication?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/crisp-loops-start.md | 5 ++ .../src/cli/utilities/theme-command.test.ts | 81 +++++++++++++++++++ .../theme/src/cli/utilities/theme-command.ts | 10 ++- 3 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 .changeset/crisp-loops-start.md diff --git a/.changeset/crisp-loops-start.md b/.changeset/crisp-loops-start.md new file mode 100644 index 00000000000..70791e36728 --- /dev/null +++ b/.changeset/crisp-loops-start.md @@ -0,0 +1,5 @@ +--- +'@shopify/theme': patch +--- + +Fix the default environments infrastructure so it doesn’t fail when running commands that don't require authentication diff --git a/packages/theme/src/cli/utilities/theme-command.test.ts b/packages/theme/src/cli/utilities/theme-command.test.ts index c4492d8d8e1..15a71e6aae9 100644 --- a/packages/theme/src/cli/utilities/theme-command.test.ts +++ b/packages/theme/src/cli/utilities/theme-command.test.ts @@ -126,6 +126,46 @@ class TestNoMultiEnvThemeCommand extends TestThemeCommand { static multiEnvironmentsFlags: RequiredFlags = null } +class TestThemeCommandWithoutStoreRequired extends ThemeCommand { + static flags = { + environment: Flags.string({ + multiple: true, + default: [], + env: 'SHOPIFY_FLAG_ENVIRONMENT', + }), + path: Flags.string({ + env: 'SHOPIFY_FLAG_PATH', + default: 'current/working/directory', + }), + password: Flags.string({ + env: 'SHOPIFY_FLAG_PASSWORD', + }), + store: Flags.string({ + env: 'SHOPIFY_FLAG_STORE', + }), + } + + static multiEnvironmentsFlags: RequiredFlags = ['path'] + + commandCalls: { + flags: any + session: AdminSession | undefined + multiEnvironment?: boolean + args?: any + context?: any + }[] = [] + + async command( + flags: any, + session: AdminSession | undefined, + multiEnvironment?: boolean, + args?: any, + context?: {stdout?: Writable; stderr?: Writable}, + ): Promise { + this.commandCalls.push({flags, session, multiEnvironment, args, context}) + } +} + describe('ThemeCommand', () => { let mockSession: AdminSession @@ -203,6 +243,47 @@ describe('ThemeCommand', () => { await expect(command.run()).rejects.toThrow('Please provide a valid environment.') }) + test('single environment provided without store - does not throw when store is not required', async () => { + // Given + const environmentConfig = {path: '/some/path'} + vi.mocked(loadEnvironment).mockResolvedValue(environmentConfig) + + await CommandConfig.load() + const command = new TestThemeCommandWithoutStoreRequired(['--environment', 'development'], CommandConfig) + + // When + await command.run() + + // Then + expect(command.commandCalls).toHaveLength(1) + expect(command.commandCalls[0]).toMatchObject({ + flags: { + environment: ['development'], + path: '/some/path', + }, + session: undefined, + multiEnvironment: false, + }) + expect(ensureAuthenticatedThemes).not.toHaveBeenCalled() + }) + + test('single environment provided with store - creates session when store is provided even if not required', async () => { + // Given + const environmentConfig = {path: '/some/path', store: 'store.myshopify.com'} + vi.mocked(loadEnvironment).mockResolvedValue(environmentConfig) + + await CommandConfig.load() + const command = new TestThemeCommandWithoutStoreRequired(['--environment', 'development'], CommandConfig) + + // When + await command.run() + + // Then + expect(ensureAuthenticatedThemes).toHaveBeenCalledOnce() + expect(command.commandCalls).toHaveLength(1) + expect(command.commandCalls[0]?.session).toEqual(mockSession) + }) + test('multiple environments provided - uses renderConcurrent for parallel execution', async () => { // Given vi.mocked(loadEnvironment) diff --git a/packages/theme/src/cli/utilities/theme-command.ts b/packages/theme/src/cli/utilities/theme-command.ts index b03a3fa5159..52dc6171993 100644 --- a/packages/theme/src/cli/utilities/theme-command.ts +++ b/packages/theme/src/cli/utilities/theme-command.ts @@ -77,13 +77,19 @@ export default abstract class ThemeCommand extends Command { const environments = (Array.isArray(flags.environment) ? flags.environment : [flags.environment]).filter(Boolean) + // Check if store flag is required by the command + const storeIsRequired = + requiredFlags !== null && + requiredFlags.some((flag) => (Array.isArray(flag) ? flag.includes('store') : flag === 'store')) + // Single environment or no environment if (environments.length <= 1) { - if (environments[0] && !flags.store) { + if (environments[0] && !flags.store && storeIsRequired) { throw new AbortError(`Please provide a valid environment.`) } - const session = commandRequiresAuth ? await this.createSession(flags) : undefined + const shouldCreateSession = commandRequiresAuth && (storeIsRequired || flags.store) + const session = shouldCreateSession ? await this.createSession(flags) : undefined const commandName = this.constructor.name.toLowerCase() recordEvent(`theme-command:${commandName}:single-env:authenticated`)