From d475649c258f150fad98dac074c8d247c1b700af Mon Sep 17 00:00:00 2001 From: Luca Forstner Date: Wed, 2 Apr 2025 16:19:06 +0200 Subject: [PATCH] feat(nextjs): Add release injection in Turbopack --- .../nextjs-turbo/next.config.js | 3 +++ .../client-trace-propagation.test.ts | 10 ++++++++++ packages/nextjs/src/client/index.ts | 2 ++ packages/nextjs/src/config/withSentryConfig.ts | 17 ++++++++++++----- packages/nextjs/src/edge/index.ts | 2 ++ packages/nextjs/src/server/index.ts | 2 ++ 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-turbo/next.config.js b/dev-packages/e2e-tests/test-applications/nextjs-turbo/next.config.js index e09e64bac6a2..a0d2b254bc42 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-turbo/next.config.js +++ b/dev-packages/e2e-tests/test-applications/nextjs-turbo/next.config.js @@ -9,4 +9,7 @@ const nextConfig = { module.exports = withSentryConfig(nextConfig, { silent: true, + release: { + name: 'foobar123', + }, }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/pages-router/client-trace-propagation.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/pages-router/client-trace-propagation.test.ts index 52e492b3f234..032aa55b3116 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/pages-router/client-trace-propagation.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/pages-router/client-trace-propagation.test.ts @@ -28,4 +28,14 @@ test('Should propagate traces from server to client in pages router', async ({ p expect(serverTransaction.contexts?.trace?.trace_id).toBeDefined(); expect(pageloadTransaction.contexts?.trace?.trace_id).toBe(serverTransaction.contexts?.trace?.trace_id); + + await test.step('release was successfully injected on the serverside', () => { + // Release as defined in next.config.js + expect(serverTransaction.release).toBe('foobar123'); + }); + + await test.step('release was successfully injected on the clientside', () => { + // Release as defined in next.config.js + expect(pageloadTransaction.release).toBe('foobar123'); + }); }); diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 031f5a412a37..37589467f5d6 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -20,6 +20,7 @@ const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & { _sentryRewriteFramesAssetPrefixPath: string; _sentryAssetPrefix?: string; _sentryBasePath?: string; + _sentryRelease?: string; _experimentalThirdPartyOriginStackFrames?: string; }; @@ -31,6 +32,7 @@ export function init(options: BrowserOptions): Client | undefined { const opts = { environment: getVercelEnv(true) || process.env.NODE_ENV, defaultIntegrations: getDefaultIntegrations(options), + release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease, ...options, } satisfies BrowserOptions; diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index 7250af15ff8e..4b24d8370393 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -50,6 +50,8 @@ function getFinalConfigObject( incomingUserNextConfigObject: NextConfigObject, userSentryOptions: SentryBuildOptions, ): NextConfigObject { + const releaseName = userSentryOptions.release?.name ?? getSentryRelease() ?? getGitRevision(); + if (userSentryOptions?.tunnelRoute) { if (incomingUserNextConfigObject.output === 'export') { if (!showedExportModeTunnelWarning) { @@ -64,7 +66,7 @@ function getFinalConfigObject( } } - setUpBuildTimeVariables(incomingUserNextConfigObject, userSentryOptions); + setUpBuildTimeVariables(incomingUserNextConfigObject, userSentryOptions, releaseName); const nextJsVersion = getNextjsVersion(); @@ -207,8 +209,6 @@ function getFinalConfigObject( ); } - const releaseName = userSentryOptions.release?.name ?? getSentryRelease() ?? getGitRevision(); - return { ...incomingUserNextConfigObject, webpack: constructWebpackConfigFunction(incomingUserNextConfigObject, userSentryOptions, releaseName), @@ -291,8 +291,11 @@ function setUpTunnelRewriteRules(userNextConfig: NextConfigObject, tunnelPath: s }; } -// TODO: For Turbopack we need to pass the release name here and pick it up in the SDK -function setUpBuildTimeVariables(userNextConfig: NextConfigObject, userSentryOptions: SentryBuildOptions): void { +function setUpBuildTimeVariables( + userNextConfig: NextConfigObject, + userSentryOptions: SentryBuildOptions, + releaseName: string | undefined, +): void { const assetPrefix = userNextConfig.assetPrefix || userNextConfig.basePath || ''; const basePath = userNextConfig.basePath ?? ''; const rewritesTunnelPath = @@ -335,6 +338,10 @@ function setUpBuildTimeVariables(userNextConfig: NextConfigObject, userSentryOpt buildTimeVariables._experimentalThirdPartyOriginStackFrames = 'true'; } + if (releaseName) { + buildTimeVariables._sentryRelease = releaseName; + } + if (typeof userNextConfig.env === 'object') { userNextConfig.env = { ...buildTimeVariables, ...userNextConfig.env }; } else if (userNextConfig.env === undefined) { diff --git a/packages/nextjs/src/edge/index.ts b/packages/nextjs/src/edge/index.ts index 8b21abd18917..82eb4f597ba6 100644 --- a/packages/nextjs/src/edge/index.ts +++ b/packages/nextjs/src/edge/index.ts @@ -26,6 +26,7 @@ export type EdgeOptions = VercelEdgeOptions; const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & { _sentryRewriteFramesDistDir?: string; + _sentryRelease?: string; }; /** Inits the Sentry NextJS SDK on the Edge Runtime. */ @@ -48,6 +49,7 @@ export function init(options: VercelEdgeOptions = {}): void { const opts = { defaultIntegrations: customDefaultIntegrations, + release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease, ...options, }; diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 5edd62d15bdd..da18f0fa459c 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -43,6 +43,7 @@ export { captureUnderscoreErrorException } from '../common/pages-router-instrume const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & { _sentryRewriteFramesDistDir?: string; _sentryRewritesTunnelPath?: string; + _sentryRelease?: string; }; /** @@ -115,6 +116,7 @@ export function init(options: NodeOptions): NodeClient | undefined { const opts: NodeOptions = { environment: process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV, + release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease, defaultIntegrations: customDefaultIntegrations, ...options, };