Skip to content

Commit ebc1e26

Browse files
authored
fix(nextjs): Stop injecting release value when create release options is set to false (#16695)
closes #16593
1 parent c37d4e5 commit ebc1e26

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

packages/nextjs/src/config/webpack.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,10 @@ function addValueInjectionLoader(
689689
): void {
690690
const assetPrefix = userNextConfig.assetPrefix || userNextConfig.basePath || '';
691691

692+
// Check if release creation is disabled to prevent injection that breaks build determinism
693+
const shouldCreateRelease = userSentryOptions.release?.create !== false;
694+
const releaseToInject = releaseName && shouldCreateRelease ? releaseName : undefined;
695+
692696
const isomorphicValues = {
693697
// `rewritesTunnel` set by the user in Next.js config
694698
_sentryRewritesTunnelPath:
@@ -700,7 +704,8 @@ function addValueInjectionLoader(
700704

701705
// The webpack plugin's release injection breaks the `app` directory so we inject the release manually here instead.
702706
// Having a release defined in dev-mode spams releases in Sentry so we only set one in non-dev mode
703-
SENTRY_RELEASE: releaseName && !buildContext.dev ? { id: releaseName } : undefined,
707+
// Only inject if release creation is not explicitly disabled (to maintain build determinism)
708+
SENTRY_RELEASE: releaseToInject && !buildContext.dev ? { id: releaseToInject } : undefined,
704709
_sentryBasePath: buildContext.dev ? userNextConfig.basePath : undefined,
705710
};
706711

packages/nextjs/src/config/withSentryConfig.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,12 @@ function getFinalConfigObject(
9090
incomingUserNextConfigObject: NextConfigObject,
9191
userSentryOptions: SentryBuildOptions,
9292
): NextConfigObject {
93-
const releaseName = userSentryOptions.release?.name ?? getSentryRelease() ?? getGitRevision();
93+
// Only determine a release name if release creation is not explicitly disabled
94+
// This prevents injection of Git commit hashes that break build determinism
95+
const shouldCreateRelease = userSentryOptions.release?.create !== false;
96+
const releaseName = shouldCreateRelease
97+
? userSentryOptions.release?.name ?? getSentryRelease() ?? getGitRevision()
98+
: userSentryOptions.release?.name;
9499

95100
if (userSentryOptions?.tunnelRoute) {
96101
if (incomingUserNextConfigObject.output === 'export') {
@@ -126,8 +131,8 @@ function getFinalConfigObject(
126131
// 1. compile: Code compilation
127132
// 2. generate: Environment variable inlining and prerendering (We don't instrument this phase, we inline in the compile phase)
128133
//
129-
// We assume a single full build and reruns Webpack instrumentation in both phases.
130-
// During the generate step it collides with Next.jss inliner
134+
// We assume a single "full" build and reruns Webpack instrumentation in both phases.
135+
// During the generate step it collides with Next.js's inliner
131136
// producing malformed JS and build failures.
132137
// We skip Sentry processing during generate to avoid this issue.
133138
return incomingUserNextConfigObject;

packages/nextjs/test/config/withSentryConfig.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,71 @@ describe('withSentryConfig', () => {
144144
);
145145
});
146146
});
147+
148+
describe('release injection behavior', () => {
149+
afterEach(() => {
150+
vi.restoreAllMocks();
151+
152+
// clear env to avoid leaking env vars from fixtures
153+
delete exportedNextConfig.env;
154+
delete process.env.SENTRY_RELEASE;
155+
});
156+
157+
it('does not inject release when create is false', () => {
158+
const sentryOptions = {
159+
release: {
160+
create: false,
161+
},
162+
};
163+
164+
// clear env to avoid leaking env vars from fixtures
165+
delete exportedNextConfig.env;
166+
167+
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
168+
169+
// Should not inject release into environment when create is false
170+
expect(finalConfig.env).not.toHaveProperty('_sentryRelease');
171+
});
172+
173+
it('injects release when create is true (default)', () => {
174+
const sentryOptions = {
175+
release: {
176+
create: true,
177+
name: 'test-release@1.0.0',
178+
},
179+
};
180+
181+
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
182+
183+
// Should inject release into environment when create is true
184+
expect(finalConfig.env).toHaveProperty('_sentryRelease', 'test-release@1.0.0');
185+
});
186+
187+
it('injects release with explicit name', () => {
188+
const sentryOptions = {
189+
release: {
190+
name: 'custom-release-v2.1.0',
191+
},
192+
};
193+
194+
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
195+
196+
// Should inject the explicit release name
197+
expect(finalConfig.env).toHaveProperty('_sentryRelease', 'custom-release-v2.1.0');
198+
});
199+
200+
it('falls back to SENTRY_RELEASE environment variable when no explicit name provided', () => {
201+
process.env.SENTRY_RELEASE = 'env-release-1.5.0';
202+
203+
const sentryOptions = {
204+
release: {
205+
create: true,
206+
},
207+
};
208+
209+
const finalConfig = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
210+
211+
expect(finalConfig.env).toHaveProperty('_sentryRelease', 'env-release-1.5.0');
212+
});
213+
});
147214
});

0 commit comments

Comments
 (0)