From d2ef14fca0539bc9b00573c53a6ded4faa2e768e Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 8 Sep 2026 11:46:29 +0200 Subject: [PATCH 1/4] feat(nextjs,vercel-edge,bundler-plugins)!: Drop `vercel-` prefix from default environment and deploys On Vercel, @sentry/nextjs and @sentry/vercel-edge now default environment to the plain Vercel target environment instead of vercel-production / vercel-preview, and the deploys the bundler plugins create automatically on Vercel use the same value. The prefix only matched events for Next.js; every other SDK defaults environment to production, so the auto-created deploy never showed up next to their events. Both sides now read VERCEL_TARGET_ENV with VERCEL_ENV as fallback through one getVercelEnv helper in @sentry/core, which also fixes mismatching names for Vercel custom environments. Fixes getsentry/sentry-javascript-bundler-plugins#849 Refs BUNDLER-98 --- CHANGELOG.md | 1 + MIGRATION.md | 13 +++++ .../src/core/options-mapping.ts | 6 ++- .../test/core/option-mappings.test.ts | 2 +- packages/core/src/index.ts | 1 + packages/core/src/utils/vercel.ts | 20 ++++++++ packages/core/test/lib/utils/vercel.test.ts | 47 +++++++++++++++++++ packages/nextjs/src/client/index.ts | 10 +++- packages/nextjs/src/common/getVercelEnv.ts | 9 ---- packages/nextjs/src/server/index.ts | 11 ++++- packages/vercel-edge/src/sdk.ts | 2 +- packages/vercel-edge/src/utils/vercel.ts | 13 ----- 12 files changed, 105 insertions(+), 30 deletions(-) create mode 100644 packages/core/src/utils/vercel.ts create mode 100644 packages/core/test/lib/utils/vercel.test.ts delete mode 100644 packages/nextjs/src/common/getVercelEnv.ts delete mode 100644 packages/vercel-edge/src/utils/vercel.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 39ca8cb79060..e6543c1de118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Work in this release was contributed by @psh4607, @thijsw, @trinitiwowka, @nehaprasad-dev, @JealousGx, @Jxxunnn, @eddie333016, @davidmurdoch, @yashschandra, @atharv-sys32, @AG0708, @birkskyum, @mkly, @mcbbugu, @suhailopensource, @zkasuran, @mohd-akram, @RealBhupesh, @halillusion, @psang39, and @hafzism. Thank you for your contributions! +- feat(nextjs,vercel-edge,bundler-plugins)!: Drop the `vercel-` prefix from the default `environment` on Vercel and from the deploys the bundler plugins create there. Both now use the plain value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name), with `VERCEL_ENV` as fallback. See the [migration guide](./MIGRATION.md#sentrynextjs) for details. - feat(langchain)!: Emit `gen_ai.pipeline.name` instead of `langchain.chain.name` on LangChain chain spans. The attribute is omitted when the chain is unnamed. - feat(deno)!: Rename several default integrations to match the other SDKs ([#22404](https://github.com/getsentry/sentry-javascript/pull/22404)). The `deno*Integration` exports are kept as deprecated aliases. If you were relying on the names (for example, to disable them), then note that these have changed: - `DenoAmqplib` => `Amqplib` diff --git a/MIGRATION.md b/MIGRATION.md index 861e092d7dbb..a8b39792964c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1156,6 +1156,15 @@ Note that v10.30.0 deprecated a top-level `reactComponentAnnotation` in favour o On Turbopack, component annotation requires Next.js 16+. The SDK now warns at build time if annotation is enabled on an older Next.js version, where it previously did nothing silently. +**Default `environment` on Vercel no longer has a `vercel-` prefix:** On Vercel, `@sentry/nextjs` and `@sentry/vercel-edge` now default `environment` to the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) instead of `vercel-production` / `vercel-preview`. Update alert rules, dashboards and saved searches that reference the old names, or keep them by setting `environment` explicitly: + +```js +// use NEXT_PUBLIC_VERCEL_ENV in the client config +Sentry.init({ + environment: process.env.VERCEL_ENV ? `vercel-${process.env.VERCEL_ENV}` : undefined, +}); +``` + **Vercel AI no longer supported on Edge runtime:** We now rely on diagnostics channels for our Vercel AI instrumentation, which does not work on the Edge runtime. Because of this, monitoring of the `ai` package is no longer supported on Edge. Note that Edge is deprecated. ### Cloudflare: `nodejs_compat` compatibility flag is now required @@ -1689,6 +1698,10 @@ moved under the `webpack` option in v10; use the replacement listed below instea The deprecated `sourceMapsUploadOptions` and other deprecated Vite/build plugin options were removed from `@sentry/astro`, `@sentry/nuxt` and `@sentry/sveltekit`. Use the top-level equivalents (e.g. `sourcemaps`, `release`, `authToken`, `org`, `project`, `telemetry`) instead. +### Bundler plugins: Vercel deploys use the plain Vercel environment name + +Deploys that the bundler plugins create automatically on Vercel now use the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) as their environment instead of `vercel-production` / `vercel-preview`. This matches the new default runtime `environment` of `@sentry/nextjs` and `@sentry/vercel-edge`, and the `production` default of all other SDKs. If your events use a different environment, set `release.deploy.env` to the same value, or set `release.deploy` to `false` to opt out. + ### Removed `unstable_` bundler plugin options The `unstable_sentry*PluginOptions` escape hatch was removed from every SDK. It existed because the Sentry diff --git a/packages/bundler-plugins/src/core/options-mapping.ts b/packages/bundler-plugins/src/core/options-mapping.ts index 42629509a737..5b342117ad6b 100644 --- a/packages/bundler-plugins/src/core/options-mapping.ts +++ b/packages/bundler-plugins/src/core/options-mapping.ts @@ -1,3 +1,4 @@ +import { getVercelEnv } from '@sentry/core'; import type { Logger } from './logger'; import type { Options as UserOptions, @@ -162,9 +163,10 @@ export function normalizeUserOptions(userOptions: UserOptions): NormalizedOption } } - if (options.release.deploy === undefined && process.env['VERCEL'] && process.env['VERCEL_TARGET_ENV']) { + const vercelEnv = getVercelEnv(false); + if (options.release.deploy === undefined && process.env['VERCEL'] && vercelEnv) { options.release.deploy = { - env: `vercel-${process.env['VERCEL_TARGET_ENV']}`, + env: vercelEnv, url: process.env['VERCEL_URL'] ? `https://${process.env['VERCEL_URL']}` : undefined, }; } diff --git a/packages/bundler-plugins/test/core/option-mappings.test.ts b/packages/bundler-plugins/test/core/option-mappings.test.ts index e1976d146c37..08998f525fc7 100644 --- a/packages/bundler-plugins/test/core/option-mappings.test.ts +++ b/packages/bundler-plugins/test/core/option-mappings.test.ts @@ -139,7 +139,7 @@ describe('normalizeUserOptions()', () => { const normalizedOptions = normalizeUserOptions(userOptions); expect(normalizedOptions.release.deploy).toEqual({ - env: 'vercel-production', + env: 'production', url: 'https://my-app.vercel.app', }); }); diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index dc01c23fda8f..0eef1ba9f64c 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -81,6 +81,7 @@ export { filterCookies as _INTERNAL_filterCookies } from './utils/data-collectio export { filterQueryParams as _INTERNAL_filterQueryParams } from './utils/data-collection/filterQueryParams'; export { filterCollectedUrl, filterCollectedUrlQuery } from './utils/data-collection/filterCollectedUrl'; export { envToBool } from './utils/envToBool'; +export { getVercelEnv } from './utils/vercel'; export { applyScopeDataToEvent, mergeScopeData, getCombinedScopeData } from './utils/scopeData'; export { prepareEvent } from './utils/prepareEvent'; export type { ExclusiveEventHintOrCaptureContext } from './utils/prepareEvent'; diff --git a/packages/core/src/utils/vercel.ts b/packages/core/src/utils/vercel.ts new file mode 100644 index 000000000000..5795ef3ab78d --- /dev/null +++ b/packages/core/src/utils/vercel.ts @@ -0,0 +1,20 @@ +/** + * Returns the Vercel environment the code is running in, determined by the `VERCEL_TARGET_ENV` environment variable + * with `VERCEL_ENV` as fallback. Returns `undefined` outside of Vercel. + * + * `VERCEL_TARGET_ENV` also carries the name of custom Vercel environments, whereas `VERCEL_ENV` only ever holds + * `production`, `preview` or `development`. SDKs use this as the default `environment` and the bundler plugins use it + * for the deploys they create, so the two stay in sync and deploys show up next to events. + * + * @param isClient Whether to read the `NEXT_PUBLIC_` prefixed variants that Next.js exposes to the browser. + */ +export function getVercelEnv(isClient: boolean): string | undefined { + if (typeof process === 'undefined') { + return undefined; + } + + const vercelEnv = isClient + ? process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV + : process.env.VERCEL_TARGET_ENV || process.env.VERCEL_ENV; + return vercelEnv || undefined; +} diff --git a/packages/core/test/lib/utils/vercel.test.ts b/packages/core/test/lib/utils/vercel.test.ts new file mode 100644 index 000000000000..989618d3745d --- /dev/null +++ b/packages/core/test/lib/utils/vercel.test.ts @@ -0,0 +1,47 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { getVercelEnv } from '../../../src/utils/vercel'; + +describe('getVercelEnv', () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + delete process.env.VERCEL_ENV; + delete process.env.VERCEL_TARGET_ENV; + delete process.env.NEXT_PUBLIC_VERCEL_ENV; + delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('returns undefined when no Vercel env vars are set', () => { + expect(getVercelEnv(false)).toBeUndefined(); + expect(getVercelEnv(true)).toBeUndefined(); + }); + + it('returns VERCEL_TARGET_ENV without a prefix', () => { + process.env.VERCEL_ENV = 'preview'; + process.env.VERCEL_TARGET_ENV = 'staging'; + + expect(getVercelEnv(false)).toBe('staging'); + }); + + it('falls back to VERCEL_ENV when VERCEL_TARGET_ENV is not set', () => { + process.env.VERCEL_ENV = 'production'; + + expect(getVercelEnv(false)).toBe('production'); + }); + + it('uses the NEXT_PUBLIC_ variants on the client', () => { + process.env.VERCEL_TARGET_ENV = 'server-only'; + process.env.NEXT_PUBLIC_VERCEL_ENV = 'preview'; + process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV = 'staging'; + + expect(getVercelEnv(true)).toBe('staging'); + + delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; + expect(getVercelEnv(true)).toBe('preview'); + }); +}); diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 5c5d3ffc2c85..76d613413c67 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -2,12 +2,18 @@ // can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703 /* eslint-disable import/export */ import type { Client, EventProcessor, Integration } from '@sentry/core'; -import { addEventProcessor, applySdkMetadata, consoleSandbox, getGlobalScope, GLOBAL_OBJ } from '@sentry/core'; +import { + addEventProcessor, + applySdkMetadata, + consoleSandbox, + getGlobalScope, + getVercelEnv, + GLOBAL_OBJ, +} from '@sentry/core'; import type { BrowserOptions } from '@sentry/react'; import { getDefaultIntegrations as getReactDefaultIntegrations, init as reactInit } from '@sentry/react'; import { DEBUG_BUILD } from '../common/debug-build'; import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor'; -import { getVercelEnv } from '../common/getVercelEnv'; import { isRedirectNavigationError } from '../common/nextNavigationErrorUtils'; import { browserTracingIntegration } from './browserTracingIntegration'; import { nextjsClientStackFrameNormalizationIntegration } from './clientNormalizationIntegration'; diff --git a/packages/nextjs/src/common/getVercelEnv.ts b/packages/nextjs/src/common/getVercelEnv.ts deleted file mode 100644 index 98755c91409d..000000000000 --- a/packages/nextjs/src/common/getVercelEnv.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Returns an environment setting value determined by Vercel's `VERCEL_ENV` environment variable. - * - * @param isClient Flag to indicate whether to use the `NEXT_PUBLIC_` prefixed version of the environment variable. - */ -export function getVercelEnv(isClient: boolean): string | undefined { - const vercelEnvVar = isClient ? process.env.NEXT_PUBLIC_VERCEL_ENV : process.env.VERCEL_ENV; - return vercelEnvVar ? `vercel-${vercelEnvVar}` : undefined; -} diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index 33d417507b39..cd18785fa6e8 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -3,12 +3,19 @@ /* eslint-disable import/export */ import { HTTP_TARGET, URL_QUERY } from '@sentry/conventions/attributes'; import type { EventProcessor } from '@sentry/core'; -import { applySdkMetadata, debug, getClient, getGlobalScope, getRootSpan, GLOBAL_OBJ } from '@sentry/core'; +import { + applySdkMetadata, + debug, + getClient, + getGlobalScope, + getRootSpan, + getVercelEnv, + GLOBAL_OBJ, +} from '@sentry/core'; import type { NodeClient, NodeOptions } from '@sentry/node'; import { getDefaultIntegrations, httpIntegration, init as nodeInit } from '@sentry/node'; import { DEBUG_BUILD } from '../common/debug-build'; import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor'; -import { getVercelEnv } from '../common/getVercelEnv'; import { isPrerenderControlFlowError } from '../common/nextNavigationErrorUtils'; import { TRANSACTION_ATTR_SHOULD_DROP_TRANSACTION } from '../common/span-attributes-with-logic-attached'; import { isBuild } from '../common/utils/isBuild'; diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index 5e6c2926ccbb..469fa56445fe 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -10,6 +10,7 @@ import { functionToStringIntegration, getCurrentScope, getIntegrationsToSetup, + getVercelEnv, GLOBAL_OBJ, linkedErrorsIntegration, requestDataIntegration, @@ -25,7 +26,6 @@ import { VercelEdgeClient } from './client'; import { winterCGFetchIntegration } from './integrations/wintercg-fetch'; import { makeEdgeTransport } from './transports'; import type { VercelEdgeOptions } from './types'; -import { getVercelEnv } from './utils/vercel'; declare const process: { env: Record; diff --git a/packages/vercel-edge/src/utils/vercel.ts b/packages/vercel-edge/src/utils/vercel.ts deleted file mode 100644 index cb640aaff1c5..000000000000 --- a/packages/vercel-edge/src/utils/vercel.ts +++ /dev/null @@ -1,13 +0,0 @@ -declare const process: { - env: Record; -}; - -/** - * Returns an environment setting value determined by Vercel's `VERCEL_ENV` environment variable. - * - * @param isClient Flag to indicate whether to use the `NEXT_PUBLIC_` prefixed version of the environment variable. - */ -export function getVercelEnv(isClient: boolean): string | undefined { - const vercelEnvVar = isClient ? process.env.NEXT_PUBLIC_VERCEL_ENV : process.env.VERCEL_ENV; - return vercelEnvVar ? `vercel-${vercelEnvVar}` : undefined; -} From cf98957c7c1c0ea75c5d35f441598673fdbd7c59 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 8 Sep 2026 11:49:08 +0200 Subject: [PATCH 2/4] chore: Remove changelog entry --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6543c1de118..39ca8cb79060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ Work in this release was contributed by @psh4607, @thijsw, @trinitiwowka, @nehaprasad-dev, @JealousGx, @Jxxunnn, @eddie333016, @davidmurdoch, @yashschandra, @atharv-sys32, @AG0708, @birkskyum, @mkly, @mcbbugu, @suhailopensource, @zkasuran, @mohd-akram, @RealBhupesh, @halillusion, @psang39, and @hafzism. Thank you for your contributions! -- feat(nextjs,vercel-edge,bundler-plugins)!: Drop the `vercel-` prefix from the default `environment` on Vercel and from the deploys the bundler plugins create there. Both now use the plain value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name), with `VERCEL_ENV` as fallback. See the [migration guide](./MIGRATION.md#sentrynextjs) for details. - feat(langchain)!: Emit `gen_ai.pipeline.name` instead of `langchain.chain.name` on LangChain chain spans. The attribute is omitted when the chain is unnamed. - feat(deno)!: Rename several default integrations to match the other SDKs ([#22404](https://github.com/getsentry/sentry-javascript/pull/22404)). The `deno*Integration` exports are kept as deprecated aliases. If you were relying on the names (for example, to disable them), then note that these have changed: - `DenoAmqplib` => `Amqplib` From 82e54b69a0895b6b833961725967847faa5e0bcc Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 8 Sep 2026 11:50:09 +0200 Subject: [PATCH 3/4] docs: Trim Vercel environment migration entries --- MIGRATION.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/MIGRATION.md b/MIGRATION.md index a8b39792964c..814446198405 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1156,14 +1156,7 @@ Note that v10.30.0 deprecated a top-level `reactComponentAnnotation` in favour o On Turbopack, component annotation requires Next.js 16+. The SDK now warns at build time if annotation is enabled on an older Next.js version, where it previously did nothing silently. -**Default `environment` on Vercel no longer has a `vercel-` prefix:** On Vercel, `@sentry/nextjs` and `@sentry/vercel-edge` now default `environment` to the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) instead of `vercel-production` / `vercel-preview`. Update alert rules, dashboards and saved searches that reference the old names, or keep them by setting `environment` explicitly: - -```js -// use NEXT_PUBLIC_VERCEL_ENV in the client config -Sentry.init({ - environment: process.env.VERCEL_ENV ? `vercel-${process.env.VERCEL_ENV}` : undefined, -}); -``` +**Default `environment` on Vercel no longer has a `vercel-` prefix:** On Vercel, the SDK now defaults `environment` to the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) instead of `vercel-production` / `vercel-preview`. Update alert rules, dashboards and saved searches that reference the old names, or keep them by setting `environment` explicitly. **Vercel AI no longer supported on Edge runtime:** We now rely on diagnostics channels for our Vercel AI instrumentation, which does not work on the Edge runtime. Because of this, monitoring of the `ai` package is no longer supported on Edge. Note that Edge is deprecated. @@ -1700,7 +1693,7 @@ The deprecated `sourceMapsUploadOptions` and other deprecated Vite/build plugin ### Bundler plugins: Vercel deploys use the plain Vercel environment name -Deploys that the bundler plugins create automatically on Vercel now use the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) as their environment instead of `vercel-production` / `vercel-preview`. This matches the new default runtime `environment` of `@sentry/nextjs` and `@sentry/vercel-edge`, and the `production` default of all other SDKs. If your events use a different environment, set `release.deploy.env` to the same value, or set `release.deploy` to `false` to opt out. +Deploys that the bundler plugins create automatically on Vercel now use the value of `VERCEL_TARGET_ENV` (`production`, `preview`, or a custom environment name) as their environment instead of `vercel-production` / `vercel-preview`. This matches the new default runtime `environment` of `@sentry/nextjs`, and the `production` default of all other SDKs. If your events use a different environment, set `release.deploy.env` to the same value, or set `release.deploy` to `false` to opt out. ### Removed `unstable_` bundler plugin options From b84498fe6d88fb85e6a931dd8df99f2d90cd5ede Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Tue, 8 Sep 2026 11:54:44 +0200 Subject: [PATCH 4/4] ref(nextjs): Keep NEXT_PUBLIC_ Vercel env lookup out of core The NEXT_PUBLIC_ variables are Next.js specific and must be referenced statically for Next.js to inline them, so the client lookup lives in @sentry/nextjs. The core helper only reads the server-side variables. --- .../src/core/options-mapping.ts | 2 +- packages/core/src/utils/vercel.ts | 9 ++---- packages/core/test/lib/utils/vercel.test.ts | 20 ++----------- packages/nextjs/src/client/index.ts | 12 ++------ packages/nextjs/src/common/getVercelEnv.ts | 7 +++++ packages/nextjs/src/server/index.ts | 2 +- .../nextjs/test/common/getVercelEnv.test.ts | 30 +++++++++++++++++++ packages/vercel-edge/src/sdk.ts | 3 +- 8 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 packages/nextjs/src/common/getVercelEnv.ts create mode 100644 packages/nextjs/test/common/getVercelEnv.test.ts diff --git a/packages/bundler-plugins/src/core/options-mapping.ts b/packages/bundler-plugins/src/core/options-mapping.ts index 5b342117ad6b..90de13daa8c9 100644 --- a/packages/bundler-plugins/src/core/options-mapping.ts +++ b/packages/bundler-plugins/src/core/options-mapping.ts @@ -163,7 +163,7 @@ export function normalizeUserOptions(userOptions: UserOptions): NormalizedOption } } - const vercelEnv = getVercelEnv(false); + const vercelEnv = getVercelEnv(); if (options.release.deploy === undefined && process.env['VERCEL'] && vercelEnv) { options.release.deploy = { env: vercelEnv, diff --git a/packages/core/src/utils/vercel.ts b/packages/core/src/utils/vercel.ts index 5795ef3ab78d..ea4c64829fbb 100644 --- a/packages/core/src/utils/vercel.ts +++ b/packages/core/src/utils/vercel.ts @@ -5,16 +5,11 @@ * `VERCEL_TARGET_ENV` also carries the name of custom Vercel environments, whereas `VERCEL_ENV` only ever holds * `production`, `preview` or `development`. SDKs use this as the default `environment` and the bundler plugins use it * for the deploys they create, so the two stay in sync and deploys show up next to events. - * - * @param isClient Whether to read the `NEXT_PUBLIC_` prefixed variants that Next.js exposes to the browser. */ -export function getVercelEnv(isClient: boolean): string | undefined { +export function getVercelEnv(): string | undefined { if (typeof process === 'undefined') { return undefined; } - const vercelEnv = isClient - ? process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV - : process.env.VERCEL_TARGET_ENV || process.env.VERCEL_ENV; - return vercelEnv || undefined; + return process.env.VERCEL_TARGET_ENV || process.env.VERCEL_ENV || undefined; } diff --git a/packages/core/test/lib/utils/vercel.test.ts b/packages/core/test/lib/utils/vercel.test.ts index 989618d3745d..0505e60dc7e6 100644 --- a/packages/core/test/lib/utils/vercel.test.ts +++ b/packages/core/test/lib/utils/vercel.test.ts @@ -8,8 +8,6 @@ describe('getVercelEnv', () => { process.env = { ...originalEnv }; delete process.env.VERCEL_ENV; delete process.env.VERCEL_TARGET_ENV; - delete process.env.NEXT_PUBLIC_VERCEL_ENV; - delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; }); afterEach(() => { @@ -17,31 +15,19 @@ describe('getVercelEnv', () => { }); it('returns undefined when no Vercel env vars are set', () => { - expect(getVercelEnv(false)).toBeUndefined(); - expect(getVercelEnv(true)).toBeUndefined(); + expect(getVercelEnv()).toBeUndefined(); }); it('returns VERCEL_TARGET_ENV without a prefix', () => { process.env.VERCEL_ENV = 'preview'; process.env.VERCEL_TARGET_ENV = 'staging'; - expect(getVercelEnv(false)).toBe('staging'); + expect(getVercelEnv()).toBe('staging'); }); it('falls back to VERCEL_ENV when VERCEL_TARGET_ENV is not set', () => { process.env.VERCEL_ENV = 'production'; - expect(getVercelEnv(false)).toBe('production'); - }); - - it('uses the NEXT_PUBLIC_ variants on the client', () => { - process.env.VERCEL_TARGET_ENV = 'server-only'; - process.env.NEXT_PUBLIC_VERCEL_ENV = 'preview'; - process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV = 'staging'; - - expect(getVercelEnv(true)).toBe('staging'); - - delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; - expect(getVercelEnv(true)).toBe('preview'); + expect(getVercelEnv()).toBe('production'); }); }); diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index 76d613413c67..abdba2d67d7d 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -2,18 +2,12 @@ // can be removed once following issue is fixed: https://github.com/import-js/eslint-plugin-import/issues/703 /* eslint-disable import/export */ import type { Client, EventProcessor, Integration } from '@sentry/core'; -import { - addEventProcessor, - applySdkMetadata, - consoleSandbox, - getGlobalScope, - getVercelEnv, - GLOBAL_OBJ, -} from '@sentry/core'; +import { addEventProcessor, applySdkMetadata, consoleSandbox, getGlobalScope, GLOBAL_OBJ } from '@sentry/core'; import type { BrowserOptions } from '@sentry/react'; import { getDefaultIntegrations as getReactDefaultIntegrations, init as reactInit } from '@sentry/react'; import { DEBUG_BUILD } from '../common/debug-build'; import { devErrorSymbolicationEventProcessor } from '../common/devErrorSymbolicationEventProcessor'; +import { getClientVercelEnv } from '../common/getVercelEnv'; import { isRedirectNavigationError } from '../common/nextNavigationErrorUtils'; import { browserTracingIntegration } from './browserTracingIntegration'; import { nextjsClientStackFrameNormalizationIntegration } from './clientNormalizationIntegration'; @@ -69,7 +63,7 @@ export function init(options: BrowserOptions): Client | undefined { } const opts = { - environment: options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv(true) || process.env.NODE_ENV, + environment: options.environment || process.env.SENTRY_ENVIRONMENT || getClientVercelEnv() || process.env.NODE_ENV, defaultIntegrations: getDefaultIntegrations(options), release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease, ...options, diff --git a/packages/nextjs/src/common/getVercelEnv.ts b/packages/nextjs/src/common/getVercelEnv.ts new file mode 100644 index 000000000000..cf977f0ee3cb --- /dev/null +++ b/packages/nextjs/src/common/getVercelEnv.ts @@ -0,0 +1,7 @@ +/** + * Browser counterpart of `getVercelEnv` from `@sentry/core`, reading the `NEXT_PUBLIC_` variants that Next.js exposes + * to the client. The variables must be referenced statically so Next.js can inline them at build time. + */ +export function getClientVercelEnv(): string | undefined { + return process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV || process.env.NEXT_PUBLIC_VERCEL_ENV || undefined; +} diff --git a/packages/nextjs/src/server/index.ts b/packages/nextjs/src/server/index.ts index cd18785fa6e8..9765260239a4 100644 --- a/packages/nextjs/src/server/index.ts +++ b/packages/nextjs/src/server/index.ts @@ -145,7 +145,7 @@ export function init(options: NodeOptions): NodeClient | undefined { const cloudflareConfig = getCloudflareRuntimeConfig(); const opts: NodeOptions = { - environment: options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV, + environment: options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv() || process.env.NODE_ENV, release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease, defaultIntegrations: customDefaultIntegrations, // Next.js emits its own OpenTelemetry spans, so it defaults to registering the Sentry tracer diff --git a/packages/nextjs/test/common/getVercelEnv.test.ts b/packages/nextjs/test/common/getVercelEnv.test.ts new file mode 100644 index 000000000000..7bc79c7d71d6 --- /dev/null +++ b/packages/nextjs/test/common/getVercelEnv.test.ts @@ -0,0 +1,30 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { getClientVercelEnv } from '../../src/common/getVercelEnv'; + +describe('getClientVercelEnv', () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + delete process.env.NEXT_PUBLIC_VERCEL_ENV; + delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('returns undefined when no Vercel env vars are set', () => { + expect(getClientVercelEnv()).toBeUndefined(); + }); + + it('prefers NEXT_PUBLIC_VERCEL_TARGET_ENV over NEXT_PUBLIC_VERCEL_ENV', () => { + process.env.NEXT_PUBLIC_VERCEL_ENV = 'preview'; + process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV = 'staging'; + + expect(getClientVercelEnv()).toBe('staging'); + + delete process.env.NEXT_PUBLIC_VERCEL_TARGET_ENV; + expect(getClientVercelEnv()).toBe('preview'); + }); +}); diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index 469fa56445fe..70503ad97164 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -76,8 +76,7 @@ export function init(options: VercelEdgeOptions = {}): Client { } } - options.environment = - options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV; + options.environment = options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv() || process.env.NODE_ENV; options.traceLifecycle = options.traceLifecycle ?? getTraceLifecycleFromEnv(process.env.SENTRY_TRACE_LIFECYCLE);