Skip to content

Commit 5791669

Browse files
Fix auto-upgrade telemetry reporting order
1 parent 616f360 commit 5791669

5 files changed

Lines changed: 83 additions & 6 deletions

File tree

packages/cli-kit/src/public/node/analytics.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {inTemporaryDirectory, touchFile, mkdir} from './fs.js'
1313
import {joinPath, dirname} from './path.js'
1414
import {publishMonorailEvent} from './monorail.js'
1515
import {mockAndCaptureOutput} from './testing/output.js'
16-
import {addPublicMetadata} from './metadata.js'
16+
import {addPublicMetadata, addSensitiveMetadata} from './metadata.js'
1717
import {sendErrorToBugsnag} from './error-handler.js'
1818
import {hashString} from './crypto.js'
1919
import * as store from '../../private/node/analytics/storage.js'
@@ -124,6 +124,40 @@ describe('event tracking', () => {
124124
})
125125
})
126126

127+
test('uses a recorded command end time when reporting after postrun work', async () => {
128+
await inProjectWithFile('package.json', async (args) => {
129+
// Given
130+
const commandContent = {command: 'dev', topic: 'app'}
131+
const startTime = currentDate.getTime() - 100
132+
const commandEndTime = currentDate.getTime()
133+
await startAnalytics({commandContent, args, currentTime: startTime})
134+
await addSensitiveMetadata(() => ({
135+
commandStartOptions: {
136+
startTime,
137+
endTime: commandEndTime,
138+
startCommand: commandContent.command,
139+
startArgs: args,
140+
},
141+
}))
142+
vi.setSystemTime(new Date(commandEndTime + 60000))
143+
144+
// When
145+
const config = {
146+
runHook: vi.fn().mockResolvedValue({successes: [], failures: []}),
147+
plugins: [],
148+
} as any
149+
await reportAnalyticsEvent({config, exitMode: 'ok'})
150+
151+
// Then
152+
expect(publishEventMock).toHaveBeenCalledOnce()
153+
expect(publishEventMock.mock.calls[0]![1]).toMatchObject({
154+
time_start: startTime,
155+
time_end: commandEndTime,
156+
total_time: 100,
157+
})
158+
})
159+
})
160+
127161
test('sends the expected data to Monorail when there is an error message', async () => {
128162
await inProjectWithFile('package.json', async (args) => {
129163
// Given

packages/cli-kit/src/public/node/analytics.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ async function buildPayload({config, errorMessage, exitMode}: ReportAnalyticsEve
117117
outputDebug('Unable to log analytics event - no information on executed command')
118118
return
119119
}
120-
const {startCommand, startArgs, startTime} = commandStartOptions
121-
const currentTime = new Date().getTime()
120+
const {startCommand, startArgs, startTime, endTime} = commandStartOptions
121+
const currentTime = endTime ?? new Date().getTime()
122122

123123
// All bundled plugins appear as `@shopify/cli` in the payload
124124
const {'@shopify/cli': internalPluginsPublic, ...externalPluginsPublic} = await fanoutHooks(

packages/cli-kit/src/public/node/hooks/postrun.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {autoUpgradeIfNeeded} from './postrun.js'
1+
import {autoUpgradeIfNeeded, hook} from './postrun.js'
22
import {mockAndCaptureOutput} from '../testing/output.js'
33
import {
44
getOutputUpdateCLIReminder,
@@ -9,6 +9,7 @@ import {
99
import {isMajorVersionChange} from '../version.js'
1010
import {inferPackageManagerForGlobalCLI} from '../is-global.js'
1111
import {addPublicMetadata} from '../metadata.js'
12+
import {reportAnalyticsEvent} from '../analytics.js'
1213
import {describe, expect, test, vi, afterEach} from 'vitest'
1314

1415
vi.mock('../upgrade.js', async (importOriginal) => {
@@ -38,6 +39,14 @@ vi.mock('../is-global.js', async (importOriginal) => {
3839
}
3940
})
4041

42+
vi.mock('../analytics.js', () => ({
43+
reportAnalyticsEvent: vi.fn().mockResolvedValue(undefined),
44+
}))
45+
46+
vi.mock('./deprecations.js', () => ({
47+
postrun: vi.fn(),
48+
}))
49+
4150
vi.mock('../metadata.js', async (importOriginal) => {
4251
const actual: any = await importOriginal()
4352
return {
@@ -61,9 +70,30 @@ vi.mock('../../../private/node/conf-store.js', async (importOriginal) => {
6170
afterEach(() => {
6271
mockAndCaptureOutput().clear()
6372
vi.mocked(addPublicMetadata).mockClear()
73+
vi.mocked(reportAnalyticsEvent).mockClear()
6474
vi.mocked(hasBlockingAutoUpgradeNotification).mockResolvedValue(false)
6575
})
6676

77+
describe('postrun hook', () => {
78+
test('reports analytics after auto-upgrade so auto-upgrade metadata is available', async () => {
79+
const config = {plugins: new Map(), runHook: vi.fn()} as any
80+
const Command = {id: 'app dev'} as any
81+
vi.mocked(versionToAutoUpgrade).mockReturnValue('3.100.0')
82+
vi.mocked(isMajorVersionChange).mockReturnValue(false)
83+
vi.mocked(runCLIUpgrade).mockResolvedValue(undefined)
84+
85+
await hook.call({} as any, {config, Command} as any)
86+
87+
expect(runCLIUpgrade).toHaveBeenCalled()
88+
expect(reportAnalyticsEvent).toHaveBeenCalledWith({config, exitMode: 'ok'})
89+
expect(vi.mocked(runCLIUpgrade).mock.invocationCallOrder[0]!).toBeLessThan(
90+
vi.mocked(reportAnalyticsEvent).mock.invocationCallOrder[0]!,
91+
)
92+
const calls = vi.mocked(addPublicMetadata).mock.calls.map((call) => call[0]())
93+
expect(calls).toContainEqual(expect.objectContaining({env_auto_upgrade_success: true}))
94+
})
95+
})
96+
6797
describe('autoUpgradeIfNeeded', () => {
6898
test('runs the upgrade when versionToAutoUpgrade returns a version', async () => {
6999
// Given

packages/cli-kit/src/public/node/hooks/postrun.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,16 @@ export function waitForPostRunHookAndExit(): void {
5656
export const hook: Hook.Postrun = async ({config, Command}) => {
5757
await detectStopCommand(Command as unknown as typeof Command)
5858

59-
const {reportAnalyticsEvent} = await import('../analytics.js')
60-
await reportAnalyticsEvent({config, exitMode: 'ok'})
59+
const metadata = await import('../metadata.js')
60+
const {commandStartOptions} = metadata.getAllSensitiveMetadata()
61+
if (commandStartOptions) {
62+
await metadata.addSensitiveMetadata(() => ({
63+
commandStartOptions: {
64+
...commandStartOptions,
65+
endTime: new Date().getTime(),
66+
},
67+
}))
68+
}
6169

6270
const {postrun: deprecationsHook} = await import('./deprecations.js')
6371
deprecationsHook(Command)
@@ -67,6 +75,10 @@ export const hook: Hook.Postrun = async ({config, Command}) => {
6775
outputDebug(`Completed command ${command}`)
6876

6977
if (!command.includes('notifications') && !command.includes('upgrade')) await autoUpgradeIfNeeded()
78+
79+
const {reportAnalyticsEvent} = await import('../analytics.js')
80+
await reportAnalyticsEvent({config, exitMode: 'ok'})
81+
7082
postRunHookCompleted = true
7183
}
7284

packages/cli-kit/src/public/node/metadata.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ const coreData = createRuntimeMetadataContainer<
189189
{
190190
commandStartOptions: {
191191
startTime: number
192+
endTime?: number
192193
startCommand: string
193194
startTopic?: string
194195
startArgs: string[]

0 commit comments

Comments
 (0)