Skip to content

Commit 8b81097

Browse files
committed
Add ability to set theme name on push
1 parent b014978 commit 8b81097

10 files changed

Lines changed: 101 additions & 8 deletions

File tree

.changeset/puny-plants-allow.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@shopify/cli-kit': minor
3+
'@shopify/theme': minor
4+
'@shopify/cli': minor
5+
---
6+
7+
Add `--name` flag to `theme push`

docs-shopify.dev/commands/interfaces/theme-push.interface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export interface themepush {
4242
*/
4343
'-l, --live'?: ''
4444

45+
/**
46+
* The name for the theme. Will always create a new theme.
47+
* @environment SHOPIFY_FLAG_NAME
48+
*/
49+
'--name <value>'?: string
50+
4551
/**
4652
* Disable color output.
4753
* @environment SHOPIFY_FLAG_NO_COLOR

docs-shopify.dev/generated/generated_docs_data.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6884,6 +6884,15 @@
68846884
"isOptional": true,
68856885
"environmentValue": "SHOPIFY_FLAG_LISTING"
68866886
},
6887+
{
6888+
"filePath": "docs-shopify.dev/commands/interfaces/theme-push.interface.ts",
6889+
"syntaxKind": "PropertySignature",
6890+
"name": "--name <value>",
6891+
"value": "string",
6892+
"description": "The name for the theme. Will always create a new theme.",
6893+
"isOptional": true,
6894+
"environmentValue": "SHOPIFY_FLAG_NAME"
6895+
},
68876896
{
68886897
"filePath": "docs-shopify.dev/commands/interfaces/theme-push.interface.ts",
68896898
"syntaxKind": "PropertySignature",
@@ -7038,7 +7047,7 @@
70387047
"environmentValue": "SHOPIFY_FLAG_IGNORE"
70397048
}
70407049
],
7041-
"value": "export interface themepush {\n /**\n * Allow push to a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * Push theme files from your remote development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Skip uploading the specified files (Multiple flags allowed). Wrap the value in double quotes if you're using wildcards.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore <value>'?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.\n * @environment SHOPIFY_FLAG_LISTING\n */\n '--listing <value>'?: string\n\n /**\n * Push theme files from your remote live theme.\n * @environment SHOPIFY_FLAG_LIVE\n */\n '-l, --live'?: ''\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevent deleting remote files that don't exist locally.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * Upload only the specified files (Multiple flags allowed). Wrap the value in double quotes if you're using wildcards.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only <value>'?: string\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path where you want to run the command. Defaults to the current working directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * Publish as the live theme after uploading.\n * @environment SHOPIFY_FLAG_PUBLISH\n */\n '-p, --publish'?: ''\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * Require theme check to pass without errors before pushing. Warnings are allowed.\n * @environment SHOPIFY_FLAG_STRICT_PUSH\n */\n '--strict'?: ''\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>'?: string\n\n /**\n * Create a new unpublished theme and push to it.\n * @environment SHOPIFY_FLAG_UNPUBLISHED\n */\n '-u, --unpublished'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
7050+
"value": "export interface themepush {\n /**\n * Allow push to a live theme.\n * @environment SHOPIFY_FLAG_ALLOW_LIVE\n */\n '-a, --allow-live'?: ''\n\n /**\n * Push theme files from your remote development theme.\n * @environment SHOPIFY_FLAG_DEVELOPMENT\n */\n '-d, --development'?: ''\n\n /**\n * The environment to apply to the current command.\n * @environment SHOPIFY_FLAG_ENVIRONMENT\n */\n '-e, --environment <value>'?: string\n\n /**\n * Skip uploading the specified files (Multiple flags allowed). Wrap the value in double quotes if you're using wildcards.\n * @environment SHOPIFY_FLAG_IGNORE\n */\n '-x, --ignore <value>'?: string\n\n /**\n * Output the result as JSON.\n * @environment SHOPIFY_FLAG_JSON\n */\n '-j, --json'?: ''\n\n /**\n * The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.\n * @environment SHOPIFY_FLAG_LISTING\n */\n '--listing <value>'?: string\n\n /**\n * Push theme files from your remote live theme.\n * @environment SHOPIFY_FLAG_LIVE\n */\n '-l, --live'?: ''\n\n /**\n * The name for the theme. Will always create a new theme.\n * @environment SHOPIFY_FLAG_NAME\n */\n '--name <value>'?: string\n\n /**\n * Disable color output.\n * @environment SHOPIFY_FLAG_NO_COLOR\n */\n '--no-color'?: ''\n\n /**\n * Prevent deleting remote files that don't exist locally.\n * @environment SHOPIFY_FLAG_NODELETE\n */\n '-n, --nodelete'?: ''\n\n /**\n * Upload only the specified files (Multiple flags allowed). Wrap the value in double quotes if you're using wildcards.\n * @environment SHOPIFY_FLAG_ONLY\n */\n '-o, --only <value>'?: string\n\n /**\n * Password generated from the Theme Access app or an Admin API token.\n * @environment SHOPIFY_CLI_THEME_TOKEN\n */\n '--password <value>'?: string\n\n /**\n * The path where you want to run the command. Defaults to the current working directory.\n * @environment SHOPIFY_FLAG_PATH\n */\n '--path <value>'?: string\n\n /**\n * Publish as the live theme after uploading.\n * @environment SHOPIFY_FLAG_PUBLISH\n */\n '-p, --publish'?: ''\n\n /**\n * Store URL. It can be the store prefix (example) or the full myshopify.com URL (example.myshopify.com, https://example.myshopify.com).\n * @environment SHOPIFY_FLAG_STORE\n */\n '-s, --store <value>'?: string\n\n /**\n * Require theme check to pass without errors before pushing. Warnings are allowed.\n * @environment SHOPIFY_FLAG_STRICT_PUSH\n */\n '--strict'?: ''\n\n /**\n * Theme ID or name of the remote theme.\n * @environment SHOPIFY_FLAG_THEME_ID\n */\n '-t, --theme <value>'?: string\n\n /**\n * Create a new unpublished theme and push to it.\n * @environment SHOPIFY_FLAG_UNPUBLISHED\n */\n '-u, --unpublished'?: ''\n\n /**\n * Increase the verbosity of the output.\n * @environment SHOPIFY_FLAG_VERBOSE\n */\n '--verbose'?: ''\n}"
70427051
}
70437052
}
70447053
}

