Skip to content

Commit 0d0c8b1

Browse files
authored
Merge branch 'main' into fix-741
2 parents f1f5581 + b2a3d9a commit 0d0c8b1

File tree

27 files changed

+346
-72
lines changed

27 files changed

+346
-72
lines changed

.changeset/dull-cycles-brush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/theme': patch
3+
---
4+
5+
No longer raise password errors when users use custom app passwords on `shopify theme pull/push` commands

.changeset/flat-schools-dance.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/cli-kit': patch
3+
---
4+
5+
Remove redundant calls to normalizeStoreFqdn in requests

.changeset/rotten-ties-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': patch
3+
---
4+
5+
Better handling on import-extensions with existing folder
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/cli-kit': patch
3+
---
4+
5+
Fix theme event reporting for monorail

.changeset/wild-moles-help.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': patch
3+
---
4+
5+
Fix deeplink URL after a deploy

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"pin-github-action": "^3.3.1",
7777
"react": "17.0.2",
7878
"rimraf": "^3.0.2",
79-
"tmp": "^0.2.1",
79+
"tmp": "^0.2.4",
8080
"ts-node": "^10.9.1",
8181
"typescript": "5.8.3",
8282
"vitest": "^3.1.4",

packages/app/src/cli/api/graphql/business-platform-organizations/generated/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export type Scalars = {
4848
OrganizationID: {input: any; output: any}
4949
/** The ID for a OrganizationUser. */
5050
OrganizationUserID: {input: any; output: any}
51+
/** The ID for a PersonAlias. */
52+
PersonAliasID: {input: any; output: any}
5153
/** The ID for a Person. */
5254
PersonID: {input: any; output: any}
5355
/** The ID for a Principal. */

packages/app/src/cli/services/generate/extension.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ describe('initialize a extension', async () => {
264264
name,
265265
handle: slugify(name),
266266
flavor,
267+
uid: 'ba7c20a9-578d-6fee-8cd2-044af992dabd92d8bbfe',
267268
})
268269
})
269270
},

packages/app/src/cli/services/generate/extension.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {downloadGitRepository} from '@shopify/cli-kit/node/git'
1919
import {fileExists, inTemporaryDirectory, mkdir, moveFile, removeFile, glob} from '@shopify/cli-kit/node/fs'
2020
import {joinPath, relativizePath} from '@shopify/cli-kit/node/path'
2121
import {slugify} from '@shopify/cli-kit/common/string'
22-
import {randomUUID} from '@shopify/cli-kit/node/crypto'
22+
import {nonRandomUUID} from '@shopify/cli-kit/node/crypto'
2323

