From 53189509699a7c31823128cd1cbff3cf4fdbefe0 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Mon, 19 Feb 2024 14:57:31 +0100 Subject: [PATCH] feat(node-experimental): Move `defaultStackParser` & `getSentryRelease` These were imported from `@sentry/node` before. Also moves the `createGetModuleFromFilename` utility over, which is needed for `defaultStackParser`. --- packages/node-experimental/src/index.ts | 6 +- packages/node-experimental/src/sdk/api.ts | 41 ++++++++++++- packages/node-experimental/src/sdk/init.ts | 8 +-- .../node-experimental/src/utils/module.ts | 57 +++++++++++++++++++ 4 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 packages/node-experimental/src/utils/module.ts diff --git a/packages/node-experimental/src/index.ts b/packages/node-experimental/src/index.ts index 22e29a146df2..b988822a5a38 100644 --- a/packages/node-experimental/src/index.ts +++ b/packages/node-experimental/src/index.ts @@ -17,7 +17,8 @@ export * as Handlers from './sdk/handlers'; export type { Span } from './types'; export { startSpan, startSpanManual, startInactiveSpan, getActiveSpan, withActiveSpan } from '@sentry/opentelemetry'; -export { getClient } from './sdk/api'; +export { getClient, getSentryRelease, defaultStackParser } from './sdk/api'; +export { createGetModuleFromFilename } from './utils/module'; // eslint-disable-next-line deprecation/deprecation export { getCurrentHub } from './sdk/hub'; @@ -25,15 +26,12 @@ export { addBreadcrumb, isInitialized, makeNodeTransport, - defaultStackParser, - getSentryRelease, getGlobalScope, addRequestDataToEvent, DEFAULT_USER_INCLUDES, extractRequestData, // eslint-disable-next-line deprecation/deprecation getModuleFromFilename, - createGetModuleFromFilename, close, createTransport, flush, diff --git a/packages/node-experimental/src/sdk/api.ts b/packages/node-experimental/src/sdk/api.ts index 0f0a4de321aa..2608795a8264 100644 --- a/packages/node-experimental/src/sdk/api.ts +++ b/packages/node-experimental/src/sdk/api.ts @@ -1,7 +1,9 @@ // PUBLIC APIS import { getCurrentScope } from '@sentry/core'; -import type { Client } from '@sentry/types'; +import type { Client, StackParser } from '@sentry/types'; +import { GLOBAL_OBJ, createStackParser, nodeStackLineParser } from '@sentry/utils'; +import { createGetModuleFromFilename } from '../utils/module'; /** Get the currently active client. */ export function getClient(): C { @@ -15,3 +17,40 @@ export function getClient(): C { // TODO otherwise ensure we use a noop client return {} as C; } + +/** + * Returns a release dynamically from environment variables. + */ +export function getSentryRelease(fallback?: string): string | undefined { + // Always read first as Sentry takes this as precedence + if (process.env.SENTRY_RELEASE) { + return process.env.SENTRY_RELEASE; + } + + // This supports the variable that sentry-webpack-plugin injects + if (GLOBAL_OBJ.SENTRY_RELEASE && GLOBAL_OBJ.SENTRY_RELEASE.id) { + return GLOBAL_OBJ.SENTRY_RELEASE.id; + } + + return ( + // GitHub Actions - https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables + process.env.GITHUB_SHA || + // Netlify - https://docs.netlify.com/configure-builds/environment-variables/#build-metadata + process.env.COMMIT_REF || + // Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables + process.env.VERCEL_GIT_COMMIT_SHA || + process.env.VERCEL_GITHUB_COMMIT_SHA || + process.env.VERCEL_GITLAB_COMMIT_SHA || + process.env.VERCEL_BITBUCKET_COMMIT_SHA || + // Zeit (now known as Vercel) + process.env.ZEIT_GITHUB_COMMIT_SHA || + process.env.ZEIT_GITLAB_COMMIT_SHA || + process.env.ZEIT_BITBUCKET_COMMIT_SHA || + // Cloudflare Pages - https://developers.cloudflare.com/pages/platform/build-configuration/#environment-variables + process.env.CF_PAGES_COMMIT_SHA || + fallback + ); +} + +/** Node.js stack parser */ +export const defaultStackParser: StackParser = createStackParser(nodeStackLineParser(createGetModuleFromFilename())); diff --git a/packages/node-experimental/src/sdk/init.ts b/packages/node-experimental/src/sdk/init.ts index 78d9fa7ef572..3c2d09fb3847 100644 --- a/packages/node-experimental/src/sdk/init.ts +++ b/packages/node-experimental/src/sdk/init.ts @@ -6,12 +6,7 @@ import { hasTracingEnabled, startSession, } from '@sentry/core'; -import { - defaultStackParser, - getDefaultIntegrations as getDefaultNodeIntegrations, - getSentryRelease, - spotlightIntegration, -} from '@sentry/node'; +import { getDefaultIntegrations as getDefaultNodeIntegrations, spotlightIntegration } from '@sentry/node'; import { setOpenTelemetryContextAsyncContextStrategy } from '@sentry/opentelemetry'; import type { Client, Integration, Options } from '@sentry/types'; import { @@ -28,6 +23,7 @@ import { httpIntegration } from '../integrations/http'; import { nativeNodeFetchIntegration } from '../integrations/node-fetch'; import { makeNodeTransport } from '../transports'; import type { NodeClientOptions, NodeOptions } from '../types'; +import { defaultStackParser, getSentryRelease } from './api'; import { NodeClient } from './client'; import { initOtel } from './initOtel'; diff --git a/packages/node-experimental/src/utils/module.ts b/packages/node-experimental/src/utils/module.ts new file mode 100644 index 000000000000..d873bf9b2f2e --- /dev/null +++ b/packages/node-experimental/src/utils/module.ts @@ -0,0 +1,57 @@ +import { posix, sep } from 'path'; +import { dirname } from '@sentry/utils'; + +/** normalizes Windows paths */ +function normalizeWindowsPath(path: string): string { + return path + .replace(/^[A-Z]:/, '') // remove Windows-style prefix + .replace(/\\/g, '/'); // replace all `\` instances with `/` +} + +/** Creates a function that gets the module name from a filename */ +export function createGetModuleFromFilename( + basePath: string = process.argv[1] ? dirname(process.argv[1]) : process.cwd(), + isWindows: boolean = sep === '\\', +): (filename: string | undefined) => string | undefined { + const normalizedBase = isWindows ? normalizeWindowsPath(basePath) : basePath; + + return (filename: string | undefined) => { + if (!filename) { + return; + } + + const normalizedFilename = isWindows ? normalizeWindowsPath(filename) : filename; + + // eslint-disable-next-line prefer-const + let { dir, base: file, ext } = posix.parse(normalizedFilename); + + if (ext === '.js' || ext === '.mjs' || ext === '.cjs') { + file = file.slice(0, ext.length * -1); + } + + if (!dir) { + // No dirname whatsoever + dir = '.'; + } + + const n = dir.lastIndexOf('/node_modules'); + if (n > -1) { + return `${dir.slice(n + 14).replace(/\//g, '.')}:${file}`; + } + + // Let's see if it's a part of the main module + // To be a part of main module, it has to share the same base + if (dir.startsWith(normalizedBase)) { + let moduleName = dir.slice(normalizedBase.length + 1).replace(/\//g, '.'); + + if (moduleName) { + moduleName += ':'; + } + moduleName += file; + + return moduleName; + } + + return file; + }; +}