From aed28c5f68aea8aaca05a4d0a59c6ce164008a81 Mon Sep 17 00:00:00 2001 From: Andy <119136210+AndyMik90@users.noreply.github.com> Date: Tue, 13 Jan 2026 17:59:11 +0100 Subject: [PATCH] feat(sentry): embed Sentry DSN at build time for packaged apps (#1025) * feat(sentry): integrate Sentry configuration into Electron build - Added build-time constants for Sentry DSN and sampling rates in electron.vite.config.ts. - Enhanced environment variable handling in env-utils.ts to include Sentry settings for subprocesses. - Implemented getSentryEnvForSubprocess function in sentry.ts to provide Sentry environment variables for Python backends. - Updated Sentry-related functions to prioritize build-time constants over runtime environment variables for improved reliability. This integration ensures that Sentry is properly configured for both local development and CI environments. * fix(sentry): add typeof guards for build-time constants in tests The __SENTRY_*__ constants are only defined when Vite's define plugin runs during build. In test environments (vitest), these constants are undefined and cause ReferenceError. Added typeof guards to safely handle both cases. Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- apps/frontend/electron.vite.config.ts | 17 +++++++ apps/frontend/src/main/env-utils.ts | 11 ++++ apps/frontend/src/main/sentry.ts | 73 ++++++++++++++++++++------- 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/apps/frontend/electron.vite.config.ts b/apps/frontend/electron.vite.config.ts index 455082a5..6ceaa51f 100644 --- a/apps/frontend/electron.vite.config.ts +++ b/apps/frontend/electron.vite.config.ts @@ -2,8 +2,24 @@ import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; +/** + * Sentry configuration embedded at build time. + * + * In CI builds, these come from GitHub secrets. + * In local development, these come from apps/frontend/.env (loaded by dotenv). + * + * The `define` option replaces these values at build time, so they're + * embedded in the bundle and available at runtime in packaged apps. + */ +const sentryDefines = { + '__SENTRY_DSN__': JSON.stringify(process.env.SENTRY_DSN || ''), + '__SENTRY_TRACES_SAMPLE_RATE__': JSON.stringify(process.env.SENTRY_TRACES_SAMPLE_RATE || '0.1'), + '__SENTRY_PROFILES_SAMPLE_RATE__': JSON.stringify(process.env.SENTRY_PROFILES_SAMPLE_RATE || '0.1'), +}; + export default defineConfig({ main: { + define: sentryDefines, plugins: [externalizeDepsPlugin({ // Bundle these packages into the main process (they won't be in node_modules in packaged app) exclude: [ @@ -43,6 +59,7 @@ export default defineConfig({ } }, renderer: { + define: sentryDefines, root: resolve(__dirname, 'src/renderer'), build: { rollupOptions: { diff --git a/apps/frontend/src/main/env-utils.ts b/apps/frontend/src/main/env-utils.ts index 8d4b8847..fc54f2ac 100644 --- a/apps/frontend/src/main/env-utils.ts +++ b/apps/frontend/src/main/env-utils.ts @@ -15,6 +15,7 @@ import * as fs from 'fs'; import { promises as fsPromises } from 'fs'; import { execFileSync, execFile } from 'child_process'; import { promisify } from 'util'; +import { getSentryEnvForSubprocess } from './sentry'; const execFileAsync = promisify(execFile); @@ -237,6 +238,11 @@ export function getAugmentedEnv(additionalPaths?: string[]): Record= 0 && parsed <= 1) { return parsed; @@ -61,13 +72,16 @@ function getTracesSampleRate(): number { } /** - * Get profile sample rate from environment variable + * Get profile sample rate from build-time constant * Controls profiling sampling relative to traces (0.0 to 1.0) * Default: 0.1 (10%) in production, 0 in development */ function getProfilesSampleRate(): number { - const envValue = process.env.SENTRY_PROFILES_SAMPLE_RATE; - if (envValue !== undefined) { + // Try build-time constant first, then runtime env var + // typeof guard needed for test environments where Vite's define doesn't apply + const buildTimeValue = typeof __SENTRY_PROFILES_SAMPLE_RATE__ !== 'undefined' ? __SENTRY_PROFILES_SAMPLE_RATE__ : ''; + const envValue = buildTimeValue || process.env.SENTRY_PROFILES_SAMPLE_RATE; + if (envValue) { const parsed = parseFloat(envValue); if (!isNaN(parsed) && parsed >= 0 && parsed <= 1) { return parsed; @@ -165,3 +179,28 @@ export function setSentryEnabled(enabled: boolean): void { sentryEnabledState = enabled; console.log(`[Sentry] Error reporting ${enabled ? 'enabled' : 'disabled'} (programmatic)`); } + +/** + * Get Sentry environment variables for passing to Python subprocesses + * + * This returns the build-time embedded values so that Python backends + * can also report errors to Sentry in packaged apps. + * + * Usage: + * ```typescript + * const env = { ...getAugmentedEnv(), ...getSentryEnvForSubprocess() }; + * spawn(pythonPath, args, { env }); + * ``` + */ +export function getSentryEnvForSubprocess(): Record { + const dsn = getSentryDsn(); + if (!dsn) { + return {}; + } + + return { + SENTRY_DSN: dsn, + SENTRY_TRACES_SAMPLE_RATE: String(getTracesSampleRate()), + SENTRY_PROFILES_SAMPLE_RATE: String(getProfilesSampleRate()), + }; +}