diff --git a/server/index.ts b/server/index.ts index 5caed72cb..874cd57f1 100644 --- a/server/index.ts +++ b/server/index.ts @@ -87,11 +87,17 @@ const getHost = (req: { get: (key: string) => string | undefined }) => const MODE = process.env.NODE_ENV -if (MODE === 'production' && process.env.SENTRY_DSN) { +const SHOULD_INIT_SENTRY = + MODE === 'production' && + Boolean(process.env.SENTRY_DSN) && + // `start:mocks` (used in CI + local e2e) runs with `MOCKS=true`. + process.env.MOCKS !== 'true' + +if (SHOULD_INIT_SENTRY) { void import('./utils/monitoring.js').then(({ init }) => init()) } -if (MODE === 'production') { +if (SHOULD_INIT_SENTRY) { sentryInit({ dsn: process.env.SENTRY_DSN, tracesSampleRate: 0.3, diff --git a/vite.config.ts b/vite.config.ts index 040b8b91e..e6ec0e741 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,13 +2,36 @@ import 'dotenv/config' import { reactRouter } from '@react-router/dev/vite' import { sentryVitePlugin } from '@sentry/vite-plugin' import tailwindcss from '@tailwindcss/vite' -import { glob } from 'glob' import { defineConfig } from 'vite' import { envOnlyMacros } from 'vite-env-only' import { cjsInterop } from 'vite-plugin-cjs-interop' import tsconfigPaths from 'vite-tsconfig-paths' const MODE = process.env.NODE_ENV +const SENTRY_UPLOAD = + process.env.SENTRY_UPLOAD === 'true' || process.env.SENTRY_UPLOAD === '1' + +if (SENTRY_UPLOAD && MODE === 'production') { + const authToken = process.env.SENTRY_AUTH_TOKEN + const project = process.env.SENTRY_PROJECT + const org = process.env.SENTRY_ORG + // New-style Sentry auth tokens (prefix "sntrys_") embed the org, so SENTRY_ORG + // is not required when using one. + const tokenImpliesOrg = Boolean(authToken?.startsWith('sntrys_')) + + // If upload is "on" but required settings are missing, the Sentry plugin will + // just warn + skip the upload. Fail fast so we don't silently deploy without + // sourcemaps in Sentry. + if (!authToken) { + throw new Error('SENTRY_UPLOAD is enabled, but SENTRY_AUTH_TOKEN is missing') + } + if (!project) { + throw new Error('SENTRY_UPLOAD is enabled, but SENTRY_PROJECT is missing') + } + if (!org && !tokenImpliesOrg) { + throw new Error('SENTRY_UPLOAD is enabled, but SENTRY_ORG is missing') + } +} export default defineConfig(async () => { return { @@ -24,28 +47,31 @@ export default defineConfig(async () => { tailwindcss(), reactRouter(), tsconfigPaths(), - process.env.SENTRY_UPLOAD + SENTRY_UPLOAD ? sentryVitePlugin({ disable: MODE !== 'production', authToken: process.env.SENTRY_AUTH_TOKEN, org: process.env.SENTRY_ORG, project: process.env.SENTRY_PROJECT, + // By default the bundler plugin logs and continues on upload/release + // errors. Fail the build so we don't deploy with broken symbolication. + errorHandler: (err) => { + throw err + }, release: { name: process.env.COMMIT_SHA, setCommits: { auto: true, }, }, - sourcemaps: { - filesToDeleteAfterUpload: await glob([ - './build/**/*.map', - '.server-build/**/*.map', - ]), - }, }) : null, ], build: { + // This is an OSS project, so it's fine to generate "regular" sourcemaps. + // If we ever want sourcemaps upload without exposing them publicly, switch + // to `sourcemap: 'hidden'` (and keep uploading to Sentry). + sourcemap: true, cssMinify: MODE === 'production', rollupOptions: { external: [/node:.*/, 'stream', 'crypto'],