From 05b909301ec3658cbcabb08b401e626f3c073d01 Mon Sep 17 00:00:00 2001 From: Donald Merand Date: Mon, 18 May 2026 15:11:50 -0400 Subject: [PATCH 1/3] Add public shop_domain to CLI command analytics --- packages/app/src/cli/metadata.ts | 3 ++- packages/app/src/cli/services/dev.ts | 1 + .../src/cli/services/store-context.test.ts | 26 +++++++++++++++++++ .../app/src/cli/services/store-context.ts | 3 ++- packages/cli-kit/src/public/node/metadata.ts | 1 + .../cli-kit/src/public/node/monorail.test.ts | 2 ++ packages/cli-kit/src/public/node/monorail.ts | 3 ++- .../cli/services/store/attribution.test.ts | 4 ++- .../src/cli/services/store/attribution.ts | 6 ++++- .../theme/src/cli/utilities/theme-command.ts | 1 + 10 files changed, 45 insertions(+), 5 deletions(-) diff --git a/packages/app/src/cli/metadata.ts b/packages/app/src/cli/metadata.ts index 79f11dbde89..8a42399084d 100644 --- a/packages/app/src/cli/metadata.ts +++ b/packages/app/src/cli/metadata.ts @@ -10,7 +10,8 @@ type CmdFieldsFromMonorail = PickByPrefix & PickByPrefix & PickByPrefix & - PickByPrefix + PickByPrefix & + Pick type CmdSensitiveFieldsFromMonorail = PickByPrefix & PickByPrefix & diff --git a/packages/app/src/cli/services/dev.ts b/packages/app/src/cli/services/dev.ts index 2798403311b..fe5180840b0 100644 --- a/packages/app/src/cli/services/dev.ts +++ b/packages/app/src/cli/services/dev.ts @@ -476,6 +476,7 @@ async function logMetadataForDev(options: { cmd_dev_tunnel_custom_hash: tunnelType === 'custom' ? hashString(options.tunnelUrl) : undefined, cmd_dev_urls_updated: options.shouldUpdateURLs, store_fqdn_hash: hashString(options.storeFqdn), + shop_domain: options.storeFqdn, cmd_app_dependency_installation_skipped: options.devOptions.skipDependenciesInstallation, })) diff --git a/packages/app/src/cli/services/store-context.test.ts b/packages/app/src/cli/services/store-context.test.ts index 6b3bd1e0306..330d8ab59ae 100644 --- a/packages/app/src/cli/services/store-context.test.ts +++ b/packages/app/src/cli/services/store-context.test.ts @@ -174,6 +174,7 @@ describe('storeContext', () => { expect(meta).toEqual( expect.objectContaining({ store_fqdn_hash: hashString(mockStore.shopDomain), + shop_domain: mockStore.shopDomain, }), ) @@ -186,6 +187,31 @@ describe('storeContext', () => { }) }) + test('normalizes the selected store before logging metadata', async () => { + await inTemporaryDirectory(async (dir) => { + const unnormalizedStore = testOrganizationStore({shopId: 'store1', shopDomain: 'test-store'}) + vi.mocked(fetchStore).mockResolvedValue(unnormalizedStore) + await prepareAppFolder(mockApp, dir) + + await storeContext({appContextResult, forceReselectStore: false}) + + const meta = metadata.getAllPublicMetadata() + expect(meta).toEqual( + expect.objectContaining({ + store_fqdn_hash: hashString('test-store.myshopify.com'), + shop_domain: 'test-store.myshopify.com', + }), + ) + + const sensitiveMeta = metadata.getAllSensitiveMetadata() + expect(sensitiveMeta).toEqual( + expect.objectContaining({ + store_fqdn: 'test-store.myshopify.com', + }), + ) + }) + }) + test('adds hidden config to gitignore if needed', async () => { await inTemporaryDirectory(async (dir) => { await prepareAppFolder(mockApp, dir) diff --git a/packages/app/src/cli/services/store-context.ts b/packages/app/src/cli/services/store-context.ts index a0df8b837eb..9af3e3c9093 100644 --- a/packages/app/src/cli/services/store-context.ts +++ b/packages/app/src/cli/services/store-context.ts @@ -64,8 +64,8 @@ export async function storeContext({ selectedStore = await selectStore(allStores, organization, developerPlatformClient) } - await logMetadata(selectedStore, forceReselectStore) selectedStore.shopDomain = normalizeStoreFqdn(selectedStore.shopDomain) + await logMetadata(selectedStore, forceReselectStore) // Save the selected store in the hidden config file if (selectedStore.shopDomain !== cachedStoreURL || !devStoreUrlFromHiddenConfig) { @@ -82,6 +82,7 @@ async function logMetadata(selectedStore: OrganizationStore, resetUsed: boolean) await metadata.addPublicMetadata(() => ({ cmd_app_reset_used: resetUsed, store_fqdn_hash: hashString(selectedStore.shopDomain), + shop_domain: selectedStore.shopDomain, })) await metadata.addSensitiveMetadata(() => ({ diff --git a/packages/cli-kit/src/public/node/metadata.ts b/packages/cli-kit/src/public/node/metadata.ts index 1b6223f7b4b..79862d3c8a2 100644 --- a/packages/cli-kit/src/public/node/metadata.ts +++ b/packages/cli-kit/src/public/node/metadata.ts @@ -182,6 +182,7 @@ type CmdFieldsFromMonorail = PickByPrefix & PickByPrefix & PickByPrefix & PickByPrefix & + Pick & PickByPrefix const coreData = createRuntimeMetadataContainer< diff --git a/packages/cli-kit/src/public/node/monorail.test.ts b/packages/cli-kit/src/public/node/monorail.test.ts index 1a4df1ed46a..1f5fd29642a 100644 --- a/packages/cli-kit/src/public/node/monorail.test.ts +++ b/packages/cli-kit/src/public/node/monorail.test.ts @@ -70,6 +70,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, + shop_domain: 'shop.myshopify.com', }, { args: '--store shop.myshopify.com', @@ -98,6 +99,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, + shop_domain: 'shop.myshopify.com', args: '--store shop.myshopify.com', store_fqdn: 'shop.myshopify.com', }, diff --git a/packages/cli-kit/src/public/node/monorail.ts b/packages/cli-kit/src/public/node/monorail.ts index 66b9ce1e9db..982dd34ad2d 100644 --- a/packages/cli-kit/src/public/node/monorail.ts +++ b/packages/cli-kit/src/public/node/monorail.ts @@ -10,7 +10,7 @@ const url = 'https://monorail-edge.shopifysvc.com/v1/produce' type Optional = T | null // This is the topic name of the main event we log to Monorail, the command tracker -export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.24' +export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.25' export interface Schemas { [MONORAIL_COMMAND_TOPIC]: { @@ -46,6 +46,7 @@ export interface Schemas { is_employee: boolean store_fqdn_hash?: Optional store_fqdn_validated?: Optional + shop_domain?: Optional user_id: string // Any and all commands diff --git a/packages/store/src/cli/services/store/attribution.test.ts b/packages/store/src/cli/services/store/attribution.test.ts index 799a5971061..32d390141b8 100644 --- a/packages/store/src/cli/services/store/attribution.test.ts +++ b/packages/store/src/cli/services/store/attribution.test.ts @@ -12,7 +12,7 @@ describe('store command attribution', () => { vi.mocked(hashString).mockReturnValue('hashed-store') }) - test('records the sensitive, hashed, and validation state for a store fqdn', async () => { + test('records the sensitive, hashed, validation, and public shop domain for a store fqdn', async () => { await recordStoreFqdnMetadata('shop.myshopify.com', true) expect(addSensitiveMetadata).toHaveBeenCalledWith(expect.any(Function)) @@ -21,7 +21,9 @@ describe('store command attribution', () => { expect(vi.mocked(addPublicMetadata).mock.calls[0]![0]()).toEqual({ store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, + shop_domain: 'shop.myshopify.com', }) expect(hashString).toHaveBeenCalledWith('shop.myshopify.com') }) + }) diff --git a/packages/store/src/cli/services/store/attribution.ts b/packages/store/src/cli/services/store/attribution.ts index 7479bc7891e..aac5ef3760f 100644 --- a/packages/store/src/cli/services/store/attribution.ts +++ b/packages/store/src/cli/services/store/attribution.ts @@ -3,5 +3,9 @@ import {addPublicMetadata, addSensitiveMetadata} from '@shopify/cli-kit/node/met export async function recordStoreFqdnMetadata(storeFqdn: string, validated: boolean): Promise { await addSensitiveMetadata(() => ({store_fqdn: storeFqdn})) - await addPublicMetadata(() => ({store_fqdn_hash: hashString(storeFqdn), store_fqdn_validated: validated})) + await addPublicMetadata(() => ({ + store_fqdn_hash: hashString(storeFqdn), + store_fqdn_validated: validated, + shop_domain: storeFqdn, + })) } diff --git a/packages/theme/src/cli/utilities/theme-command.ts b/packages/theme/src/cli/utilities/theme-command.ts index 1c63e8b9970..59f9a462c63 100644 --- a/packages/theme/src/cli/utilities/theme-command.ts +++ b/packages/theme/src/cli/utilities/theme-command.ts @@ -387,6 +387,7 @@ export default abstract class ThemeCommand extends Command { const data = compileData() await addPublicMetadata(() => ({ store_fqdn_hash: hashString(session.storeFqdn), + shop_domain: session.storeFqdn, cmd_theme_timings: JSON.stringify(data.timings), cmd_theme_errors: JSON.stringify(data.errors), From 1758e62b101d52f452007a2e9dd145b99ca0d811 Mon Sep 17 00:00:00 2001 From: Donald Merand Date: Tue, 26 May 2026 16:40:08 -0400 Subject: [PATCH 2/3] Rename shop domain analytics field to store prefix --- packages/app/src/cli/metadata.ts | 3 +-- packages/app/src/cli/services/dev.ts | 2 +- packages/app/src/cli/services/store-context.test.ts | 4 ++-- packages/app/src/cli/services/store-context.ts | 2 +- packages/cli-kit/src/public/node/metadata.ts | 1 - packages/cli-kit/src/public/node/monorail.test.ts | 4 ++-- packages/cli-kit/src/public/node/monorail.ts | 2 +- packages/store/src/cli/services/store/attribution.test.ts | 2 +- packages/store/src/cli/services/store/attribution.ts | 2 +- packages/theme/src/cli/utilities/theme-command.ts | 2 +- 10 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/app/src/cli/metadata.ts b/packages/app/src/cli/metadata.ts index 8a42399084d..79f11dbde89 100644 --- a/packages/app/src/cli/metadata.ts +++ b/packages/app/src/cli/metadata.ts @@ -10,8 +10,7 @@ type CmdFieldsFromMonorail = PickByPrefix & PickByPrefix & PickByPrefix & - PickByPrefix & - Pick + PickByPrefix type CmdSensitiveFieldsFromMonorail = PickByPrefix & PickByPrefix & diff --git a/packages/app/src/cli/services/dev.ts b/packages/app/src/cli/services/dev.ts index fe5180840b0..383ccbf494a 100644 --- a/packages/app/src/cli/services/dev.ts +++ b/packages/app/src/cli/services/dev.ts @@ -476,7 +476,7 @@ async function logMetadataForDev(options: { cmd_dev_tunnel_custom_hash: tunnelType === 'custom' ? hashString(options.tunnelUrl) : undefined, cmd_dev_urls_updated: options.shouldUpdateURLs, store_fqdn_hash: hashString(options.storeFqdn), - shop_domain: options.storeFqdn, + store_shop_domain: options.storeFqdn, cmd_app_dependency_installation_skipped: options.devOptions.skipDependenciesInstallation, })) diff --git a/packages/app/src/cli/services/store-context.test.ts b/packages/app/src/cli/services/store-context.test.ts index 330d8ab59ae..837a8417f22 100644 --- a/packages/app/src/cli/services/store-context.test.ts +++ b/packages/app/src/cli/services/store-context.test.ts @@ -174,7 +174,7 @@ describe('storeContext', () => { expect(meta).toEqual( expect.objectContaining({ store_fqdn_hash: hashString(mockStore.shopDomain), - shop_domain: mockStore.shopDomain, + store_shop_domain: mockStore.shopDomain, }), ) @@ -199,7 +199,7 @@ describe('storeContext', () => { expect(meta).toEqual( expect.objectContaining({ store_fqdn_hash: hashString('test-store.myshopify.com'), - shop_domain: 'test-store.myshopify.com', + store_shop_domain: 'test-store.myshopify.com', }), ) diff --git a/packages/app/src/cli/services/store-context.ts b/packages/app/src/cli/services/store-context.ts index 9af3e3c9093..7b1ebcc8282 100644 --- a/packages/app/src/cli/services/store-context.ts +++ b/packages/app/src/cli/services/store-context.ts @@ -82,7 +82,7 @@ async function logMetadata(selectedStore: OrganizationStore, resetUsed: boolean) await metadata.addPublicMetadata(() => ({ cmd_app_reset_used: resetUsed, store_fqdn_hash: hashString(selectedStore.shopDomain), - shop_domain: selectedStore.shopDomain, + store_shop_domain: selectedStore.shopDomain, })) await metadata.addSensitiveMetadata(() => ({ diff --git a/packages/cli-kit/src/public/node/metadata.ts b/packages/cli-kit/src/public/node/metadata.ts index 79862d3c8a2..1b6223f7b4b 100644 --- a/packages/cli-kit/src/public/node/metadata.ts +++ b/packages/cli-kit/src/public/node/metadata.ts @@ -182,7 +182,6 @@ type CmdFieldsFromMonorail = PickByPrefix & PickByPrefix & PickByPrefix & PickByPrefix & - Pick & PickByPrefix const coreData = createRuntimeMetadataContainer< diff --git a/packages/cli-kit/src/public/node/monorail.test.ts b/packages/cli-kit/src/public/node/monorail.test.ts index 1f5fd29642a..a5379eb5958 100644 --- a/packages/cli-kit/src/public/node/monorail.test.ts +++ b/packages/cli-kit/src/public/node/monorail.test.ts @@ -70,7 +70,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - shop_domain: 'shop.myshopify.com', + store_shop_domain: 'shop.myshopify.com', }, { args: '--store shop.myshopify.com', @@ -99,7 +99,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - shop_domain: 'shop.myshopify.com', + store_shop_domain: 'shop.myshopify.com', args: '--store shop.myshopify.com', store_fqdn: 'shop.myshopify.com', }, diff --git a/packages/cli-kit/src/public/node/monorail.ts b/packages/cli-kit/src/public/node/monorail.ts index 982dd34ad2d..48b9c4af534 100644 --- a/packages/cli-kit/src/public/node/monorail.ts +++ b/packages/cli-kit/src/public/node/monorail.ts @@ -46,7 +46,7 @@ export interface Schemas { is_employee: boolean store_fqdn_hash?: Optional store_fqdn_validated?: Optional - shop_domain?: Optional + store_shop_domain?: Optional user_id: string // Any and all commands diff --git a/packages/store/src/cli/services/store/attribution.test.ts b/packages/store/src/cli/services/store/attribution.test.ts index 32d390141b8..6b9c26eaa79 100644 --- a/packages/store/src/cli/services/store/attribution.test.ts +++ b/packages/store/src/cli/services/store/attribution.test.ts @@ -21,7 +21,7 @@ describe('store command attribution', () => { expect(vi.mocked(addPublicMetadata).mock.calls[0]![0]()).toEqual({ store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - shop_domain: 'shop.myshopify.com', + store_shop_domain: 'shop.myshopify.com', }) expect(hashString).toHaveBeenCalledWith('shop.myshopify.com') }) diff --git a/packages/store/src/cli/services/store/attribution.ts b/packages/store/src/cli/services/store/attribution.ts index aac5ef3760f..9ca88b8f611 100644 --- a/packages/store/src/cli/services/store/attribution.ts +++ b/packages/store/src/cli/services/store/attribution.ts @@ -6,6 +6,6 @@ export async function recordStoreFqdnMetadata(storeFqdn: string, validated: bool await addPublicMetadata(() => ({ store_fqdn_hash: hashString(storeFqdn), store_fqdn_validated: validated, - shop_domain: storeFqdn, + store_shop_domain: storeFqdn, })) } diff --git a/packages/theme/src/cli/utilities/theme-command.ts b/packages/theme/src/cli/utilities/theme-command.ts index 59f9a462c63..d7360e3dda1 100644 --- a/packages/theme/src/cli/utilities/theme-command.ts +++ b/packages/theme/src/cli/utilities/theme-command.ts @@ -387,7 +387,7 @@ export default abstract class ThemeCommand extends Command { const data = compileData() await addPublicMetadata(() => ({ store_fqdn_hash: hashString(session.storeFqdn), - shop_domain: session.storeFqdn, + store_shop_domain: session.storeFqdn, cmd_theme_timings: JSON.stringify(data.timings), cmd_theme_errors: JSON.stringify(data.errors), From a1fe5ec624f415a5a67098b9bab28e24ef2c0640 Mon Sep 17 00:00:00 2001 From: Donald Merand Date: Thu, 28 May 2026 14:03:33 -0400 Subject: [PATCH 3/3] Use store_domain for public store analytics --- packages/app/src/cli/services/dev.test.ts | 77 ++++++++++++++++++- packages/app/src/cli/services/dev.ts | 2 +- .../src/cli/services/store-context.test.ts | 4 +- .../app/src/cli/services/store-context.ts | 2 +- .../cli-kit/src/public/node/monorail.test.ts | 4 +- packages/cli-kit/src/public/node/monorail.ts | 4 +- .../cli/services/store/attribution.test.ts | 4 +- .../src/cli/services/store/attribution.ts | 2 +- .../src/cli/utilities/theme-command.test.ts | 60 +++++++++++++++ .../theme/src/cli/utilities/theme-command.ts | 2 +- 10 files changed, 148 insertions(+), 13 deletions(-) diff --git a/packages/app/src/cli/services/dev.test.ts b/packages/app/src/cli/services/dev.test.ts index c1a7888f892..2806e279347 100644 --- a/packages/app/src/cli/services/dev.test.ts +++ b/packages/app/src/cli/services/dev.test.ts @@ -1,17 +1,92 @@ -import {warnIfScopesDifferBeforeDev, blockIfMigrationIncomplete} from './dev.js' +import {dev, warnIfScopesDifferBeforeDev, blockIfMigrationIncomplete} from './dev.js' +import {setupDevProcesses} from './dev/processes/setup-dev-processes.js' +import {renderDev} from './dev/ui.js' +import {fetchAppRemoteConfiguration} from './app/select-app.js' import { testAppLinked, testDeveloperPlatformClient, + testOrganization, testOrganizationApp, + testOrganizationStore, testProject, } from '../models/app/app.test-data.js' +import metadata from '../metadata.js' import {describe, expect, test, vi} from 'vitest' import {mockAndCaptureOutput} from '@shopify/cli-kit/node/testing/output' +import {hashString} from '@shopify/cli-kit/node/crypto' +import {reportAnalyticsEvent} from '@shopify/cli-kit/node/analytics' +import {checkPortAvailability, getAvailableTCPPort} from '@shopify/cli-kit/node/tcp' vi.mock('./dev/fetch.js') +vi.mock('./dev/processes/setup-dev-processes.js') +vi.mock('./dev/ui.js') +vi.mock('./app/select-app.js') +vi.mock('@shopify/cli-kit/node/analytics') vi.mock('@shopify/cli-kit/node/tcp') vi.mock('../utilities/mkcert.js') +describe('dev', () => { + test('logs store domain metadata when launching dev', async () => { + const store = testOrganizationStore({shopDomain: 'dev-store.myshopify.com'}) + const app = testAppLinked() + let publicMetadata: Record | undefined + let sensitiveMetadata: Record | undefined + + vi.mocked(fetchAppRemoteConfiguration).mockResolvedValue({name: 'Remote app', application_url: '', embedded: true}) + vi.mocked(getAvailableTCPPort).mockResolvedValue(3456) + vi.mocked(checkPortAvailability).mockResolvedValue(true) + vi.mocked(setupDevProcesses).mockResolvedValue({ + processes: [], + previewUrl: 'https://dev-store.myshopify.com/admin/apps/api-key', + graphiqlUrl: undefined, + devSessionStatusManager: {} as any, + }) + vi.mocked(renderDev).mockResolvedValue(undefined) + + const addPublicMetadata = vi.spyOn(metadata, 'addPublicMetadata').mockImplementation(async (getMetadata) => { + publicMetadata = getMetadata() as Record + }) + const addSensitiveMetadata = vi.spyOn(metadata, 'addSensitiveMetadata').mockImplementation(async (getMetadata) => { + sensitiveMetadata = getMetadata() as Record + }) + + await dev({ + app, + project: testProject({usesWorkspaces: true}), + remoteApp: testOrganizationApp({apiKey: 'api-key'}), + organization: testOrganization(), + specifications: [], + developerPlatformClient: testDeveloperPlatformClient({supportsDevSessions: false}), + store, + directory: app.directory, + update: false, + commandConfig: {} as any, + skipDependenciesInstallation: true, + tunnel: {mode: 'custom', url: 'https://localhost:3456'}, + }) + + expect(publicMetadata).toEqual( + expect.objectContaining({ + cmd_dev_tunnel_type: 'localhost', + cmd_dev_urls_updated: false, + store_fqdn_hash: hashString(store.shopDomain), + store_domain: store.shopDomain, + cmd_app_dependency_installation_skipped: true, + }), + ) + expect(sensitiveMetadata).toEqual( + expect.objectContaining({ + store_fqdn: store.shopDomain, + cmd_dev_tunnel_custom: undefined, + }), + ) + expect(reportAnalyticsEvent).toHaveBeenCalledWith({config: {}, exitMode: 'ok'}) + + addPublicMetadata.mockRestore() + addSensitiveMetadata.mockRestore() + }) +}) + describe('warnIfScopesDifferBeforeDev', () => { const appsWithScopes = (local: string, remote: string) => { const localApp = testAppLinked({}) diff --git a/packages/app/src/cli/services/dev.ts b/packages/app/src/cli/services/dev.ts index 383ccbf494a..0d30481a791 100644 --- a/packages/app/src/cli/services/dev.ts +++ b/packages/app/src/cli/services/dev.ts @@ -476,7 +476,7 @@ async function logMetadataForDev(options: { cmd_dev_tunnel_custom_hash: tunnelType === 'custom' ? hashString(options.tunnelUrl) : undefined, cmd_dev_urls_updated: options.shouldUpdateURLs, store_fqdn_hash: hashString(options.storeFqdn), - store_shop_domain: options.storeFqdn, + store_domain: options.storeFqdn, cmd_app_dependency_installation_skipped: options.devOptions.skipDependenciesInstallation, })) diff --git a/packages/app/src/cli/services/store-context.test.ts b/packages/app/src/cli/services/store-context.test.ts index 837a8417f22..bb83f8538da 100644 --- a/packages/app/src/cli/services/store-context.test.ts +++ b/packages/app/src/cli/services/store-context.test.ts @@ -174,7 +174,7 @@ describe('storeContext', () => { expect(meta).toEqual( expect.objectContaining({ store_fqdn_hash: hashString(mockStore.shopDomain), - store_shop_domain: mockStore.shopDomain, + store_domain: mockStore.shopDomain, }), ) @@ -199,7 +199,7 @@ describe('storeContext', () => { expect(meta).toEqual( expect.objectContaining({ store_fqdn_hash: hashString('test-store.myshopify.com'), - store_shop_domain: 'test-store.myshopify.com', + store_domain: 'test-store.myshopify.com', }), ) diff --git a/packages/app/src/cli/services/store-context.ts b/packages/app/src/cli/services/store-context.ts index 7b1ebcc8282..42827d58bb1 100644 --- a/packages/app/src/cli/services/store-context.ts +++ b/packages/app/src/cli/services/store-context.ts @@ -82,7 +82,7 @@ async function logMetadata(selectedStore: OrganizationStore, resetUsed: boolean) await metadata.addPublicMetadata(() => ({ cmd_app_reset_used: resetUsed, store_fqdn_hash: hashString(selectedStore.shopDomain), - store_shop_domain: selectedStore.shopDomain, + store_domain: selectedStore.shopDomain, })) await metadata.addSensitiveMetadata(() => ({ diff --git a/packages/cli-kit/src/public/node/monorail.test.ts b/packages/cli-kit/src/public/node/monorail.test.ts index a5379eb5958..72b7e89bb62 100644 --- a/packages/cli-kit/src/public/node/monorail.test.ts +++ b/packages/cli-kit/src/public/node/monorail.test.ts @@ -70,7 +70,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - store_shop_domain: 'shop.myshopify.com', + store_domain: 'shop.myshopify.com', }, { args: '--store shop.myshopify.com', @@ -99,7 +99,7 @@ describe('monorail', () => { user_id: '42', store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - store_shop_domain: 'shop.myshopify.com', + store_domain: 'shop.myshopify.com', args: '--store shop.myshopify.com', store_fqdn: 'shop.myshopify.com', }, diff --git a/packages/cli-kit/src/public/node/monorail.ts b/packages/cli-kit/src/public/node/monorail.ts index 48b9c4af534..bb8d1e9dcd2 100644 --- a/packages/cli-kit/src/public/node/monorail.ts +++ b/packages/cli-kit/src/public/node/monorail.ts @@ -10,7 +10,7 @@ const url = 'https://monorail-edge.shopifysvc.com/v1/produce' type Optional = T | null // This is the topic name of the main event we log to Monorail, the command tracker -export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.25' +export const MONORAIL_COMMAND_TOPIC = 'app_cli3_command/1.26' export interface Schemas { [MONORAIL_COMMAND_TOPIC]: { @@ -46,7 +46,7 @@ export interface Schemas { is_employee: boolean store_fqdn_hash?: Optional store_fqdn_validated?: Optional - store_shop_domain?: Optional + store_domain?: Optional user_id: string // Any and all commands diff --git a/packages/store/src/cli/services/store/attribution.test.ts b/packages/store/src/cli/services/store/attribution.test.ts index 6b9c26eaa79..905341e940e 100644 --- a/packages/store/src/cli/services/store/attribution.test.ts +++ b/packages/store/src/cli/services/store/attribution.test.ts @@ -12,7 +12,7 @@ describe('store command attribution', () => { vi.mocked(hashString).mockReturnValue('hashed-store') }) - test('records the sensitive, hashed, validation, and public shop domain for a store fqdn', async () => { + test('records the sensitive, hashed, validation, and public store domain for a store fqdn', async () => { await recordStoreFqdnMetadata('shop.myshopify.com', true) expect(addSensitiveMetadata).toHaveBeenCalledWith(expect.any(Function)) @@ -21,7 +21,7 @@ describe('store command attribution', () => { expect(vi.mocked(addPublicMetadata).mock.calls[0]![0]()).toEqual({ store_fqdn_hash: 'hashed-store', store_fqdn_validated: true, - store_shop_domain: 'shop.myshopify.com', + store_domain: 'shop.myshopify.com', }) expect(hashString).toHaveBeenCalledWith('shop.myshopify.com') }) diff --git a/packages/store/src/cli/services/store/attribution.ts b/packages/store/src/cli/services/store/attribution.ts index 9ca88b8f611..20e2b67ac07 100644 --- a/packages/store/src/cli/services/store/attribution.ts +++ b/packages/store/src/cli/services/store/attribution.ts @@ -6,6 +6,6 @@ export async function recordStoreFqdnMetadata(storeFqdn: string, validated: bool await addPublicMetadata(() => ({ store_fqdn_hash: hashString(storeFqdn), store_fqdn_validated: validated, - store_shop_domain: storeFqdn, + store_domain: storeFqdn, })) } diff --git a/packages/theme/src/cli/utilities/theme-command.test.ts b/packages/theme/src/cli/utilities/theme-command.test.ts index 564878c6c82..674a232850a 100644 --- a/packages/theme/src/cli/utilities/theme-command.test.ts +++ b/packages/theme/src/cli/utilities/theme-command.test.ts @@ -8,12 +8,18 @@ import {fileExistsSync} from '@shopify/cli-kit/node/fs' import {AbortError} from '@shopify/cli-kit/node/error' import {resolvePath} from '@shopify/cli-kit/node/path' import {renderConcurrent, renderConfirmationPrompt, renderError, renderWarning} from '@shopify/cli-kit/node/ui' +import {addPublicMetadata, addSensitiveMetadata} from '@shopify/cli-kit/node/metadata' +import {hashString} from '@shopify/cli-kit/node/crypto' import type {Writable} from 'stream' vi.mock('@shopify/cli-kit/node/session') vi.mock('@shopify/cli-kit/node/environments') vi.mock('@shopify/cli-kit/node/ui') +vi.mock('@shopify/cli-kit/node/metadata', () => ({ + addPublicMetadata: vi.fn(), + addSensitiveMetadata: vi.fn(), +})) vi.mock('./theme-store.js') vi.mock('@shopify/cli-kit/node/fs') @@ -168,6 +174,8 @@ describe('ThemeCommand', () => { let mockSession: AdminSession beforeEach(() => { + vi.clearAllMocks() + mockSession = { token: 'test-token', storeFqdn: 'test-store.myshopify.com', @@ -227,6 +235,15 @@ describe('ThemeCommand', () => { args: {}, context: undefined, }) + const publicMetadata = vi.mocked(addPublicMetadata).mock.calls.map(([getMetadata]) => getMetadata()) + expect(publicMetadata).toContainEqual( + expect.objectContaining({ + store_fqdn_hash: hashString(mockSession.storeFqdn), + store_domain: mockSession.storeFqdn, + }), + ) + const sensitiveMetadata = vi.mocked(addSensitiveMetadata).mock.calls.map(([getMetadata]) => getMetadata()) + expect(sensitiveMetadata).toContainEqual({store_fqdn: mockSession.storeFqdn}) }) test('single environment provided but not found in TOML - throws AbortError', async () => { @@ -319,6 +336,49 @@ describe('ThemeCommand', () => { ) }) + test('multiple environments provided - logs metadata for each authenticated session', async () => { + // Given + vi.mocked(loadEnvironment) + .mockResolvedValueOnce({store: 'store1.myshopify.com', development: true}) + .mockResolvedValueOnce({store: 'store2.myshopify.com', theme: 'staging'}) + vi.mocked(ensureThemeStore).mockImplementation((options: any) => options.store) + vi.mocked(ensureAuthenticatedThemes).mockImplementation(async (store) => ({ + token: 'test-token', + storeFqdn: store as string, + })) + vi.mocked(renderConcurrent).mockImplementation(async ({processes}) => { + for (const process of processes) { + // eslint-disable-next-line no-await-in-loop + await process.action({} as Writable, {} as Writable, {} as any) + } + }) + + await CommandConfig.load() + const command = new TestThemeCommand(['--environment', 'development', '--environment', 'staging'], CommandConfig) + + // When + await command.run() + + // Then + const publicMetadata = vi.mocked(addPublicMetadata).mock.calls.map(([getMetadata]) => getMetadata()) + expect(publicMetadata).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + store_fqdn_hash: hashString('store1.myshopify.com'), + store_domain: 'store1.myshopify.com', + }), + expect.objectContaining({ + store_fqdn_hash: hashString('store2.myshopify.com'), + store_domain: 'store2.myshopify.com', + }), + ]), + ) + const sensitiveMetadata = vi.mocked(addSensitiveMetadata).mock.calls.map(([getMetadata]) => getMetadata()) + expect(sensitiveMetadata).toEqual( + expect.arrayContaining([{store_fqdn: 'store1.myshopify.com'}, {store_fqdn: 'store2.myshopify.com'}]), + ) + }) + test("throws an AbortError if the path doesn't exist", async () => { await CommandConfig.load() const command = new TestThemeCommand([], CommandConfig) diff --git a/packages/theme/src/cli/utilities/theme-command.ts b/packages/theme/src/cli/utilities/theme-command.ts index d7360e3dda1..e80f625972e 100644 --- a/packages/theme/src/cli/utilities/theme-command.ts +++ b/packages/theme/src/cli/utilities/theme-command.ts @@ -387,7 +387,7 @@ export default abstract class ThemeCommand extends Command { const data = compileData() await addPublicMetadata(() => ({ store_fqdn_hash: hashString(session.storeFqdn), - store_shop_domain: session.storeFqdn, + store_domain: session.storeFqdn, cmd_theme_timings: JSON.stringify(data.timings), cmd_theme_errors: JSON.stringify(data.errors),