From 00eee3abc887db59498dac79515eb808db11a865 Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Thu, 23 Jul 2026 11:25:46 +0200 Subject: [PATCH] feat(sentry): enable Sentry for Tauri builds --- .github/workflows/tauri-build.yml | 15 ++ docs/SENTRY_INTEGRATION.md | 9 ++ src-tauri/Cargo.lock | 221 ++++++++++++++++++++++++++-- src-tauri/Cargo.toml | 9 ++ src-tauri/build.rs | 13 ++ src-tauri/src/lib.rs | 4 + src-tauri/src/sentry.rs | 35 +++++ src/app/generated/tauri/commands.ts | 4 + src/app/generated/tauri/types.ts | 5 + src/instrument.ts | 8 + vite.config.ts | 7 +- 11 files changed, 314 insertions(+), 16 deletions(-) create mode 100644 src-tauri/src/sentry.rs diff --git a/.github/workflows/tauri-build.yml b/.github/workflows/tauri-build.yml index a427044e72..6dae65e73e 100644 --- a/.github/workflows/tauri-build.yml +++ b/.github/workflows/tauri-build.yml @@ -140,6 +140,11 @@ jobs: VERSION: ${{ needs.setup-release.outputs.version }} VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} VITE_IS_RELEASE_TAG: ${{ needs.setup-release.outputs.nightly == 'true' && 'true' || '' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} NODE_OPTIONS: --max-old-space-size=8192 TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} @@ -329,6 +334,11 @@ jobs: VERSION: ${{ needs.setup-release.outputs.version }} VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} VITE_IS_RELEASE_TAG: ${{ needs.setup-release.outputs.nightly == 'true' && 'true' || '' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} NODE_OPTIONS: --max-old-space-size=8192 ANDROID_KEY_BASE64: ${{ secrets.ANDROID_KEY_BASE64 }} ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} @@ -435,6 +445,11 @@ jobs: VERSION: ${{ needs.setup-release.outputs.version }} VITE_APP_VERSION: ${{ needs.setup-release.outputs.version }} VITE_IS_RELEASE_TAG: ${{ needs.setup-release.outputs.nightly == 'true' && 'true' || '' }} + VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }} + VITE_SENTRY_ENVIRONMENT: ${{ needs.setup-release.outputs.nightly == 'true' && 'preview' || 'production' }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} NODE_OPTIONS: --max-old-space-size=8192 steps: - name: Checkout repository diff --git a/docs/SENTRY_INTEGRATION.md b/docs/SENTRY_INTEGRATION.md index df5511e574..a926feb44e 100644 --- a/docs/SENTRY_INTEGRATION.md +++ b/docs/SENTRY_INTEGRATION.md @@ -250,6 +250,15 @@ docker build \ - Full debugging capabilities for testing - Configured in `.github/workflows/cloudflare-web-preview.yml` +**Desktop & mobile (Tauri) builds (`.github/workflows/tauri-build.yml`):** + +- Tagged releases (`v*`) → `VITE_SENTRY_ENVIRONMENT=production` (10% sampling) +- Nightly builds (from `dev`) → `VITE_SENTRY_ENVIRONMENT=preview` (100% sampling) +- `VITE_SENTRY_DSN`, `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, and `SENTRY_PROJECT` are injected from repository secrets into the `build`, `android`, and `ios` jobs +- Source maps are generated for release builds **only** when the Sentry upload credentials are present, then uploaded and deleted from the bundle via `filesToDeleteAfterUpload` — so no `.map` files ship inside the app bundle. Without credentials, no source maps are emitted +- The `release` is `VITE_APP_VERSION` (the tag version, or the nightly version), matching the release the source maps are uploaded against so Sentry can un-minify stack traces +- Native (Rust) panics are captured via the `sentry` crate, consent-gated to match the JS opt-in: the SDK initialises at startup but a `before_send` hook drops all events until the frontend calls `set_native_sentry_enabled(true)` after its `sable_sentry_enabled` check + **Local development:** - `VITE_SENTRY_ENVIRONMENT` not set (defaults to `development` via Vite MODE) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index fd5d34206a..51682fb889 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -18,6 +18,15 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -148,7 +157,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -159,7 +168,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -449,6 +458,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + [[package]] name = "base64" version = "0.21.7" @@ -1313,6 +1337,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "defmt" version = "1.1.1" @@ -1481,7 +1515,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1783,7 +1817,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1869,6 +1903,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -2239,6 +2285,12 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "gio" version = "0.18.4" @@ -2529,6 +2581,17 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hostname" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" +dependencies = [ + "cfg-if", + "libc", + "windows-link 0.2.1", +] + [[package]] name = "html5ever" version = "0.29.1" @@ -3548,7 +3611,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3672,7 +3735,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3979,6 +4042,15 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -4068,7 +4140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5130,6 +5202,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + [[package]] name = "rustc-hash" version = "2.1.2" @@ -5155,7 +5233,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5213,7 +5291,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5262,6 +5340,7 @@ dependencies = [ "objc2-foundation", "percent-encoding", "reqwest 0.12.28", + "sentry", "serde", "serde_json", "sha2", @@ -5458,6 +5537,111 @@ dependencies = [ "serde_core", ] +[[package]] +name = "sentry" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d631477761f57c76148456e55e80e9a479ff3fa4c65b2b4a0c3acf1167fd4638" +dependencies = [ + "cfg_aliases", + "httpdate", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "448b0981fbde6cdc9eb087ba3dc01035a253fef9a0d9e79aaf198fc26acb2e64" +dependencies = [ + "backtrace", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e909d02170ba6d1dc5ebd05bef7999280f5d960e3eaee5d1a18d88d41b334" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecf0b1a4a4e9ec88395b52e6fa4868b95564c1fa96b26a0606f5f6288a0f7149" +dependencies = [ + "rand 0.9.4", + "sentry-types", + "serde", + "serde_json", + "url", +] + +[[package]] +name = "sentry-debug-images" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81d7749c57fc78ed52134889e8121da874065bc40da788d5798cce0f8c19f15" +dependencies = [ + "findshlibs", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7d93d6ecb55d2251c5fc084c55c03a2bc68904918d76117e602c999e92f00f" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58d26379236c4ef97eaf081bca3c7bf0ef6f06b3aa881eca2ee8e8dbc923e5b8" +dependencies = [ + "bitflags 2.11.1", + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2239099d47e76857b0825a182ecdb90159add7aa1097b60247c6e7ca6bf49c6a" +dependencies = [ + "debugid", + "hex", + "rand 0.9.4", + "serde", + "serde_json", + "thiserror 2.0.18", + "time", + "url", + "uuid", +] + [[package]] name = "serde" version = "1.0.228" @@ -5795,7 +5979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6842,7 +7026,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7468,7 +7652,7 @@ dependencies = [ "png 0.18.1", "serde", "thiserror 2.0.18", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -7542,7 +7726,16 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.60.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", ] [[package]] @@ -8212,7 +8405,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index aedc31083f..b177357f36 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -22,6 +22,15 @@ tauri-typegen = "0.5" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4" +# default-features = false drops reqwest + native-tls (openssl); ureq is a sync +# rustls transport with no async-runtime dependency. +sentry = { version = "0.48.5", default-features = false, features = [ + "backtrace", + "contexts", + "panic", + "debug-images", + "ureq", +] } tokio = { version = "1", features = ["time", "sync", "fs", "io-util"] } ts-rs = "12.0.0" sha2 = "0.10" diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 704ebff1be..f51d01ff68 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,6 +1,19 @@ fn main() { println!("cargo:rustc-env=TS_RS_EXPORT_DIR=../src/app/generated/tauri"); + if let Ok(dsn) = std::env::var("VITE_SENTRY_DSN") { + println!("cargo:rustc-env=SENTRY_DSN={dsn}"); + } + if let Ok(env) = std::env::var("VITE_SENTRY_ENVIRONMENT") { + println!("cargo:rustc-env=SENTRY_ENVIRONMENT={env}"); + } + if let Ok(version) = std::env::var("VITE_APP_VERSION") { + println!("cargo:rustc-env=SENTRY_APP_VERSION={version}"); + } + println!("cargo:rerun-if-env-changed=VITE_SENTRY_DSN"); + println!("cargo:rerun-if-env-changed=VITE_SENTRY_ENVIRONMENT"); + println!("cargo:rerun-if-env-changed=VITE_APP_VERSION"); + // Find libcef.so next to the binary (CEF ships it there). if std::env::var_os("CARGO_FEATURE_CEF").is_some() && std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0a90139843..a005b906ca 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7,6 +7,7 @@ mod ios; #[cfg(target_os = "android")] mod mobile; mod network; +mod sentry; mod share_inbox; use tauri::{AppHandle, Manager}; @@ -217,6 +218,8 @@ pub fn show_or_create_main_window(app: &AppHandle) -> taur #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + let _sentry_guard = sentry::init(); + let builder = tauri::Builder::::new(); // macOS needs a standard menu (with the Edit submenu) for keyboard @@ -353,6 +356,7 @@ pub fn run() { network::native_upload::abort_native_upload, network::media_protocol::set_media_session, network::media_protocol::clear_media_session, + sentry::set_native_sentry_enabled, share_inbox::share_inbox_drain, share_inbox::share_inbox_read, share_inbox::share_inbox_clear, diff --git a/src-tauri/src/sentry.rs b/src-tauri/src/sentry.rs new file mode 100644 index 0000000000..dfba7681f4 --- /dev/null +++ b/src-tauri/src/sentry.rs @@ -0,0 +1,35 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +use sentry::protocol::Event; + +static CONSENT: AtomicBool = AtomicBool::new(false); + +/// Initialise Sentry before the Tauri runtime spawns threads. The guard +/// flushes pending events on drop. No-op when no DSN was baked in at build time. +pub fn init() -> Option { + let dsn = option_env!("SENTRY_DSN")?; + let environment = option_env!("SENTRY_ENVIRONMENT"); + let release = option_env!("SENTRY_APP_VERSION").map(|v| format!("sable@{v}")); + + let guard = sentry::init(sentry::ClientOptions { + dsn: dsn.parse().ok(), + release: release.map(Into::into), + environment: environment.map(Into::into), + send_default_pii: false, + before_send: Some(std::sync::Arc::new(|event: Event<'static>| { + if CONSENT.load(Ordering::Relaxed) { + Some(event) + } else { + None + } + })), + ..Default::default() + }); + + Some(guard) +} + +#[tauri::command] +pub fn set_native_sentry_enabled(enabled: bool) { + CONSENT.store(enabled, Ordering::Relaxed); +} diff --git a/src/app/generated/tauri/commands.ts b/src/app/generated/tauri/commands.ts index c8a0243cb9..7b710d3013 100644 --- a/src/app/generated/tauri/commands.ts +++ b/src/app/generated/tauri/commands.ts @@ -54,6 +54,10 @@ export async function setMediaSession(params: types.SetMediaSessionParams): Prom return invoke('set_media_session', params); } +export async function setNativeSentryEnabled(params: types.SetNativeSentryEnabledParams): Promise { + return invoke('set_native_sentry_enabled', params); +} + export async function setNavigationBarColor(params: types.SetNavigationBarColorParams): Promise { return invoke('set_navigation_bar_color', params); } diff --git a/src/app/generated/tauri/types.ts b/src/app/generated/tauri/types.ts index 4169494f91..bdcae30cb5 100644 --- a/src/app/generated/tauri/types.ts +++ b/src/app/generated/tauri/types.ts @@ -103,6 +103,11 @@ export interface SetMediaSessionParams { [key: string]: unknown; } +export interface SetNativeSentryEnabledParams { + enabled: boolean; + [key: string]: unknown; +} + export interface SetNavigationBarColorParams { color: number; [key: string]: unknown; diff --git a/src/instrument.ts b/src/instrument.ts index 02c0a6441f..a67c029ffe 100644 --- a/src/instrument.ts +++ b/src/instrument.ts @@ -16,6 +16,8 @@ import { matchRoutes, } from 'react-router-dom'; import { scrubMatrixIds, sanitizeSentryPayload, scrubMatrixUrl } from './app/utils/sentryScrubbers'; +import { isTauri } from '@tauri-apps/api/core'; +import { setNativeSentryEnabled } from './app/generated/tauri/commands'; const dsn = import.meta.env.VITE_SENTRY_DSN; const environment = import.meta.env.VITE_SENTRY_ENVIRONMENT || import.meta.env.MODE; @@ -293,5 +295,11 @@ if (dsn && sentryEnabled) { console.info('[Sentry] Disabled - no DSN provided'); } +if (isTauri()) { + setNativeSentryEnabled({ enabled: sentryEnabled }).catch((err) => + console.warn('[Sentry] Failed to sync native crash capture consent', err) + ); +} + // Export Sentry for use in other parts of the application export { Sentry }; diff --git a/vite.config.ts b/vite.config.ts index 848723f4a8..2e68718d0d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -53,6 +53,9 @@ const tauriDevHost = process.env.TAURI_DEV_HOST; const isTauriBuild = Boolean(process.env.TAURI_ENV_PLATFORM); const isTauriDebug = process.env.TAURI_ENV_DEBUG === 'true'; const tauriBuildMinify = !isTauriDebug ? 'esbuild' : false; +const sentryUploadEnabled = Boolean( + process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT +); const isReleaseTag = (() => { const envVal = process.env.VITE_IS_RELEASE_TAG; @@ -237,7 +240,7 @@ export default defineConfig(({ command }) => ({ ] : []), // Sentry source map upload — only active when credentials are provided at build time - ...(process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_ORG && process.env.SENTRY_PROJECT + ...(sentryUploadEnabled ? [ sentryVitePlugin({ org: process.env.SENTRY_ORG, @@ -288,7 +291,7 @@ export default defineConfig(({ command }) => ({ // vite-plugin-top-level-await re-transpiles chunks (see vitejs/vite#22225). target: 'es2022', minify: isTauriBuild ? tauriBuildMinify : undefined, - sourcemap: isTauriBuild ? isTauriDebug : true, + sourcemap: isTauriBuild ? isTauriDebug || sentryUploadEnabled : true, outDir: 'dist', copyPublicDir: false, rollupOptions: {