diff --git a/.changeset/crisp-loops-start.md b/.changeset/crisp-loops-start.md new file mode 100644 index 0000000000..70791e3672 --- /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 c4492d8d8e..15a71e6aae 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 b03a3fa515..52dc617199 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`)