forked from akai/readest
feat(sentry): add crash reporting for Android, iOS, desktop, and web (#4914)
Report crashes and unhandled errors from every layer to one Sentry project via a single build-time SENTRY_DSN (empty => disabled, so local and fork builds do not report): - JS/WebView + Rust panics via tauri-plugin-sentry (rustls transport; minidump handler desktop-only). - Android native (JVM/NDK/ANR) via sentry-android 8.47.0 with manifest auto-init (excludes the discontinued lifecycle-common-java8 transitive). - iOS native via sentry-cocoa started from a tracked SentrySupport/+load bootstrap that reads the DSN from a Rust readest_sentry_dsn() FFI (no generated-file edits). - SENTRY_DSN resolved at build time from the environment, then .env.local, then .env (build.rs bakes it via cargo:rustc-env; build.gradle.kts for Android). CI passes the SENTRY_DSN secret through the existing .env.local step. Crashes + errors only: traces sample rate 0, no session replay, no PII. Symbolication (source maps / ProGuard / dSYM upload) is a follow-up. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -142,6 +142,7 @@ jobs:
|
||||
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env.local
|
||||
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}" >> .env.local
|
||||
echo "NEXT_PUBLIC_APP_PLATFORM=tauri" >> .env.local
|
||||
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env.local
|
||||
cp .env.local apps/readest-app/.env.local
|
||||
|
||||
- name: install rclone
|
||||
|
||||
@@ -229,6 +229,7 @@ jobs:
|
||||
echo "NEXT_PUBLIC_SUPABASE_URL=${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}" >> .env.local
|
||||
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}" >> .env.local
|
||||
echo "NEXT_PUBLIC_APP_PLATFORM=tauri" >> .env.local
|
||||
echo "SENTRY_DSN=${{ secrets.SENTRY_DSN }}" >> .env.local
|
||||
cp .env.local apps/readest-app/.env.local
|
||||
|
||||
- name: build and upload Android apks
|
||||
|
||||
Generated
+448
-5
@@ -36,6 +36,7 @@ dependencies = [
|
||||
"read-progress-stream",
|
||||
"reqwest 0.12.28",
|
||||
"semver",
|
||||
"sentry",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
@@ -57,6 +58,7 @@ dependencies = [
|
||||
"tauri-plugin-os",
|
||||
"tauri-plugin-persisted-scope",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-sentry",
|
||||
"tauri-plugin-sharekit",
|
||||
"tauri-plugin-shell",
|
||||
"tauri-plugin-sign-in-with-apple",
|
||||
@@ -75,6 +77,15 @@ dependencies = [
|
||||
"zip 2.4.2",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -545,6 +556,21 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -749,7 +775,7 @@ checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a"
|
||||
dependencies = [
|
||||
"borsh-derive",
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1031,6 +1057,12 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||
|
||||
[[package]]
|
||||
name = "cfg_aliases"
|
||||
version = "0.2.1"
|
||||
@@ -1341,6 +1373,30 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crash-context"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "031ed29858d90cfdf27fe49fae28028a1f20466db97962fa2f4ea34809aeebf3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"mach2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crash-handler"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2066907075af649bcb8bcb1b9b986329b243677e6918b2d920aa64b0aac5ace3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crash-context",
|
||||
"libc",
|
||||
"mach2",
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32c"
|
||||
version = "0.6.8"
|
||||
@@ -1622,6 +1678,16 @@ dependencies = [
|
||||
"keyring-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debugid"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"uuid 1.23.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "default-net"
|
||||
version = "0.22.0"
|
||||
@@ -2731,6 +2797,12 @@ dependencies = [
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -2895,6 +2967,17 @@ dependencies = [
|
||||
"system-deps 7.0.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "goblin"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b363a30c165f666402fe6a3024d3bec7ebc898f96a4a23bd1c99f8dbf3f4f47"
|
||||
dependencies = [
|
||||
"log",
|
||||
"plain",
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gtk"
|
||||
version = "0.18.2"
|
||||
@@ -3069,6 +3152,17 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -3984,6 +4078,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
@@ -4127,6 +4230,75 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minidump-common"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c4d14bcca0fd3ed165a03000480aaa364c6860c34e900cb2dafdf3b95340e77"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"debugid",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"range-map",
|
||||
"scroll",
|
||||
"smart-default",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minidump-writer"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"byteorder",
|
||||
"cfg-if",
|
||||
"crash-context",
|
||||
"goblin",
|
||||
"libc",
|
||||
"log",
|
||||
"mach2",
|
||||
"memmap2",
|
||||
"memoffset",
|
||||
"minidump-common",
|
||||
"nix 0.28.0",
|
||||
"procfs-core",
|
||||
"scroll",
|
||||
"tempfile",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minidumper"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4ebc9d1f8847ec1d078f78b35ed598e0ebefa1f242d5f83cd8d7f03960a7d1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crash-context",
|
||||
"libc",
|
||||
"log",
|
||||
"minidump-writer",
|
||||
"parking_lot",
|
||||
"polling",
|
||||
"scroll",
|
||||
"thiserror 1.0.69",
|
||||
"uds",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minidumper-child"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7c4f23f835dbe67e44ddf884d3802ff549ca5948bf60e9fd70e9a13c96324d1"
|
||||
dependencies = [
|
||||
"crash-handler",
|
||||
"minidumper",
|
||||
"thiserror 1.0.69",
|
||||
"uuid 1.23.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
@@ -4352,6 +4524,18 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"cfg-if",
|
||||
"cfg_aliases 0.1.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.3"
|
||||
@@ -4360,7 +4544,7 @@ checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -4487,6 +4671,17 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
@@ -4857,6 +5052,15 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -5387,6 +5591,12 @@ version = "0.3.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
|
||||
|
||||
[[package]]
|
||||
name = "plain"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "plist"
|
||||
version = "1.9.0"
|
||||
@@ -5582,6 +5792,16 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs-core"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"hex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.11.0"
|
||||
@@ -5725,7 +5945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.2.1",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
@@ -5765,7 +5985,7 @@ version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.2.1",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
@@ -5944,6 +6164,15 @@ dependencies = [
|
||||
"rand_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range-map"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rapidhash"
|
||||
version = "4.4.1"
|
||||
@@ -6079,6 +6308,7 @@ dependencies = [
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
@@ -6270,6 +6500,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@@ -6333,6 +6569,7 @@ version = "0.23.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
@@ -6515,6 +6752,26 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6"
|
||||
dependencies = [
|
||||
"scroll_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll_derive"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sdd"
|
||||
version = "3.0.10"
|
||||
@@ -6597,6 +6854,113 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "989425268ab5c011e06400187eed6c298272f8ef913e49fcadc3fda788b45030"
|
||||
dependencies = [
|
||||
"httpdate",
|
||||
"reqwest 0.12.28",
|
||||
"rustls",
|
||||
"sentry-backtrace",
|
||||
"sentry-contexts",
|
||||
"sentry-core",
|
||||
"sentry-panic",
|
||||
"sentry-tracing",
|
||||
"tokio",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-backtrace"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68e299dd3f7bcf676875eee852c9941e1d08278a743c32ca528e2debf846a653"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"regex",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-contexts"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac0c5d6892cd4c414492fc957477b620026fb3411fca9fa12774831da561c88"
|
||||
dependencies = [
|
||||
"hostname",
|
||||
"libc",
|
||||
"os_info",
|
||||
"rustc_version",
|
||||
"sentry-core",
|
||||
"uname",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-core"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deaa38b94e70820ff3f1f9db3c8b0aef053b667be130f618e615e0ff2492cbcc"
|
||||
dependencies = [
|
||||
"rand 0.9.4",
|
||||
"sentry-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-panic"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b7a23b13c004873de3ce7db86eb0f59fe4adfc655a31f7bbc17fd10bacc9bfe"
|
||||
dependencies = [
|
||||
"sentry-backtrace",
|
||||
"sentry-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-rust-minidump"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63964525bf74b16233dbcfb307e11485ebd8ff8f87f6ae212b07ca7937cd2db1"
|
||||
dependencies = [
|
||||
"minidumper-child",
|
||||
"sentry",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-tracing"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fac841c7050aa73fc2bec8f7d8e9cb1159af0b3095757b99820823f3e54e5080"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"sentry-backtrace",
|
||||
"sentry-core",
|
||||
"tracing-core",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sentry-types"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e477f4d4db08ddb4ab553717a8d3a511bc9e81dde0c808c680feacbb8105c412"
|
||||
dependencies = [
|
||||
"debugid",
|
||||
"hex",
|
||||
"rand 0.9.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"url",
|
||||
"uuid 1.23.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -6989,6 +7353,17 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "smart-default"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.6.4"
|
||||
@@ -7994,6 +8369,22 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-sentry"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7432b519b6d2d027082a940783c61ba9b684b38d8df7558cd5f924d87009b295"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"schemars 0.8.22",
|
||||
"sentry",
|
||||
"sentry-rust-minidump",
|
||||
"serde",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-sharekit"
|
||||
version = "0.3.1"
|
||||
@@ -8896,7 +9287,7 @@ dependencies = [
|
||||
"branches",
|
||||
"bumpalo",
|
||||
"bytemuck",
|
||||
"cfg_aliases",
|
||||
"cfg_aliases 0.2.1",
|
||||
"cfg_block",
|
||||
"chrono",
|
||||
"crc32c",
|
||||
@@ -9102,6 +9493,15 @@ version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
||||
|
||||
[[package]]
|
||||
name = "uds"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uds_windows"
|
||||
version = "1.2.1"
|
||||
@@ -9113,6 +9513,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
@@ -9219,6 +9628,34 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"ureq-proto",
|
||||
"utf8-zero",
|
||||
"webpki-roots 1.0.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -9262,6 +9699,12 @@ version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-zero"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
|
||||
@@ -99,6 +99,20 @@ image = { version = "0.25", default-features = false, features = ["jpeg", "png",
|
||||
# WebView). Pure-Rust crate, ships to every Tauri target.
|
||||
mobi = "0.8"
|
||||
|
||||
# Crash/error reporting. `tauri-plugin-sentry` injects @sentry/browser into
|
||||
# every webview and routes browser + Rust panic events through one client; it
|
||||
# re-exports sentry-rust-minidump for desktop native crashes. `rustls` avoids
|
||||
# the native-tls/OpenSSL system dependency so the transport builds for
|
||||
# Android/iOS. Native mobile crashes are handled by sentry-android/-cocoa.
|
||||
sentry = { version = "0.42", default-features = false, features = [
|
||||
"backtrace",
|
||||
"contexts",
|
||||
"panic",
|
||||
"reqwest",
|
||||
"rustls",
|
||||
] }
|
||||
tauri-plugin-sentry = "0.5"
|
||||
|
||||
[target."cfg(target_os = \"macos\")".dependencies]
|
||||
rand = "0.8"
|
||||
cocoa = "0.25"
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use std::{env, fs, path::PathBuf, process::Command};
|
||||
use std::{
|
||||
env, fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../extensions/windows-thumbnail/src");
|
||||
@@ -7,9 +11,60 @@ fn main() {
|
||||
build_windows_thumbnail();
|
||||
}
|
||||
|
||||
propagate_sentry_dsn();
|
||||
|
||||
tauri_build::build()
|
||||
}
|
||||
|
||||
/// Bake the Sentry DSN into the crate at build time via `cargo:rustc-env`, so
|
||||
/// `option_env!("SENTRY_DSN")` (and, on iOS, the `readest_sentry_dsn` FFI) sees
|
||||
/// it. Precedence: an existing `SENTRY_DSN` in the environment (CI secret / shell
|
||||
/// export) wins; otherwise fall back to the gitignored `.env.local`, then `.env`,
|
||||
/// at the app root. Absent everywhere => unset, so reporting stays disabled for
|
||||
/// local and fork builds. `rerun-if-*` makes cargo recompile when the value or
|
||||
/// the dotenv files change (avoiding a stale baked-in value).
|
||||
fn propagate_sentry_dsn() {
|
||||
println!("cargo:rerun-if-env-changed=SENTRY_DSN");
|
||||
let app_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("..");
|
||||
let env_local = app_dir.join(".env.local");
|
||||
let env_file = app_dir.join(".env");
|
||||
println!("cargo:rerun-if-changed={}", env_local.display());
|
||||
println!("cargo:rerun-if-changed={}", env_file.display());
|
||||
|
||||
let dsn = env::var("SENTRY_DSN")
|
||||
.ok()
|
||||
.filter(|v| !v.is_empty())
|
||||
.or_else(|| read_env_value(&env_local, "SENTRY_DSN"))
|
||||
.or_else(|| read_env_value(&env_file, "SENTRY_DSN"));
|
||||
|
||||
if let Some(dsn) = dsn {
|
||||
println!("cargo:rustc-env=SENTRY_DSN={dsn}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a single `KEY=value` from a dotenv-style file, skipping blank lines and
|
||||
/// `#` comments and stripping surrounding quotes. `None` if the file/key is
|
||||
/// absent or the value is empty.
|
||||
fn read_env_value(path: &Path, key: &str) -> Option<String> {
|
||||
let contents = fs::read_to_string(path).ok()?;
|
||||
for line in contents.lines() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if let Some(value) = line
|
||||
.strip_prefix(key)
|
||||
.and_then(|rest| rest.trim_start().strip_prefix('='))
|
||||
{
|
||||
let value = value.trim().trim_matches(|c| c == '"' || c == '\'');
|
||||
if !value.is_empty() {
|
||||
return Some(value.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn build_windows_thumbnail() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let dll_crate_dir = manifest_dir
|
||||
|
||||
@@ -188,6 +188,7 @@
|
||||
"device-info:default",
|
||||
"turso:default",
|
||||
"native-tts:default",
|
||||
"native-bridge:default"
|
||||
"native-bridge:default",
|
||||
"sentry:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,6 +24,20 @@ android {
|
||||
}
|
||||
defaultConfig {
|
||||
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
||||
// Sentry DSN precedence: environment (CI secret / shell export) wins,
|
||||
// else the gitignored .env.local, else .env at the app root (../../../
|
||||
// from this module). Empty => Sentry auto-init no-ops.
|
||||
manifestPlaceholders["sentryDsn"] = System.getenv("SENTRY_DSN")?.takeIf { it.isNotBlank() }
|
||||
?: listOf("../../../.env.local", "../../../.env")
|
||||
.map { rootProject.file(it) }
|
||||
.filter { it.exists() }
|
||||
.firstNotNullOfOrNull { f ->
|
||||
f.readLines()
|
||||
.map { it.trim() }
|
||||
.firstOrNull { it.startsWith("SENTRY_DSN=") }
|
||||
?.substringAfter("=")?.trim()?.trim('"', '\'')?.takeIf { it.isNotEmpty() }
|
||||
}
|
||||
?: ""
|
||||
applicationId = "com.bilingify.readest"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
@@ -88,6 +102,15 @@ dependencies {
|
||||
implementation("androidx.activity:activity-ktx:1.10.1")
|
||||
implementation("com.google.android.material:material:1.12.0")
|
||||
implementation("androidx.lifecycle:lifecycle-process:2.10.0")
|
||||
implementation("io.sentry:sentry-android:8.47.0") {
|
||||
// androidx.lifecycle:lifecycle-common-java8 was merged into
|
||||
// lifecycle-common and is no longer published at 2.9.0+. The project
|
||||
// pins androidx.lifecycle to 2.10.0 (lifecycle-process above), which
|
||||
// version-aligns this Sentry transitive to a nonexistent 2.10.0 and
|
||||
// breaks dependency resolution. The Java8 lifecycle APIs Sentry uses now
|
||||
// live in lifecycle-common, pulled in transitively via lifecycle-process.
|
||||
exclude(group = "androidx.lifecycle", module = "lifecycle-common-java8")
|
||||
}
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.4")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
|
||||
<!-- Sentry auto-init (ContentProvider). Empty DSN => disabled. Release
|
||||
is auto-detected from versionName; performance and PII stay at
|
||||
their safe off/false defaults. -->
|
||||
<meta-data android:name="io.sentry.dsn" android:value="${sentryDsn}" />
|
||||
<meta-data android:name="io.sentry.environment" android:value="production" />
|
||||
|
||||
<activity
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@import Sentry;
|
||||
|
||||
// Provided by the Rust static library (see src-tauri/src/sentry_config.rs).
|
||||
extern const char *readest_sentry_dsn(void);
|
||||
|
||||
// Starts sentry-cocoa at class-load time (before main), so native iOS crashes
|
||||
// are captured from launch without editing the generated main.mm / Info.plist.
|
||||
// The DSN comes from the Rust side (single SENTRY_DSN build-time source); a null
|
||||
// or empty DSN leaves the native SDK disabled, so local and fork builds do not
|
||||
// report. Crashes + errors only: no tracing, no PII.
|
||||
@interface ReadestSentryBootstrap : NSObject
|
||||
@end
|
||||
|
||||
@implementation ReadestSentryBootstrap
|
||||
|
||||
+ (void)load {
|
||||
const char *dsn = readest_sentry_dsn();
|
||||
if (dsn == NULL || dsn[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
NSString *dsnString = [NSString stringWithUTF8String:dsn];
|
||||
[SentrySDK startWithConfigureOptions:^(SentryOptions *options) {
|
||||
options.dsn = dsnString;
|
||||
options.environment = @"production";
|
||||
options.tracesSampleRate = @0.0;
|
||||
options.sendDefaultPii = NO;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -4,6 +4,10 @@ options:
|
||||
deploymentTarget:
|
||||
iOS: 15.0
|
||||
fileGroups: [../../src]
|
||||
packages:
|
||||
Sentry:
|
||||
url: https://github.com/getsentry/sentry-cocoa
|
||||
from: 8.0.0
|
||||
configs:
|
||||
debug: debug
|
||||
release: release
|
||||
@@ -45,6 +49,7 @@ targets:
|
||||
# than ship a malformed one. IAP works without it.
|
||||
sources:
|
||||
- path: Sources
|
||||
- path: SentrySupport
|
||||
- path: Assets.xcassets
|
||||
# Only reference Externals as a file group (no compile/resource
|
||||
# phases) — the actual libapp.a lookup happens via the
|
||||
@@ -92,6 +97,8 @@ targets:
|
||||
EXCLUDED_ARCHS[sdk=iphoneos*]: x86_64
|
||||
groups: [app]
|
||||
dependencies:
|
||||
- package: Sentry
|
||||
product: Sentry
|
||||
- framework: libapp.a
|
||||
embed: false
|
||||
- target: ShareExtension
|
||||
|
||||
@@ -33,6 +33,7 @@ mod mobi_parser;
|
||||
mod nightly_update;
|
||||
mod parser_common;
|
||||
mod range_file;
|
||||
mod sentry_config;
|
||||
#[cfg(desktop)]
|
||||
mod spawn_fresh_browser;
|
||||
mod transfer_file;
|
||||
@@ -302,6 +303,30 @@ struct SingleInstancePayload {
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
// Initialize Sentry as early as possible so panics during startup are
|
||||
// captured. `None` DSN (unset SENTRY_DSN) => disabled, so local and fork
|
||||
// builds don't report. Desktop also starts the out-of-process minidump
|
||||
// handler for native crashes; on mobile, native crashes belong to the
|
||||
// sentry-android / sentry-cocoa SDKs. The guard must outlive the app, so it
|
||||
// is held until `run()` returns (after the blocking `.run(...)` call).
|
||||
let sentry_guard = sentry_config::sentry_dsn().map(|dsn| {
|
||||
sentry::init((
|
||||
dsn,
|
||||
sentry::ClientOptions {
|
||||
release: sentry::release_name!(),
|
||||
environment: Some(sentry_config::sentry_environment().into()),
|
||||
traces_sample_rate: 0.0,
|
||||
send_default_pii: false,
|
||||
..Default::default()
|
||||
},
|
||||
))
|
||||
});
|
||||
|
||||
#[cfg(not(any(target_os = "ios", target_os = "android")))]
|
||||
let _minidump_guard = sentry_guard
|
||||
.as_ref()
|
||||
.map(|guard| tauri_plugin_sentry::minidump::init(guard));
|
||||
|
||||
let builder = tauri::Builder::default()
|
||||
.plugin(
|
||||
tauri_plugin_log::Builder::new()
|
||||
@@ -417,6 +442,11 @@ pub fn run() {
|
||||
#[cfg(feature = "webdriver")]
|
||||
let builder = builder.plugin(tauri_plugin_webdriver::init());
|
||||
|
||||
let builder = match sentry_guard.as_ref() {
|
||||
Some(client) => builder.plugin(tauri_plugin_sentry::init(client)),
|
||||
None => builder,
|
||||
};
|
||||
|
||||
builder
|
||||
.setup(|#[allow(unused_variables)] app| {
|
||||
// When running with the webdriver feature (E2E/integration tests),
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
//! Pure helpers for configuring Sentry. Kept dependency-free and unit-tested;
|
||||
//! the actual `sentry::init` call and plugin registration live in `lib.rs`.
|
||||
|
||||
/// Normalizes a raw `SENTRY_DSN` value. Unset, empty, or whitespace-only maps to
|
||||
/// `None` so local and fork builds (no DSN) never report.
|
||||
pub fn dsn_from_env(raw: Option<&str>) -> Option<&str> {
|
||||
match raw {
|
||||
Some(dsn) if !dsn.trim().is_empty() => Some(dsn.trim()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compile-time Sentry DSN from the `SENTRY_DSN` env var (baked at build time).
|
||||
pub fn sentry_dsn() -> Option<&'static str> {
|
||||
dsn_from_env(option_env!("SENTRY_DSN"))
|
||||
}
|
||||
|
||||
/// Sentry `environment` tag derived from the crate version. Nightly builds use a
|
||||
/// single 10-digit `YYYYMMDDHH` prerelease (e.g. `0.11.17-2026070301`);
|
||||
/// everything else is treated as production.
|
||||
pub fn environment_for_version(version: &str) -> &'static str {
|
||||
if let Some((_, pre)) = version.split_once('-') {
|
||||
if pre.len() == 10 && pre.bytes().all(|b| b.is_ascii_digit()) {
|
||||
return "nightly";
|
||||
}
|
||||
}
|
||||
"production"
|
||||
}
|
||||
|
||||
/// Sentry `environment` for the current build.
|
||||
pub fn sentry_environment() -> &'static str {
|
||||
environment_for_version(env!("CARGO_PKG_VERSION"))
|
||||
}
|
||||
|
||||
/// C-ABI accessor for the compile-time Sentry DSN, used by the iOS native
|
||||
/// bootstrap (sentry-cocoa) so it starts with the same DSN as the Rust client
|
||||
/// without a second env read or fragile Info.plist / preprocessor plumbing.
|
||||
/// Returns a NUL-terminated pointer valid for the process lifetime, or null when
|
||||
/// no DSN is configured (empty `SENTRY_DSN` => the native SDK stays disabled).
|
||||
#[cfg(target_os = "ios")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn readest_sentry_dsn() -> *const std::os::raw::c_char {
|
||||
use std::ffi::CString;
|
||||
use std::sync::OnceLock;
|
||||
static DSN: OnceLock<Option<CString>> = OnceLock::new();
|
||||
match DSN.get_or_init(|| sentry_dsn().and_then(|d| CString::new(d).ok())) {
|
||||
Some(cstr) => cstr.as_ptr(),
|
||||
None => std::ptr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{dsn_from_env, environment_for_version};
|
||||
|
||||
#[test]
|
||||
fn dsn_is_none_when_unset_or_blank() {
|
||||
assert_eq!(dsn_from_env(None), None);
|
||||
assert_eq!(dsn_from_env(Some("")), None);
|
||||
assert_eq!(dsn_from_env(Some(" ")), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dsn_is_trimmed_when_present() {
|
||||
assert_eq!(
|
||||
dsn_from_env(Some(" https://k@o.ingest.sentry.io/1 ")),
|
||||
Some("https://k@o.ingest.sentry.io/1")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn environment_is_nightly_for_ten_digit_prerelease() {
|
||||
assert_eq!(environment_for_version("0.11.17-2026070301"), "nightly");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn environment_is_production_otherwise() {
|
||||
assert_eq!(environment_for_version("0.11.17"), "production");
|
||||
assert_eq!(environment_for_version("0.11.17-rc.1"), "production");
|
||||
assert_eq!(environment_for_version("1.0.0-2026"), "production");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user