diff --git a/packages/cli-kit/src/public/node/analytics.test.ts b/packages/cli-kit/src/public/node/analytics.test.ts index 437ee1bb93..39871ecc46 100644 --- a/packages/cli-kit/src/public/node/analytics.test.ts +++ b/packages/cli-kit/src/public/node/analytics.test.ts @@ -14,6 +14,7 @@ import {joinPath, dirname} from './path.js' import {publishMonorailEvent} from './monorail.js' import {mockAndCaptureOutput} from './testing/output.js' import {addPublicMetadata} from './metadata.js' +import {sendErrorToBugsnag} from './error-handler.js' import * as store from '../../private/node/analytics/storage.js' import {startAnalytics} from '../../private/node/analytics.js' import {hashString} from '../../public/node/crypto.js' @@ -29,6 +30,7 @@ vi.mock('../../public/node/crypto.js') vi.mock('../../version.js') vi.mock('./monorail.js') vi.mock('./cli.js') +vi.mock('./error-handler.js') describe('event tracking', () => { const currentDate = new Date(Date.UTC(2022, 1, 1, 10, 0, 0)) @@ -256,6 +258,36 @@ describe('event tracking', () => { }) }) + test('reports telemetry failures to Bugsnag', async () => { + await inProjectWithFile('package.json', async (args) => { + // Given + const commandContent = {command: 'dev', topic: 'app'} + const telemetryError = new Error('OTLP endpoint unavailable') + vi.mocked(os.platformAndArch).mockImplementationOnce(() => { + throw telemetryError + }) + vi.mocked(sendErrorToBugsnag).mockResolvedValue({ + error: telemetryError, + reported: true, + unhandled: false, + }) + const outputMock = mockAndCaptureOutput() + await startAnalytics({commandContent, args}) + + // When + const config = { + runHook: vi.fn().mockResolvedValue({successes: [], failures: []}), + plugins: [], + } as any + await reportAnalyticsEvent({config, exitMode: 'ok'}) + + // Then + expect(sendErrorToBugsnag).toHaveBeenCalledOnce() + expect(sendErrorToBugsnag).toHaveBeenCalledWith(telemetryError, 'expected_error') + expect(outputMock.debug()).toMatch('Failed to report usage analytics: OTLP endpoint unavailable') + }) + }) + describe('recordTiming', () => { test('delegates to store.recordTiming', () => { // Given diff --git a/packages/cli-kit/src/public/node/analytics.ts b/packages/cli-kit/src/public/node/analytics.ts index 03345a195b..bdf51e12d6 100644 --- a/packages/cli-kit/src/public/node/analytics.ts +++ b/packages/cli-kit/src/public/node/analytics.ts @@ -2,6 +2,7 @@ import {alwaysLogAnalytics, alwaysLogMetrics, analyticsDisabled, isShopify} from import * as metadata from './metadata.js' import {publishMonorailEvent, MONORAIL_COMMAND_TOPIC} from './monorail.js' import {fanoutHooks} from './plugins.js' +import {sendErrorToBugsnag} from './error-handler.js' import { recordTiming as storageRecordTiming, recordError as storageRecordError, @@ -105,6 +106,7 @@ export async function reportAnalyticsEvent(options: ReportAnalyticsEventOptions) message = message.concat(`: ${error.message}`) } outputDebug(message) + await sendErrorToBugsnag(error, 'expected_error') } }