From df934cd47d1d37c2836200bea35b37f08404ee1c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 16 Oct 2026 20:11:21 +0000 Subject: [PATCH 1/2] ref(core): Share the commit env var list used for release detection The Node SDK, the Vercel Edge SDK and the bundler plugins each kept their own copy of the commit environment variables they read to detect a release, along with their own placeholder check. The copies had already drifted: the bundler plugins did not read HEROKU_BUILD_COMMIT. The list and the placeholder check now live in @sentry/core/server as getReleaseFromCommitEnv() and readCommitEnvVar(). Node and Vercel Edge keep their SENTRY_RELEASE and injected-release checks, and the bundler plugins keep their git fallback and their own once-per-build warning. The bundler plugins now also read HEROKU_BUILD_COMMIT. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WgDm3ijhQZQUHGAFFqQzKQ --- packages/bundler-plugins/src/core/utils.ts | 127 +++------------- .../bundler-plugins/test/core/utils.test.ts | 10 ++ packages/core/src/server.ts | 2 + packages/core/src/utils/commitEnv.ts | 139 ++++++++++++++++++ .../core/test/lib/utils/commitEnv.test.ts | 95 ++++++++++++ packages/node/src/sdk/api.ts | 118 +-------------- packages/vercel-edge/src/sdk.ts | 117 +-------------- 7 files changed, 269 insertions(+), 339 deletions(-) create mode 100644 packages/core/src/utils/commitEnv.ts create mode 100644 packages/core/test/lib/utils/commitEnv.test.ts diff --git a/packages/bundler-plugins/src/core/utils.ts b/packages/bundler-plugins/src/core/utils.ts index 6cf8c6af8e81..8dfccd8f5b74 100644 --- a/packages/bundler-plugins/src/core/utils.ts +++ b/packages/bundler-plugins/src/core/utils.ts @@ -6,6 +6,7 @@ import crypto from 'crypto'; import childProcess from 'child_process'; import type { SourceMap } from 'magic-string'; import MagicString from 'magic-string'; +import { getReleaseFromCommitEnv, readCommitEnvVar as readCoreCommitEnvVar } from '@sentry/core/server'; /** * Checks whether the given input is already an array, and if it isn't, wraps it in one. @@ -215,124 +216,32 @@ function gitRevision(): string | undefined { /** * Tries to guess a release name based on environmental data. */ -// oxlint-disable-next-line complexity export function determineReleaseName(): string | undefined { - // This list is in approximate alpha order, separated into 3 categories: - // 1. Git providers - // 2. CI providers with specific environment variables (has the provider name in the variable name) - // 3. CI providers with generic environment variables (checked for last to prevent possible false positives) - // Each group is only read if the earlier ones found nothing, so a placeholder value is only - // warned about when it would otherwise have become the release. - - const possibleReleaseNameOfGitProvider = (): string | undefined => - // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables - readCommitEnvVar('GITHUB_SHA') || - // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html - readCommitEnvVar('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || - readCommitEnvVar('CI_BUILD_REF') || - readCommitEnvVar('CI_COMMIT_SHA') || - // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ - readCommitEnvVar('BITBUCKET_COMMIT'); - - const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = (): string | undefined => - // AppVeyor - https://www.appveyor.com/docs/environment-variables/ - readCommitEnvVar('APPVEYOR_PULL_REQUEST_HEAD_COMMIT') || - readCommitEnvVar('APPVEYOR_REPO_COMMIT') || - // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html - readCommitEnvVar('CODEBUILD_RESOLVED_SOURCE_VERSION') || - // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html - readCommitEnvVar('AWS_COMMIT_ID') || - // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml - readCommitEnvVar('BUILD_SOURCEVERSION') || - // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ - readCommitEnvVar('GIT_CLONE_COMMIT_HASH') || - // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables - readCommitEnvVar('BUDDY_EXECUTION_REVISION') || - // Builtkite - https://buildkite.com/docs/pipelines/environment-variables - readCommitEnvVar('BUILDKITE_COMMIT') || - // CircleCI - https://circleci.com/docs/variables/ - readCommitEnvVar('CIRCLE_SHA1') || - // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables - readCommitEnvVar('CIRRUS_CHANGE_IN_REPO') || - // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ - readCommitEnvVar('CF_REVISION') || - // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ - readCommitEnvVar('CM_COMMIT') || - // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables - readCommitEnvVar('CF_PAGES_COMMIT_SHA') || - // Drone - https://docs.drone.io/pipeline/environment/reference/ - readCommitEnvVar('DRONE_COMMIT_SHA') || - // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables - readCommitEnvVar('FC_GIT_COMMIT_SHA') || - // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci - readCommitEnvVar('HEROKU_TEST_RUN_COMMIT_VERSION') || - // Heroku #2 https://docs.sentry.io/product/integrations/deployment/heroku/#configure-releases - readCommitEnvVar('HEROKU_SLUG_COMMIT') || - // Railway - https://docs.railway.app/reference/variables#git-variables - readCommitEnvVar('RAILWAY_GIT_COMMIT_SHA') || - // Render - https://render.com/docs/environment-variables - readCommitEnvVar('RENDER_GIT_COMMIT') || - // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables - readCommitEnvVar('SEMAPHORE_GIT_SHA') || - // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables - readCommitEnvVar('TRAVIS_PULL_REQUEST_SHA') || - // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables - readCommitEnvVar('VERCEL_GIT_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITHUB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITLAB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_BITBUCKET_COMMIT_SHA') || - // Zeit (now known as Vercel) - readCommitEnvVar('ZEIT_GITHUB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_GITLAB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_BITBUCKET_COMMIT_SHA'); - - const possibleReleaseNameOfCiProvidersWithGenericEnvVar = (): string | undefined => - // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables - readCommitEnvVar('CI_COMMIT_ID') || - // Coolify - https://coolify.io/docs/knowledge-base/environment-variables - readCommitEnvVar('SOURCE_COMMIT') || - // Heroku #3 https://devcenter.heroku.com/changelog-items/630 - readCommitEnvVar('SOURCE_VERSION') || - // Jenkins - https://plugins.jenkins.io/git/#environment-variables - readCommitEnvVar('GIT_COMMIT') || - // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata - readCommitEnvVar('COMMIT_REF') || - // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html - readCommitEnvVar('BUILD_VCS_NUMBER'); - // Woodpecker CI (https://woodpecker-ci.org/docs/usage/environment) sets CI_COMMIT_SHA, which the - // GitLab check above already reads. - - return ( - possibleReleaseNameOfGitProvider() || - possibleReleaseNameOfCiProvidersWithSpecificEnvVar() || - possibleReleaseNameOfCiProvidersWithGenericEnvVar() || - gitRevision() - ); + return getReleaseFromCommitEnv(warnAboutPlaceholderCommitEnvVar) || gitRevision(); } // Release detection and commit association can read the same variable in one build. const warnedPlaceholderEnvVars = new Set(); +function warnAboutPlaceholderCommitEnvVar(name: string): void { + if (warnedPlaceholderEnvVars.has(name)) { + return; + } + warnedPlaceholderEnvVars.add(name); + // This runs before the plugin logger exists, so `silent: true` does not suppress it. That is + // deliberate: a warning that is easy to miss is how this misconfiguration went unnoticed. + // eslint-disable-next-line no-console + console.warn( + `[@sentry/bundler-plugins] Ignoring the ${name} environment variable as a release name, because its value is its own name. Set the release explicitly, or fix the variable in your build environment.`, + ); +} + /** - * Reads an environment variable that should hold a commit SHA. A value that is the variable's own - * name, such as `VERCEL_GIT_COMMIT_SHA=VERCEL_GIT_COMMIT_SHA`, is ignored: it never changes, so as - * a release name it would put every build into one release. + * Reads an environment variable that should hold a commit SHA, ignoring a value that is the + * variable's own name. See `readCommitEnvVar` in `@sentry/core/server`. */ export function readCommitEnvVar(name: string): string | undefined { - const value = process.env[name]; - if (value?.trim() === name) { - if (!warnedPlaceholderEnvVars.has(name)) { - warnedPlaceholderEnvVars.add(name); - // This runs before the plugin logger exists, so `silent: true` does not suppress it. That is - // deliberate: a warning that is easy to miss is how this misconfiguration went unnoticed. - // eslint-disable-next-line no-console - console.warn( - `[@sentry/bundler-plugins] Ignoring the ${name} environment variable as a release name, because its value is its own name. Set the release explicitly, or fix the variable in your build environment.`, - ); - } - return undefined; - } - return value; + return readCoreCommitEnvVar(name, warnAboutPlaceholderCommitEnvVar); } function stringifyForCodeInjection(value: unknown): string | undefined { diff --git a/packages/bundler-plugins/test/core/utils.test.ts b/packages/bundler-plugins/test/core/utils.test.ts index e387230a4ada..68e62701f0fa 100644 --- a/packages/bundler-plugins/test/core/utils.test.ts +++ b/packages/bundler-plugins/test/core/utils.test.ts @@ -345,6 +345,16 @@ describe('determineReleaseName', () => { execSyncSpy.mockRestore(); } }); + it('detects HEROKU_BUILD_COMMIT', () => { + const originalEnv = process.env; + process.env = { HEROKU_BUILD_COMMIT: 'heroku-build-commit' }; + + try { + expect(determineReleaseName()).toBe('heroku-build-commit'); + } finally { + process.env = originalEnv; + } + }); it('ignores a commit env var whose value is its own name', () => { const originalEnv = process.env; process.env = { VERCEL_GIT_COMMIT_SHA: 'VERCEL_GIT_COMMIT_SHA', COMMIT_REF: 'abc123' }; diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index e44bb9c2df8b..f981065c14c8 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -12,6 +12,8 @@ export { wrapMcpServerWithSentry } from './integrations/mcp-server'; export { isNodeEnv, loadModule } from './utils/node'; export { filenameIsInApp, node, nodeStackLineParser } from './utils/node-stack-trace'; export { vercelWaitUntil } from './utils/vercelWaitUntil'; +export { getReleaseFromCommitEnv, readCommitEnvVar } from './utils/commitEnv'; +export type { PlaceholderCommitEnvVarHandler } from './utils/commitEnv'; export { flushIfServerless } from './utils/flushIfServerless'; export { callFrameToStackFrame, watchdogTimer } from './utils/anr'; export { safeUnref as _INTERNAL_safeUnref } from './utils/timer'; diff --git a/packages/core/src/utils/commitEnv.ts b/packages/core/src/utils/commitEnv.ts new file mode 100644 index 000000000000..ca5d6fe75de1 --- /dev/null +++ b/packages/core/src/utils/commitEnv.ts @@ -0,0 +1,139 @@ +import { consoleSandbox } from './debug-logger'; + +/** + * Environment variables that CI and hosting providers set to the commit being built, in the order + * release detection checks them. The list is in approximate alpha order, separated into 3 categories: + * 1. Git providers + * 2. CI providers with specific environment variables (has the provider name in the variable name) + * 3. CI providers with generic environment variables (checked for last to prevent possible false positives) + */ +const COMMIT_ENV_VARS = [ + // 1. Git providers + // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables + 'GITHUB_SHA', + // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html + // Woodpecker CI also sets CI_COMMIT_SHA - https://woodpecker-ci.org/docs/usage/environment + 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', + 'CI_BUILD_REF', + 'CI_COMMIT_SHA', + // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ + 'BITBUCKET_COMMIT', + + // 2. CI providers with specific environment variables + // AppVeyor - https://www.appveyor.com/docs/environment-variables/ + 'APPVEYOR_PULL_REQUEST_HEAD_COMMIT', + 'APPVEYOR_REPO_COMMIT', + // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html + 'CODEBUILD_RESOLVED_SOURCE_VERSION', + // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html + 'AWS_COMMIT_ID', + // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml + 'BUILD_SOURCEVERSION', + // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ + 'GIT_CLONE_COMMIT_HASH', + // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables + 'BUDDY_EXECUTION_REVISION', + // Builtkite - https://buildkite.com/docs/pipelines/environment-variables + 'BUILDKITE_COMMIT', + // CircleCI - https://circleci.com/docs/variables/ + 'CIRCLE_SHA1', + // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables + 'CIRRUS_CHANGE_IN_REPO', + // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ + 'CF_REVISION', + // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ + 'CM_COMMIT', + // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables + 'CF_PAGES_COMMIT_SHA', + // Drone - https://docs.drone.io/pipeline/environment/reference/ + 'DRONE_COMMIT_SHA', + // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables + 'FC_GIT_COMMIT_SHA', + // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci + 'HEROKU_TEST_RUN_COMMIT_VERSION', + // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata + 'HEROKU_BUILD_COMMIT', + // Heroku #3 (deprecated by Heroku, kept for backward compatibility) + 'HEROKU_SLUG_COMMIT', + // Railway - https://docs.railway.app/reference/variables#git-variables + 'RAILWAY_GIT_COMMIT_SHA', + // Render - https://render.com/docs/environment-variables + 'RENDER_GIT_COMMIT', + // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables + 'SEMAPHORE_GIT_SHA', + // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables + 'TRAVIS_PULL_REQUEST_SHA', + // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables + 'VERCEL_GIT_COMMIT_SHA', + 'VERCEL_GITHUB_COMMIT_SHA', + 'VERCEL_GITLAB_COMMIT_SHA', + 'VERCEL_BITBUCKET_COMMIT_SHA', + // Zeit (now known as Vercel) + 'ZEIT_GITHUB_COMMIT_SHA', + 'ZEIT_GITLAB_COMMIT_SHA', + 'ZEIT_BITBUCKET_COMMIT_SHA', + + // 3. CI providers with generic environment variables + // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables + 'CI_COMMIT_ID', + // Coolify - https://coolify.io/docs/knowledge-base/environment-variables + 'SOURCE_COMMIT', + // Heroku #4 https://devcenter.heroku.com/changelog-items/630 + 'SOURCE_VERSION', + // Jenkins - https://plugins.jenkins.io/git/#environment-variables + 'GIT_COMMIT', + // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata + 'COMMIT_REF', + // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html + 'BUILD_VCS_NUMBER', +] as const; + +/** Called with the name of a commit environment variable whose value is its own name. */ +export type PlaceholderCommitEnvVarHandler = (name: string) => void; + +function warnAboutPlaceholderCommitEnvVar(name: string): void { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] Ignoring the ${name} environment variable as a release name, because its value is its own name. Set the release explicitly, or fix the variable in your build environment.`, + ); + }); +} + +/** + * Reads an environment variable that should hold a commit SHA. A value that is the variable's own + * name, such as `VERCEL_GIT_COMMIT_SHA=VERCEL_GIT_COMMIT_SHA`, is ignored: it never changes, so as + * a release name it would put every build into one release. `onPlaceholder` is called for such a + * value and warns on the console by default. + */ +export function readCommitEnvVar( + name: string, + onPlaceholder: PlaceholderCommitEnvVarHandler = warnAboutPlaceholderCommitEnvVar, +): string | undefined { + if (typeof process === 'undefined') { + return undefined; + } + const value = process.env[name]; + if (value?.trim() === name) { + onPlaceholder(name); + return undefined; + } + return value || undefined; +} + +/** + * Returns the commit that CI or the hosting provider is building, read from the first of the known + * commit environment variables that is set. Variables are read in order and only until one is + * found, so a placeholder value is only reported when it would otherwise have become the release. + */ +export function getReleaseFromCommitEnv( + onPlaceholder: PlaceholderCommitEnvVarHandler = warnAboutPlaceholderCommitEnvVar, +): string | undefined { + for (const name of COMMIT_ENV_VARS) { + const value = readCommitEnvVar(name, onPlaceholder); + if (value) { + return value; + } + } + return undefined; +} diff --git a/packages/core/test/lib/utils/commitEnv.test.ts b/packages/core/test/lib/utils/commitEnv.test.ts new file mode 100644 index 000000000000..5954785256fb --- /dev/null +++ b/packages/core/test/lib/utils/commitEnv.test.ts @@ -0,0 +1,95 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { getReleaseFromCommitEnv, readCommitEnvVar } from '../../../src/utils/commitEnv'; + +describe('commitEnv', () => { + const originalEnv = process.env; + + beforeEach(() => { + // CI sets some of these variables (e.g. GITHUB_SHA on GitHub Actions), so start from an empty env. + process.env = {}; + }); + + afterEach(() => { + process.env = originalEnv; + vi.restoreAllMocks(); + }); + + describe('readCommitEnvVar', () => { + it('returns the value of the variable', () => { + process.env.VERCEL_GIT_COMMIT_SHA = 'abc123'; + + expect(readCommitEnvVar('VERCEL_GIT_COMMIT_SHA')).toBe('abc123'); + }); + + it('returns undefined for an unset or empty variable', () => { + process.env.VERCEL_GIT_COMMIT_SHA = ''; + + expect(readCommitEnvVar('VERCEL_GIT_COMMIT_SHA')).toBeUndefined(); + expect(readCommitEnvVar('GITHUB_SHA')).toBeUndefined(); + }); + + it('ignores a value that is its own name and warns by default', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + process.env.VERCEL_GIT_COMMIT_SHA = ' VERCEL_GIT_COMMIT_SHA '; + + expect(readCommitEnvVar('VERCEL_GIT_COMMIT_SHA')).toBeUndefined(); + expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('VERCEL_GIT_COMMIT_SHA')); + }); + + it('passes a placeholder to a custom handler instead of warning', () => { + const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + const onPlaceholder = vi.fn(); + process.env.VERCEL_GIT_COMMIT_SHA = 'VERCEL_GIT_COMMIT_SHA'; + + expect(readCommitEnvVar('VERCEL_GIT_COMMIT_SHA', onPlaceholder)).toBeUndefined(); + expect(onPlaceholder).toHaveBeenCalledWith('VERCEL_GIT_COMMIT_SHA'); + expect(warnSpy).not.toHaveBeenCalled(); + }); + }); + + describe('getReleaseFromCommitEnv', () => { + it('returns undefined when no commit variable is set', () => { + expect(getReleaseFromCommitEnv()).toBeUndefined(); + }); + + it('prefers git provider variables over generic CI variables', () => { + process.env.COMMIT_REF = 'netlify-commit'; + process.env.GITHUB_SHA = 'github-commit'; + + expect(getReleaseFromCommitEnv()).toBe('github-commit'); + }); + + it('reads HEROKU_BUILD_COMMIT before HEROKU_SLUG_COMMIT', () => { + process.env.HEROKU_BUILD_COMMIT = 'heroku-build-commit'; + process.env.HEROKU_SLUG_COMMIT = 'heroku-slug-commit'; + + expect(getReleaseFromCommitEnv()).toBe('heroku-build-commit'); + }); + + it('skips a placeholder and uses the next variable that is set', () => { + const onPlaceholder = vi.fn(); + process.env.VERCEL_GIT_COMMIT_SHA = 'VERCEL_GIT_COMMIT_SHA'; + process.env.COMMIT_REF = 'netlify-commit'; + + expect(getReleaseFromCommitEnv(onPlaceholder)).toBe('netlify-commit'); + expect(onPlaceholder).toHaveBeenCalledWith('VERCEL_GIT_COMMIT_SHA'); + }); + + it('does not report a placeholder checked after the variable that supplies the release', () => { + const onPlaceholder = vi.fn(); + process.env.VERCEL_GIT_COMMIT_SHA = 'vercel-commit'; + process.env.COMMIT_REF = 'COMMIT_REF'; + + expect(getReleaseFromCommitEnv(onPlaceholder)).toBe('vercel-commit'); + expect(onPlaceholder).not.toHaveBeenCalled(); + }); + + it('reports each placeholder once', () => { + const onPlaceholder = vi.fn(); + process.env.CI_COMMIT_SHA = 'CI_COMMIT_SHA'; + + expect(getReleaseFromCommitEnv(onPlaceholder)).toBeUndefined(); + expect(onPlaceholder).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/packages/node/src/sdk/api.ts b/packages/node/src/sdk/api.ts index 572b96fafd48..71fc95c3095e 100644 --- a/packages/node/src/sdk/api.ts +++ b/packages/node/src/sdk/api.ts @@ -1,14 +1,13 @@ // PUBLIC APIS import type { StackParser } from '@sentry/core'; -import { consoleSandbox, createStackParser, GLOBAL_OBJ } from '@sentry/core'; -import { nodeStackLineParser } from '@sentry/core/server'; +import { createStackParser, GLOBAL_OBJ } from '@sentry/core'; +import { getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; import { createGetModuleFromFilename } from '../utils/module'; /** * Returns a release dynamically from environment variables. */ -// eslint-disable-next-line complexity export function getSentryRelease(fallback?: string): string | undefined { // Always read first as Sentry takes this as precedence if (process.env.SENTRY_RELEASE) { @@ -20,118 +19,7 @@ export function getSentryRelease(fallback?: string): string | undefined { return GLOBAL_OBJ.SENTRY_RELEASE.id; } - // This list is in approximate alpha order, separated into 3 categories: - // 1. Git providers - // 2. CI providers with specific environment variables (has the provider name in the variable name) - // 3. CI providers with generic environment variables (checked for last to prevent possible false positives) - // Each group is only read if the earlier ones found nothing, so a placeholder value is only - // warned about when it would otherwise have become the release. - - const possibleReleaseNameOfGitProvider = (): string | undefined => - // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables - readCommitEnvVar('GITHUB_SHA') || - // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html - readCommitEnvVar('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || - readCommitEnvVar('CI_BUILD_REF') || - readCommitEnvVar('CI_COMMIT_SHA') || - // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ - readCommitEnvVar('BITBUCKET_COMMIT'); - - const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = (): string | undefined => - // AppVeyor - https://www.appveyor.com/docs/environment-variables/ - readCommitEnvVar('APPVEYOR_PULL_REQUEST_HEAD_COMMIT') || - readCommitEnvVar('APPVEYOR_REPO_COMMIT') || - // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html - readCommitEnvVar('CODEBUILD_RESOLVED_SOURCE_VERSION') || - // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html - readCommitEnvVar('AWS_COMMIT_ID') || - // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml - readCommitEnvVar('BUILD_SOURCEVERSION') || - // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ - readCommitEnvVar('GIT_CLONE_COMMIT_HASH') || - // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables - readCommitEnvVar('BUDDY_EXECUTION_REVISION') || - // Builtkite - https://buildkite.com/docs/pipelines/environment-variables - readCommitEnvVar('BUILDKITE_COMMIT') || - // CircleCI - https://circleci.com/docs/variables/ - readCommitEnvVar('CIRCLE_SHA1') || - // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables - readCommitEnvVar('CIRRUS_CHANGE_IN_REPO') || - // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ - readCommitEnvVar('CF_REVISION') || - // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ - readCommitEnvVar('CM_COMMIT') || - // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables - readCommitEnvVar('CF_PAGES_COMMIT_SHA') || - // Drone - https://docs.drone.io/pipeline/environment/reference/ - readCommitEnvVar('DRONE_COMMIT_SHA') || - // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables - readCommitEnvVar('FC_GIT_COMMIT_SHA') || - // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci - readCommitEnvVar('HEROKU_TEST_RUN_COMMIT_VERSION') || - // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata - readCommitEnvVar('HEROKU_BUILD_COMMIT') || - // Heroku #3 (deprecated by Heroku, kept for backward compatibility) - readCommitEnvVar('HEROKU_SLUG_COMMIT') || - // Railway - https://docs.railway.app/reference/variables#git-variables - readCommitEnvVar('RAILWAY_GIT_COMMIT_SHA') || - // Render - https://render.com/docs/environment-variables - readCommitEnvVar('RENDER_GIT_COMMIT') || - // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables - readCommitEnvVar('SEMAPHORE_GIT_SHA') || - // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables - readCommitEnvVar('TRAVIS_PULL_REQUEST_SHA') || - // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables - readCommitEnvVar('VERCEL_GIT_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITHUB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITLAB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_BITBUCKET_COMMIT_SHA') || - // Zeit (now known as Vercel) - readCommitEnvVar('ZEIT_GITHUB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_GITLAB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_BITBUCKET_COMMIT_SHA'); - - const possibleReleaseNameOfCiProvidersWithGenericEnvVar = (): string | undefined => - // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables - readCommitEnvVar('CI_COMMIT_ID') || - // Coolify - https://coolify.io/docs/knowledge-base/environment-variables - readCommitEnvVar('SOURCE_COMMIT') || - // Heroku #3 https://devcenter.heroku.com/changelog-items/630 - readCommitEnvVar('SOURCE_VERSION') || - // Jenkins - https://plugins.jenkins.io/git/#environment-variables - readCommitEnvVar('GIT_COMMIT') || - // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata - readCommitEnvVar('COMMIT_REF') || - // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html - readCommitEnvVar('BUILD_VCS_NUMBER'); - // Woodpecker CI (https://woodpecker-ci.org/docs/usage/environment) sets CI_COMMIT_SHA, which the - // GitLab check above already reads. - - return ( - possibleReleaseNameOfGitProvider() || - possibleReleaseNameOfCiProvidersWithSpecificEnvVar() || - possibleReleaseNameOfCiProvidersWithGenericEnvVar() || - fallback - ); -} - -/** - * Reads an environment variable that should hold a commit SHA. A value that is the variable's own - * name, such as `VERCEL_GIT_COMMIT_SHA=VERCEL_GIT_COMMIT_SHA`, is ignored: it never changes, so as - * a release name it would put every build into one release. - */ -function readCommitEnvVar(name: string): string | undefined { - const value = process.env[name]; - if (value?.trim() === name) { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] Ignoring the ${name} environment variable as a release name, because its value is its own name. Set the release explicitly, or fix the variable in your build environment.`, - ); - }); - return undefined; - } - return value; + return getReleaseFromCommitEnv() || fallback; } /** Node.js stack parser */ diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index bc2d74822d65..c79468692456 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -3,7 +3,6 @@ import type { Client, Integration } from '@sentry/core'; import { _INTERNAL_warnIfClientIsActive, consoleIntegration, - consoleSandbox, conversationIdIntegration, createStackParser, debug, @@ -18,7 +17,7 @@ import { requestDataIntegration, stackParserFromStackParserOptions, } from '@sentry/core'; -import { nodeStackLineParser } from '@sentry/core/server'; +import { getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; import { SentryPropagator, SentryTracerProvider, @@ -140,7 +139,6 @@ function setupOpenTelemetryLogger(): void { /** * Returns a release dynamically from environment variables. */ -// eslint-disable-next-line complexity export function getSentryRelease(fallback?: string): string | undefined { // Always read first as Sentry takes this as precedence if (process.env.SENTRY_RELEASE) { @@ -152,118 +150,7 @@ export function getSentryRelease(fallback?: string): string | undefined { return GLOBAL_OBJ.SENTRY_RELEASE.id; } - // This list is in approximate alpha order, separated into 3 categories: - // 1. Git providers - // 2. CI providers with specific environment variables (has the provider name in the variable name) - // 3. CI providers with generic environment variables (checked for last to prevent possible false positives) - // Each group is only read if the earlier ones found nothing, so a placeholder value is only - // warned about when it would otherwise have become the release. - - const possibleReleaseNameOfGitProvider = (): string | undefined => - // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables - readCommitEnvVar('GITHUB_SHA') || - // GitLab CI - https://docs.gitlab.com/ee/ci/variables/predefined_variables.html - readCommitEnvVar('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || - readCommitEnvVar('CI_BUILD_REF') || - readCommitEnvVar('CI_COMMIT_SHA') || - // Bitbucket - https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/ - readCommitEnvVar('BITBUCKET_COMMIT'); - - const possibleReleaseNameOfCiProvidersWithSpecificEnvVar = (): string | undefined => - // AppVeyor - https://www.appveyor.com/docs/environment-variables/ - readCommitEnvVar('APPVEYOR_PULL_REQUEST_HEAD_COMMIT') || - readCommitEnvVar('APPVEYOR_REPO_COMMIT') || - // AWS CodeBuild - https://docs.aws.amazon.com/codebuild/latest/userguide/build-env-ref-env-vars.html - readCommitEnvVar('CODEBUILD_RESOLVED_SOURCE_VERSION') || - // AWS Amplify - https://docs.aws.amazon.com/amplify/latest/userguide/environment-variables.html - readCommitEnvVar('AWS_COMMIT_ID') || - // Azure Pipelines - https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml - readCommitEnvVar('BUILD_SOURCEVERSION') || - // Bitrise - https://devcenter.bitrise.io/builds/available-environment-variables/ - readCommitEnvVar('GIT_CLONE_COMMIT_HASH') || - // Buddy CI - https://buddy.works/docs/pipelines/environment-variables#default-environment-variables - readCommitEnvVar('BUDDY_EXECUTION_REVISION') || - // Builtkite - https://buildkite.com/docs/pipelines/environment-variables - readCommitEnvVar('BUILDKITE_COMMIT') || - // CircleCI - https://circleci.com/docs/variables/ - readCommitEnvVar('CIRCLE_SHA1') || - // Cirrus CI - https://cirrus-ci.org/guide/writing-tasks/#environment-variables - readCommitEnvVar('CIRRUS_CHANGE_IN_REPO') || - // Codefresh - https://codefresh.io/docs/docs/codefresh-yaml/variables/ - readCommitEnvVar('CF_REVISION') || - // Codemagic - https://docs.codemagic.io/yaml-basic-configuration/environment-variables/ - readCommitEnvVar('CM_COMMIT') || - // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables - readCommitEnvVar('CF_PAGES_COMMIT_SHA') || - // Drone - https://docs.drone.io/pipeline/environment/reference/ - readCommitEnvVar('DRONE_COMMIT_SHA') || - // Flightcontrol - https://www.flightcontrol.dev/docs/guides/flightcontrol/environment-variables#built-in-environment-variables - readCommitEnvVar('FC_GIT_COMMIT_SHA') || - // Heroku #1 https://devcenter.heroku.com/articles/heroku-ci - readCommitEnvVar('HEROKU_TEST_RUN_COMMIT_VERSION') || - // Heroku #2 https://devcenter.heroku.com/articles/dyno-metadata#dyno-metadata - readCommitEnvVar('HEROKU_BUILD_COMMIT') || - // Heroku #3 (deprecated by Heroku, kept for backward compatibility) - readCommitEnvVar('HEROKU_SLUG_COMMIT') || - // Railway - https://docs.railway.app/reference/variables#git-variables - readCommitEnvVar('RAILWAY_GIT_COMMIT_SHA') || - // Render - https://render.com/docs/environment-variables - readCommitEnvVar('RENDER_GIT_COMMIT') || - // Semaphore CI - https://docs.semaphoreci.com/ci-cd-environment/environment-variables - readCommitEnvVar('SEMAPHORE_GIT_SHA') || - // TravisCI - https://docs.travis-ci.com/user/environment-variables/#default-environment-variables - readCommitEnvVar('TRAVIS_PULL_REQUEST_SHA') || - // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables - readCommitEnvVar('VERCEL_GIT_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITHUB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_GITLAB_COMMIT_SHA') || - readCommitEnvVar('VERCEL_BITBUCKET_COMMIT_SHA') || - // Zeit (now known as Vercel) - readCommitEnvVar('ZEIT_GITHUB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_GITLAB_COMMIT_SHA') || - readCommitEnvVar('ZEIT_BITBUCKET_COMMIT_SHA'); - - const possibleReleaseNameOfCiProvidersWithGenericEnvVar = (): string | undefined => - // CloudBees CodeShip - https://docs.cloudbees.com/docs/cloudbees-codeship/latest/pro-builds-and-configuration/environment-variables - readCommitEnvVar('CI_COMMIT_ID') || - // Coolify - https://coolify.io/docs/knowledge-base/environment-variables - readCommitEnvVar('SOURCE_COMMIT') || - // Heroku #3 https://devcenter.heroku.com/changelog-items/630 - readCommitEnvVar('SOURCE_VERSION') || - // Jenkins - https://plugins.jenkins.io/git/#environment-variables - readCommitEnvVar('GIT_COMMIT') || - // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata - readCommitEnvVar('COMMIT_REF') || - // TeamCity - https://www.jetbrains.com/help/teamcity/predefined-build-parameters.html - readCommitEnvVar('BUILD_VCS_NUMBER'); - // Woodpecker CI (https://woodpecker-ci.org/docs/usage/environment) sets CI_COMMIT_SHA, which the - // GitLab check above already reads. - - return ( - possibleReleaseNameOfGitProvider() || - possibleReleaseNameOfCiProvidersWithSpecificEnvVar() || - possibleReleaseNameOfCiProvidersWithGenericEnvVar() || - fallback - ); -} - -/** - * Reads an environment variable that should hold a commit SHA. A value that is the variable's own - * name, such as `VERCEL_GIT_COMMIT_SHA=VERCEL_GIT_COMMIT_SHA`, is ignored: it never changes, so as - * a release name it would put every build into one release. - */ -function readCommitEnvVar(name: string): string | undefined { - const value = process.env[name]; - if (value?.trim() === name) { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn( - `[Sentry] Ignoring the ${name} environment variable as a release name, because its value is its own name. Set the release explicitly, or fix the variable in your build environment.`, - ); - }); - return undefined; - } - return value; + return getReleaseFromCommitEnv() || fallback; } function getTraceLifecycleFromEnv(envVar: string | undefined): 'static' | 'stream' | undefined { From 3c5b60d42a455bfa179aeed08b895a716fc9db2f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 17:47:42 +0000 Subject: [PATCH 2/2] ref(core): Export commit env helpers as internal Export getReleaseFromCommitEnv and readCommitEnvVar from @sentry/core/server with the _INTERNAL_ prefix, so they are not part of the public API, and drop the PlaceholderCommitEnvVarHandler type export, which nothing imports. Note a subtle behaviour change from the previous commit: readCommitEnvVar now returns undefined for an empty variable. Release detection already skipped empty values, so the only caller that sees it is the bundler plugins' Vercel setCommits default: VERCEL_GIT_PREVIOUS_SHA="" now leaves previousCommit unset instead of sending an empty string. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01WgDm3ijhQZQUHGAFFqQzKQ --- packages/bundler-plugins/src/core/utils.ts | 7 +++++-- packages/core/src/server.ts | 6 ++++-- packages/node/src/sdk/api.ts | 2 +- packages/vercel-edge/src/sdk.ts | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/bundler-plugins/src/core/utils.ts b/packages/bundler-plugins/src/core/utils.ts index 8dfccd8f5b74..1e949c1b4785 100644 --- a/packages/bundler-plugins/src/core/utils.ts +++ b/packages/bundler-plugins/src/core/utils.ts @@ -6,7 +6,10 @@ import crypto from 'crypto'; import childProcess from 'child_process'; import type { SourceMap } from 'magic-string'; import MagicString from 'magic-string'; -import { getReleaseFromCommitEnv, readCommitEnvVar as readCoreCommitEnvVar } from '@sentry/core/server'; +import { + _INTERNAL_getReleaseFromCommitEnv as getReleaseFromCommitEnv, + _INTERNAL_readCommitEnvVar as readCoreCommitEnvVar, +} from '@sentry/core/server'; /** * Checks whether the given input is already an array, and if it isn't, wraps it in one. @@ -238,7 +241,7 @@ function warnAboutPlaceholderCommitEnvVar(name: string): void { /** * Reads an environment variable that should hold a commit SHA, ignoring a value that is the - * variable's own name. See `readCommitEnvVar` in `@sentry/core/server`. + * variable's own name. See `_INTERNAL_readCommitEnvVar` in `@sentry/core/server`. */ export function readCommitEnvVar(name: string): string | undefined { return readCoreCommitEnvVar(name, warnAboutPlaceholderCommitEnvVar); diff --git a/packages/core/src/server.ts b/packages/core/src/server.ts index f981065c14c8..f328253a9208 100644 --- a/packages/core/src/server.ts +++ b/packages/core/src/server.ts @@ -12,8 +12,10 @@ export { wrapMcpServerWithSentry } from './integrations/mcp-server'; export { isNodeEnv, loadModule } from './utils/node'; export { filenameIsInApp, node, nodeStackLineParser } from './utils/node-stack-trace'; export { vercelWaitUntil } from './utils/vercelWaitUntil'; -export { getReleaseFromCommitEnv, readCommitEnvVar } from './utils/commitEnv'; -export type { PlaceholderCommitEnvVarHandler } from './utils/commitEnv'; +export { + getReleaseFromCommitEnv as _INTERNAL_getReleaseFromCommitEnv, + readCommitEnvVar as _INTERNAL_readCommitEnvVar, +} from './utils/commitEnv'; export { flushIfServerless } from './utils/flushIfServerless'; export { callFrameToStackFrame, watchdogTimer } from './utils/anr'; export { safeUnref as _INTERNAL_safeUnref } from './utils/timer'; diff --git a/packages/node/src/sdk/api.ts b/packages/node/src/sdk/api.ts index 71fc95c3095e..2e07f45297d8 100644 --- a/packages/node/src/sdk/api.ts +++ b/packages/node/src/sdk/api.ts @@ -2,7 +2,7 @@ import type { StackParser } from '@sentry/core'; import { createStackParser, GLOBAL_OBJ } from '@sentry/core'; -import { getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; +import { _INTERNAL_getReleaseFromCommitEnv as getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; import { createGetModuleFromFilename } from '../utils/module'; /** diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index c79468692456..47522657a510 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -17,7 +17,7 @@ import { requestDataIntegration, stackParserFromStackParserOptions, } from '@sentry/core'; -import { getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; +import { _INTERNAL_getReleaseFromCommitEnv as getReleaseFromCommitEnv, nodeStackLineParser } from '@sentry/core/server'; import { SentryPropagator, SentryTracerProvider,