Skip to content
Merged
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/config-link-extension-directory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli': patch
---

Prevent `app config link` from overwriting the root app config when run from an extension directory
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,29 @@ use_legacy_install_flow = true
"
`;

exports[`link > prompts for a new config name when linking a new app from an extension directory 1`] = `
"# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration

client_id = "new-api-key"
name = "app1"
application_url = "https://example.com"
embedded = true

[auth]
redirect_urls = [ "https://example.com/callback1" ]

[webhooks]
api_version = "2023-07"

[pos]
embedded = false

[access_scopes]
# Learn more at https://shopify.dev/docs/apps/tools/cli/configuration#access_scopes
use_legacy_install_flow = true
"
`;

exports[`link > replace arrays content with the remote one 1`] = `
"# Learn more about configuring your app at https://shopify.dev/docs/apps/tools/cli/configuration

Expand Down
39 changes: 38 additions & 1 deletion packages/app/src/cli/services/app/config/link.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {fetchAppRemoteConfiguration} from '../select-app.js'
import {DeveloperPlatformClient} from '../../../utilities/developer-platform-client.js'
import {MinimalAppIdentifiers, OrganizationApp} from '../../../models/organization.js'
import {beforeEach, describe, expect, test, vi} from 'vitest'
import {fileExistsSync, inTemporaryDirectory, readFile, writeFileSync} from '@shopify/cli-kit/node/fs'
import {fileExistsSync, inTemporaryDirectory, mkdir, readFile, writeFileSync} from '@shopify/cli-kit/node/fs'
import {joinPath} from '@shopify/cli-kit/node/path'
import {renderSuccess} from '@shopify/cli-kit/node/ui'
import {outputContent} from '@shopify/cli-kit/node/output'
Expand Down Expand Up @@ -514,6 +514,43 @@ describe('link', () => {
})
})

test('prompts for a new config name when linking a new app from an extension directory', async () => {
await inTemporaryDirectory(async (tmp) => {
const developerPlatformClient = buildDeveloperPlatformClient()
const extensionDirectory = joinPath(tmp, 'extensions', 'discount_function')
await mkdir(extensionDirectory)
const initialContent = 'client_id = "existing-api-key"\nname = "existing app"\n'
writeFileSync(joinPath(tmp, 'shopify.app.toml'), initialContent)
const options: LinkOptions = {
directory: extensionDirectory,
developerPlatformClient,
}
const localApp = {
configPath: joinPath(tmp, 'shopify.app.toml'),
configuration: {
name: 'existing app',
client_id: 'existing-api-key',
webhooks: {api_version: '2023-04'},
application_url: 'https://myapp.com',
} as CurrentAppConfiguration,
}
await mockLoadOpaqueAppWithApp(tmp, localApp, [], 'current')
vi.mocked(fetchOrCreateOrganizationApp).mockResolvedValue(
testOrganizationApp({
apiKey: 'new-api-key',
developerPlatformClient,
}),
)
vi.mocked(selectConfigName).mockResolvedValue('shopify.app.staging.toml')

await link(options)

expect(selectConfigName).toHaveBeenCalledWith(tmp, 'app1')
await expect(readFile(joinPath(tmp, 'shopify.app.toml'))).resolves.toBe(initialContent)
await expect(readFile(joinPath(tmp, 'shopify.app.staging.toml'))).resolves.toMatchSnapshot()
})
})

test('updates the shopify.app.toml when it already exists and is unlinked', async () => {
await inTemporaryDirectory(async (tmp) => {
// Given
Expand Down
7 changes: 4 additions & 3 deletions packages/app/src/cli/services/app/config/link.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default async function link(options: LinkOptions, shouldRenderSuccess = t
const flags = remoteApp.flags
const localAppOptions = await loadLocalAppOptions(options, specifications, flags, remoteApp.apiKey)
const configFileName = await loadConfigurationFileName(remoteApp, options, {
appDirectory: localAppOptions.appDirectory,
appDirectory: localAppOptions.appDirectory ?? appDirectory,
})

await logMetadataForLoadedContext(remoteApp, developerPlatformClient.organizationSource)
Expand Down Expand Up @@ -304,14 +304,15 @@ async function loadConfigurationFileName(
const cache = getCachedCommandInfo()
if (cache?.selectedToml) return cache.selectedToml as AppConfigurationFileName

const existingTomls = await getTomls(options.directory)
const configDirectory = localAppInfo.appDirectory ?? options.directory
const existingTomls = await getTomls(configDirectory)
const currentToml = existingTomls[remoteApp.apiKey]
if (currentToml) return currentToml

// If no TOML files exist at all, use the default filename without prompting
if (isEmpty(existingTomls)) return 'shopify.app.toml'

return selectConfigName(localAppInfo.appDirectory ?? options.directory, remoteApp.title)
return selectConfigName(configDirectory, remoteApp.title)
}

/**
Expand Down
Loading