packages/cli-kit/src/public/node/themes/theme-manager.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,28 @@ describe('ThemeManager', () => {
9292
expect(result).toEqual(mockTheme)
9393
expect(manager.getStoredThemeId()).toBe('123')
9494
})
95+
96+
test('always creates a new theme when name is provided', async () => {
97+
// Given
98+
manager.setThemeId('123')
99+
const customTheme = {...mockTheme, name: 'Custom name'}
100+
vi.mocked(themeCreate).mockResolvedValue(customTheme)
101+
102+
// When
103+
const result = await manager.findOrCreate('Custom name')
104+
105+
// Then
106+
expect(fetchTheme).not.toHaveBeenCalled()
107+
expect(themeCreate).toHaveBeenCalledWith(
108+
{
109+
name: 'Custom name',
110+
role: DEVELOPMENT_THEME_ROLE,
111+
},
112+
session,
113+
)
114+
expect(result).toEqual(customTheme)
115+
expect(manager.getStoredThemeId()).toBe('123')
116+
})
95117
})
96118

97119
describe('fetch', () => {

packages/cli-kit/src/public/node/themes/theme-manager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ export abstract class ThemeManager {
1313

1414
constructor(protected adminSession: AdminSession) {}
1515

16-
async findOrCreate(): Promise<Theme> {
17-
let theme = await this.fetch()
16+
async findOrCreate(name?: string): Promise<Theme> {
17+
let theme = name ? undefined : await this.fetch()
1818
if (!theme) {
19-
theme = await this.create()
19+
theme = await this.create(undefined, name)
2020
}
2121
return theme
2222
}

packages/cli/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,6 +2400,7 @@ FLAGS
24002400
quotes if you're using wildcards.
24012401
--listing=<value> The listing preset to use for multi-preset themes. Applies preset files from
24022402
listings/[preset-name] directory.
2403+
--name=<value> The name for the theme. Will always create a new theme.
24032404
--no-color Disable color output.
24042405
--password=<value> Password generated from the Theme Access app or an Admin API token.
24052406
--path=<value> The path where you want to run the command. Defaults to the current working directory.

packages/cli/oclif.manifest.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7134,6 +7134,17 @@
71347134
"name": "live",
71357135
"type": "boolean"
71367136
},
7137+
"name": {
7138+
"description": "The name for the theme. Will always create a new theme.",
7139+
"env": "SHOPIFY_FLAG_NAME",
7140+
"exclusive": [
7141+
"theme"
7142+
],
7143+
"hasDynamicHelp": false,
7144+
"multiple": false,
7145+
"name": "name",
7146+
"type": "option"
7147+
},
71377148
"no-color": {
71387149
"allowNo": false,
71397150
"description": "Disable color output.",

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ export default class Push extends ThemeCommand {
102102
'The listing preset to use for multi-preset themes. Applies preset files from listings/[preset-name] directory.',
103103
env: 'SHOPIFY_FLAG_LISTING',
104104
}),
105+
name: Flags.string({
106+
description: 'The name for the theme. Will always create a new theme.',
107+
env: 'SHOPIFY_FLAG_NAME',
108+
exclusive: ['theme'],
109+
}),
105110
environment: themeFlags.environment,
106111
}
107112

packages/theme/src/cli/services/push.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,21 @@ describe('createOrSelectTheme', async () => {
278278
expect(setDevelopmentTheme).not.toHaveBeenCalled()
279279
})
280280

281+
test('creates unpublished theme when name flag is provided', async () => {
282+
// Given
283+
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 2, name: 'Custom name', role: UNPUBLISHED_THEME_ROLE}))
284+
vi.mocked(fetchTheme).mockResolvedValue(undefined)
285+
286+
const flags: PushFlags = {name: 'Custom name'}
287+
288+
// When
289+
const theme = await createOrSelectTheme(adminSession, flags)
290+
291+
// Then
292+
expect(theme).toMatchObject({name: 'Custom name'})
293+
expect(setDevelopmentTheme).not.toHaveBeenCalled()
294+
})
295+
281296
test('creates development theme when development flag is provided', async () => {
282297
// Given
283298
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 1, name: 'Theme', role: DEVELOPMENT_THEME_ROLE}))
@@ -292,6 +307,20 @@ describe('createOrSelectTheme', async () => {
292307
expect(setDevelopmentTheme).toHaveBeenCalled()
293308
})
294309