2424
export interface GenerateExtensionTemplateOptions {
2525
app: AppLinkedInterface
@@ -72,7 +72,6 @@ interface ExtensionInitOptions {
7272
type: string
7373
name: string
7474
extensionFlavor: ExtensionFlavor | undefined
75-
uid: string | undefined
7675
onGetTemplateRepository: (url: string, destination: string) => Promise<void>
7776
}
7877

@@ -87,15 +86,13 @@ export async function generateExtensionTemplate(
8786
const directory = await ensureExtensionDirectoryExists({app: options.app, name: extensionName})
8887
const url = options.cloneUrl ?? options.extensionTemplate.url
8988

90-
const uid = options.developerPlatformClient.supportsAtomicDeployments ? randomUUID() : undefined
9189
const initOptions: ExtensionInitOptions = {
9290
directory,
9391
url,
9492
app: options.app,
9593
type: options.extensionTemplate.type,
9694
name: extensionName,
9795
extensionFlavor,
98-
uid,
9996
onGetTemplateRepository:
10097
options.onGetTemplateRepository ??
10198
(async (url, destination) => {
@@ -133,7 +130,6 @@ async function themeExtensionInit({
133130
type,
134131
name,
135132
extensionFlavor,
136-
uid,
137133
onGetTemplateRepository,
138134
}: ExtensionInitOptions) {
139135
return inTemporaryDirectory(async (tmpDir) => {
@@ -143,7 +139,7 @@ async function themeExtensionInit({
143139
tmpDir,
144140
onGetTemplateRepository,
145141
)
146-
await recursiveLiquidTemplateCopy(templateDirectory, directory, {name, type, uid})
142+
await recursiveLiquidTemplateCopy(templateDirectory, directory, {name, type, uid: nonRandomUUID(slugify(name))})
147143
})
148144
}
149145

@@ -153,7 +149,6 @@ async function functionExtensionInit({
153149
app,
154150
name,
155151
extensionFlavor,
156-
uid,
157152
onGetTemplateRepository,
158153
}: ExtensionInitOptions) {
159154
const templateLanguage = getTemplateLanguage(extensionFlavor?.value)
@@ -173,7 +168,7 @@ async function functionExtensionInit({
173168
name,
174169
handle: slugify(name),
175170
flavor: extensionFlavor?.value,
176-
uid,
171+
uid: nonRandomUUID(slugify(name)),
177172
})
178173
})
179174

@@ -221,7 +216,6 @@ async function uiExtensionInit({
221216
app,
222217
name,
223218
extensionFlavor,
224-
uid,
225219
onGetTemplateRepository,
226220
}: ExtensionInitOptions) {
227221
const templateLanguage = getTemplateLanguage(extensionFlavor?.value)
@@ -244,7 +238,7 @@ async function uiExtensionInit({
244238
name,
245239
handle: slugify(name),
246240
flavor: extensionFlavor?.value ?? '',
247-
uid,
241+
uid: nonRandomUUID(slugify(name)),
248242
})
249243
})
250244

packages/app/src/cli/services/import-extensions.test.ts

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import {testAppLinked, testDeveloperPlatformClient, testUIExtension} from '../mo
44
import {OrganizationApp} from '../models/organization.js'
55
import {ExtensionRegistration} from '../api/graphql/all_app_extension_registrations.js'
66
import {describe, expect, test, vi, beforeEach} from 'vitest'
7-
import {fileExistsSync, inTemporaryDirectory} from '@shopify/cli-kit/node/fs'
7+
import {fileExistsSync, inTemporaryDirectory, mkdir} from '@shopify/cli-kit/node/fs'
88
import {renderSelectPrompt, renderSuccess} from '@shopify/cli-kit/node/ui'
99
import {joinPath} from '@shopify/cli-kit/node/path'
10+
import {AbortSilentError} from '@shopify/cli-kit/node/error'
1011

1112
vi.mock('@shopify/cli-kit/node/ui')
1213
vi.mock('./context.js')
@@ -135,6 +136,141 @@ describe('import-extensions', () => {
135136
})
136137
})
137138

139+
test('handles existing directory with user prompt - skip', async () => {
140+
// Given
141+
const extensions = [flowExtensionA]
142+
143+
// When
144+
await inTemporaryDirectory(async (tmpDir) => {
145+
const app = testAppLinked({directory: tmpDir})
146+
147+
// Create the extensions directory
148+
const extensionsDir = joinPath(tmpDir, 'extensions')
149+
await mkdir(extensionsDir)
150+
151+
// Create the specific extension directory
152+
const extensionDir = joinPath(extensionsDir, 'title-a')
153+
await mkdir(extensionDir)
154+
155+
// Mock prompts:
156+
// 1. First prompt: select which extension to migrate (select flowExtensionA by its UUID)
157+
// 2. Second prompt: what to do with existing directory (select skip)
158+
vi.mocked(renderSelectPrompt)
159+
// Select flowExtensionA
160+
.mockResolvedValueOnce('uuidA')
161+
// Skip existing directory
162+
.mockResolvedValueOnce('skip')
163+
164+
await importExtensions({
165+
app,
166+
remoteApp: organizationApp,
167+
developerPlatformClient: testDeveloperPlatformClient(),
168+
extensionTypes: ['flow_action_definition'],
169+
extensions,
170+
buildTomlObject,
171+
})
172+
173+
// Then - expect the success message to be shown (even for skipped extensions)
174+
expect(renderSuccess).toHaveBeenCalledWith({
175+
headline: ['Imported the following extensions from the dashboard:'],
176+
body: '• "titleA" at: extensions/title-a',
177+
})
178+
179+
// The toml file should not be created since we skipped
180+
const tomlPathA = joinPath(tmpDir, 'extensions', 'title-a', 'shopify.extension.toml')
181+
expect(fileExistsSync(tomlPathA)).toBe(false)
182+
})
183+
})
184+
185+
test('handles existing directory with write option', async () => {
186+
// Given
187+
const extensions = [flowExtensionA]
188+
189+
// When
190+
await inTemporaryDirectory(async (tmpDir) => {
191+
const app = testAppLinked({directory: tmpDir})
192+
193+
// Create the extensions directory
194+
const extensionsDir = joinPath(tmpDir, 'extensions')
195+
await mkdir(extensionsDir)
196+
197+
// Create the specific extension directory
198+
const extensionDir = joinPath(extensionsDir, 'title-a')
199+
await mkdir(extensionDir)
200+
201+
// Mock prompts:
202+
// 1. First prompt: select which extension to migrate (select flowExtensionA by its UUID)
203+
// 2. Second prompt: what to do with existing directory (select write)
204+
vi.mocked(renderSelectPrompt)
205+
// Select flowExtensionA
206+
.mockResolvedValueOnce('uuidA')
207+
// Write/overwrite existing directory
208+
.mockResolvedValueOnce('write')
209+
210+
await importExtensions({
211+
app,
212+
remoteApp: organizationApp,
213+
developerPlatformClient: testDeveloperPlatformClient(),
214+
extensionTypes: ['flow_action_definition'],
215+
extensions,
216+
buildTomlObject,
217+
})
218+
219+
// Then - expect the success message to be shown
220+
expect(renderSuccess).toHaveBeenCalledWith({
221+
headline: ['Imported the following extensions from the dashboard:'],
222+
body: '• "titleA" at: extensions/title-a',
223+
})
224+
225+
// The toml file should be created since we wrote/overwrote
226+
const tomlPathA = joinPath(tmpDir, 'extensions', 'title-a', 'shopify.extension.toml')
227+
expect(fileExistsSync(tomlPathA)).toBe(true)
228+
})
229+
})
230+
231+
test('handles existing directory with cancel option', async () => {
232+
// Given
233+
const extensions = [flowExtensionA]
234+
235+
// When
236+
await inTemporaryDirectory(async (tmpDir) => {
237+
const app = testAppLinked({directory: tmpDir})
238+
239+
// Create the extensions directory
240+
const extensionsDir = joinPath(tmpDir, 'extensions')
241+
await mkdir(extensionsDir)
242+
243+
// Create the specific extension directory
244+
const extensionDir = joinPath(extensionsDir, 'title-a')
245+
await mkdir(extensionDir)
246+
247+
// Mock prompts:
248+
// 1. First prompt: select which extension to migrate (select flowExtensionA by its UUID)
249+
// 2. Second prompt: what to do with existing directory (select cancel)
250+
vi.mocked(renderSelectPrompt)
251+
// Select flowExtensionA
252+
.mockResolvedValueOnce('uuidA')
253+
// Cancel the operation
254+
.mockResolvedValueOnce('cancel')
255+
256+
// Then - expect the function to throw an AbortSilentError
257+
await expect(
258+
importExtensions({
259+
app,
260+
remoteApp: organizationApp,
261+
developerPlatformClient: testDeveloperPlatformClient(),
262+
extensionTypes: ['flow_action_definition'],
263+
extensions,
264+
buildTomlObject,
265+
}),
266+
).rejects.toThrow(AbortSilentError)
267+
268+
// The toml file should not be created since we cancelled
269+
const tomlPathA = joinPath(tmpDir, 'extensions', 'title-a', 'shopify.extension.toml')
270+
expect(fileExistsSync(tomlPathA)).toBe(false)
271+
})
272+
})
273+
138274
test('selecting All imports all extensions', async () => {
139275
// Given
140276
const extensions = [

0 commit comments

Comments
 (0)