diff --git a/packages/bundler-plugin-core/src/options-mapping.ts b/packages/bundler-plugin-core/src/options-mapping.ts index 3057c8af..0c79a5ae 100644 --- a/packages/bundler-plugin-core/src/options-mapping.ts +++ b/packages/bundler-plugin-core/src/options-mapping.ts @@ -44,14 +44,16 @@ export type NormalizedOptions = { | false | undefined; dist?: string; - deploy?: { - env: string; - started?: number | string; - finished?: number | string; - time?: number; - name?: string; - url?: string; - }; + deploy?: + | { + env: string; + started?: number | string; + finished?: number | string; + time?: number; + name?: string; + url?: string; + } + | false; uploadLegacySourcemaps?: string | IncludeEntry | Array; }; bundleSizeOptimizations: @@ -195,7 +197,11 @@ export function validateOptions(options: NormalizedOptions, logger: Logger): boo } } - if (options.release?.deploy && !options.release?.deploy.env) { + if ( + options.release?.deploy && + typeof options.release.deploy === "object" && + !options.release.deploy.env + ) { logger.error( "The `deploy` option was specified but is missing the required `env` property.", "Please set the `env` property." diff --git a/packages/bundler-plugin-core/src/types.ts b/packages/bundler-plugin-core/src/types.ts index 81862a52..accceee5 100644 --- a/packages/bundler-plugin-core/src/types.ts +++ b/packages/bundler-plugin-core/src/types.ts @@ -239,8 +239,10 @@ export interface Options { /** * Configuration for adding deployment information to the release in Sentry. + * + * Set to `false` to disable automatic deployment detection and creation. */ - deploy?: DeployOptions; + deploy?: DeployOptions | false; /** * Legacy method of uploading source maps. (not recommended unless necessary) diff --git a/packages/bundler-plugin-core/test/option-mappings.test.ts b/packages/bundler-plugin-core/test/option-mappings.test.ts index f233e1e7..76bceb3d 100644 --- a/packages/bundler-plugin-core/test/option-mappings.test.ts +++ b/packages/bundler-plugin-core/test/option-mappings.test.ts @@ -110,6 +110,86 @@ describe("normalizeUserOptions()", () => { expect(normalizeUserOptions(options).telemetry).toBe(true); } ); + + describe("Vercel deploy detection", () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + test("should automatically create deploy config when Vercel env vars are present", () => { + process.env["VERCEL"] = "1"; + process.env["VERCEL_TARGET_ENV"] = "production"; + process.env["VERCEL_URL"] = "my-app.vercel.app"; + + const userOptions: Options = { + org: "my-org", + project: "my-project", + authToken: "my-auth-token", + release: { name: "my-release" }, + }; + + const normalizedOptions = normalizeUserOptions(userOptions); + + expect(normalizedOptions.release.deploy).toEqual({ + env: "vercel-production", + url: "https://my-app.vercel.app", + }); + }); + + test("should not create deploy config when deploy is explicitly set to false", () => { + process.env["VERCEL"] = "1"; + process.env["VERCEL_TARGET_ENV"] = "production"; + process.env["VERCEL_URL"] = "my-app.vercel.app"; + + const userOptions: Options = { + org: "my-org", + project: "my-project", + authToken: "my-auth-token", + release: { name: "my-release", deploy: false }, + }; + + const normalizedOptions = normalizeUserOptions(userOptions); + + expect(normalizedOptions.release.deploy).toBe(false); + }); + + test("should not override manually provided deploy config", () => { + process.env["VERCEL"] = "1"; + process.env["VERCEL_TARGET_ENV"] = "production"; + process.env["VERCEL_URL"] = "my-app.vercel.app"; + + const manualDeployConfig = { env: "custom-env", name: "custom-deploy" }; + const userOptions: Options = { + org: "my-org", + project: "my-project", + authToken: "my-auth-token", + release: { name: "my-release", deploy: manualDeployConfig }, + }; + + const normalizedOptions = normalizeUserOptions(userOptions); + + expect(normalizedOptions.release.deploy).toEqual(manualDeployConfig); + }); + + test("should not create deploy config when Vercel env vars are missing", () => { + const userOptions: Options = { + org: "my-org", + project: "my-project", + authToken: "my-auth-token", + release: { name: "my-release" }, + }; + + const normalizedOptions = normalizeUserOptions(userOptions); + + expect(normalizedOptions.release.deploy).toBeUndefined(); + }); + }); }); describe("validateOptions", () => { @@ -164,7 +244,14 @@ describe("validateOptions", () => { }); it("should return `true` if `deploy`is set and `env` is provided", () => { - const options = { deploy: { env: "my-env" } } as Partial; + const options = { release: { deploy: { env: "my-env" } } } as Partial; + + expect(validateOptions(options as unknown as NormalizedOptions, mockedLogger)).toBe(true); + expect(mockedLogger.error).not.toHaveBeenCalled(); + }); + + it("should return `true` if `deploy` is set to `false`", () => { + const options = { release: { deploy: false } } as Partial; expect(validateOptions(options as unknown as NormalizedOptions, mockedLogger)).toBe(true); expect(mockedLogger.error).not.toHaveBeenCalled(); diff --git a/packages/dev-utils/src/generate-documentation-table.ts b/packages/dev-utils/src/generate-documentation-table.ts index 3a4529c4..1757ffe9 100644 --- a/packages/dev-utils/src/generate-documentation-table.ts +++ b/packages/dev-utils/src/generate-documentation-table.ts @@ -221,8 +221,9 @@ Use the \`debug\` option to print information about source map resolution. }, { name: "deploy", + type: "DeployOptions | false", fullDescription: - "Configuration for adding deployment information to the release in Sentry.", + "Configuration for adding deployment information to the release in Sentry.\n\nSet to `false` to disable automatic deployment detection and creation (e.g., when deploying on Vercel).", children: [ { name: "env",