310+
test('creates development theme when development and name flags are provided', async () => {
311+
// Given
312+
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 1, name: 'Custom name', role: DEVELOPMENT_THEME_ROLE}))
313+
vi.mocked(fetchTheme).mockResolvedValue(undefined)
314+
const flags: PushFlags = {development: true, name: 'Custom name'}
315+
316+
// When
317+
const theme = await createOrSelectTheme(adminSession, flags)
318+
319+
// Then
320+
expect(theme).toMatchObject({role: DEVELOPMENT_THEME_ROLE, name: 'Custom name'})
321+
expect(setDevelopmentTheme).toHaveBeenCalled()
322+
})
323+
295324
test('creates development theme when development and unpublished flags are provided', async () => {
296325
// Given
297326
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 1, name: 'Theme', role: DEVELOPMENT_THEME_ROLE}))

packages/theme/src/cli/services/push.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ export interface PushFlags {
113113

114114
/** The listing preset to use for multi-preset themes. */
115115
listing?: string
116+
117+
/** The name of the theme. Will always create a new theme. */
118+
name?: string
116119
}
117120

118121
/**
@@ -379,13 +382,13 @@ export async function createOrSelectTheme(
379382
flags: PushFlags,
380383
multiEnvironment?: boolean,
381384
): Promise<Theme | undefined> {
382-
const {live, development, unpublished, theme, environment} = flags
385+
const {live, development, unpublished, theme, environment, name} = flags
383386

384387
if (development) {
385388
const themeManager = new DevelopmentThemeManager(session)
386-
return themeManager.findOrCreate()
387-
} else if (unpublished) {
388-
const themeName = theme ?? (await promptThemeName('Name of the new theme'))
389+
return themeManager.findOrCreate(name)
390+
} else if (unpublished ?? name) {
391+
const themeName = name ?? theme ?? (await promptThemeName('Name of the new theme'))
389392
return themeCreate(
390393
{
391394
name: themeName,

0 commit comments

Comments
 (0)