Skip to content

Commit c0dd1ba

Browse files
committed
Add ability to set theme name on push
1 parent c9ac31a commit c0dd1ba

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
@@ -36,6 +36,12 @@ export interface themepush {
3636
*/
3737
'-l, --live'?: ''
3838

39+
/**
40+
* The name for the theme. Will always create a new theme.
41+
* @environment SHOPIFY_FLAG_NAME
42+
*/
43+
'--name <value>'?: string
44+
3945
/**
4046
* Disable color output.
4147
* @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
@@ -6866,6 +6866,15 @@
68666866
"name": "themepush",
68676867
"description": "",
68686868
"members": [
6869+
{
6870+
"filePath": "docs-shopify.dev/commands/interfaces/theme-push.interface.ts",
6871+
"syntaxKind": "PropertySignature",
6872+
"name": "--name <value>",
6873+
"value": "string",
6874+
"description": "The name for the theme. Will always create a new theme.",
6875+
"isOptional": true,
6876+
"environmentValue": "SHOPIFY_FLAG_NAME"
6877+
},
68696878
{
68706879
"filePath": "docs-shopify.dev/commands/interfaces/theme-push.interface.ts",
68716880
"syntaxKind": "PropertySignature",
@@ -7020,7 +7029,7 @@
70207029
"environmentValue": "SHOPIFY_FLAG_IGNORE"
70217030
}
70227031
],
7023-
"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 * 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}"
7032+
"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 * 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}"
70247033
}
70257034
}
70267035
}

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
@@ -2395,6 +2395,7 @@ FLAGS
23952395
-u, --unpublished Create a new unpublished theme and push to it.
23962396
-x, --ignore=<value>... Skip uploading the specified files (Multiple flags allowed). Wrap the value in double
23972397
quotes if you're using wildcards.
2398+
--name=<value> The name for the theme. Will always create a new theme.
23982399
--no-color Disable color output.
23992400
--password=<value> Password generated from the Theme Access app or an Admin API token.
24002401
--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
@@ -7114,6 +7114,17 @@
71147114
"name": "live",
71157115
"type": "boolean"
71167116
},
7117+
"name": {
7118+
"description": "The name for the theme. Will always create a new theme.",
7119+
"env": "SHOPIFY_FLAG_NAME",
7120+
"exclusive": [
7121+
"theme"
7122+
],
7123+
"hasDynamicHelp": false,
7124+
"multiple": false,
7125+
"name": "name",
7126+
"type": "option"
7127+
},
71177128
"no-color": {
71187129
"allowNo": false,
71197130
"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
@@ -97,6 +97,11 @@ export default class Push extends ThemeCommand {
9797
description: 'Require theme check to pass without errors before pushing. Warnings are allowed.',
9898
env: 'SHOPIFY_FLAG_STRICT_PUSH',
9999
}),
100+
name: Flags.string({
101+
description: 'The name for the theme. Will always create a new theme.',
102+
env: 'SHOPIFY_FLAG_NAME',
103+
exclusive: ['theme'],
104+
}),
100105
environment: themeFlags.environment,
101106
}
102107

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

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

261+
test('creates unpublished theme when name flag is provided', async () => {
262+
// Given
263+
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 2, name: 'Custom name', role: UNPUBLISHED_THEME_ROLE}))
264+
vi.mocked(fetchTheme).mockResolvedValue(undefined)
265+
266+
const flags: PushFlags = {name: 'Custom name'}
267+
268+
// When
269+
const theme = await createOrSelectTheme(adminSession, flags)
270+
271+
// Then
272+
expect(theme).toMatchObject({name: 'Custom name'})
273+
expect(setDevelopmentTheme).not.toHaveBeenCalled()
274+
})
275+
261276
test('creates development theme when development flag is provided', async () => {
262277
// Given
263278
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 1, name: 'Theme', role: DEVELOPMENT_THEME_ROLE}))
@@ -272,6 +287,20 @@ describe('createOrSelectTheme', async () => {
272287
expect(setDevelopmentTheme).toHaveBeenCalled()
273288
})
274289

290+
test('creates development theme when development and name flags are provided', async () => {
291+
// Given
292+
vi.mocked(themeCreate).mockResolvedValue(buildTheme({id: 1, name: 'Custom name', role: DEVELOPMENT_THEME_ROLE}))
293+
vi.mocked(fetchTheme).mockResolvedValue(undefined)
294+
const flags: PushFlags = {development: true, name: 'Custom name'}
295+
296+
// When
297+
const theme = await createOrSelectTheme(adminSession, flags)
298+
299+
// Then
300+
expect(theme).toMatchObject({role: DEVELOPMENT_THEME_ROLE, name: 'Custom name'})
301+
expect(setDevelopmentTheme).toHaveBeenCalled()
302+
})
303+
275304
test('creates development theme when development and unpublished flags are provided', async () => {
276305
// Given
277306
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
@@ -108,6 +108,9 @@ export interface PushFlags {
108108

109109
/** The environment to push the theme to. */
110110
environment?: string[]
111+
112+
/** The name of the theme. Will always create a new theme. */
113+
name?: string
111114
}
112115

113116
/**
@@ -366,13 +369,13 @@ export async function createOrSelectTheme(
366369
flags: PushFlags,
367370
multiEnvironment?: boolean,
368371
): Promise<Theme | undefined> {
369-
const {live, development, unpublished, theme, environment} = flags
372+
const {live, development, unpublished, theme, environment, name} = flags
370373

371374
if (development) {
372375
const themeManager = new DevelopmentThemeManager(session)
373-
return themeManager.findOrCreate()
374-
} else if (unpublished) {
375-
const themeName = theme ?? (await promptThemeName('Name of the new theme'))
376+
return themeManager.findOrCreate(name)
377+
} else if (unpublished ?? name) {
378+
const themeName = name ?? theme ?? (await promptThemeName('Name of the new theme'))
376379
return themeCreate(
377380
{
378381
name: themeName,

0 commit comments

Comments
 (0)