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()), + }; +}