Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/crisp-loops-start.md
Original file line number Diff line number Diff line change
@@ -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
81 changes: 81 additions & 0 deletions packages/theme/src/cli/utilities/theme-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
this.commandCalls.push({flags, session, multiEnvironment, args, context})
}
}

describe('ThemeCommand', () => {
let mockSession: AdminSession

Expand Down Expand Up @@ -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)
Expand Down
10 changes: 8 additions & 2 deletions packages/theme/src/cli/utilities/theme-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
Loading