11 KiB
Sentry Release Tracking & Source Maps
Tracks issue #405.
OpenHuman reports crashes and errors from three surfaces, each with its own Sentry project but all sharing the same release tag so events line up:
- Frontend —
@sentry/reactinapp/src/services/analytics.ts→openhuman-react. - Rust core (CLI / Docker) —
sentry::initinsrc/main.rs→openhuman-core. - Tauri shell (desktop) —
sentry::initinapp/src-tauri/src/lib.rs::run()→openhuman-tauri. The core is linked into this binary as a path dep, so a singlecargo tauri buildproduces all the Rust DIFs uploaded for both projects.
Canonical release identifier
Both surfaces report the same release tag:
openhuman@<semver>+<short_git_sha>
Where:
<semver>ispackageJson.version/env!("CARGO_PKG_VERSION").<short_git_sha>is the first 12 chars of the commit that produced the build. When the SHA is absent (local dev), the tag collapses toopenhuman@<semver>with no+suffix.
The frontend computes this in app/src/utils/config.ts::SENTRY_RELEASE
from VITE_BUILD_SHA. The core does the same in
src/main.rs::build_release_tag() from option_env!("OPENHUMAN_BUILD_SHA").
Environments
Reported as the Sentry environment tag:
| Value | When |
|---|---|
development |
Local pnpm tauri dev / debug builds |
staging |
VITE_OPENHUMAN_APP_ENV=staging or OPENHUMAN_APP_ENV=staging |
production |
Release builds from workflow_dispatch with build_target=production |
Fallback precedence for the core:
OPENHUMAN_APP_ENVenv var at runtime (override).- Compile-time
debug_assertions→development. - Otherwise →
production.
Source-map upload
The frontend emits source maps (vite.config.ts sets build.sourcemap = true). When SENTRY_AUTH_TOKEN is present at build time
@sentry/vite-plugin:
- Uploads every
dist/**/*.jsand its.mapsibling. - Tags the upload with the canonical release name above.
- Deletes the on-disk
.mapfiles after upload so users never receive them in the shipped bundle.
If SENTRY_AUTH_TOKEN is empty (local dev, smoke CI, forks without
secrets), the plugin registers as a no-op — the build still produces source
maps on disk but nothing is uploaded. This keeps the local dev loop zero-
config.
Rust debug symbols + source context
scripts/upload_sentry_symbols.sh runs after the Tauri build in
release.yml and pushes:
- Debug info files (
.dwp/.debug/.pdb/ macOS.dSYM) found underapp/src-tauri/target/<triple>/release/deps. The[profile.release] debug = "line-tables-only"setting in bothCargo.tomlfiles emits just enough DWARF (file+line tables, no full type info) for Sentry to symbolicate frame addresses without bloating the shipped binary.split-debuginfo = "packed"writes the debug data into a separate.dSYMbundle on macOS. - A
.src.zipsource bundle built from the Rust source files referenced by those DIFs (sentry-cli upload-dif --include-sources). This is what lets Sentry render the surrounding lines of source for a panic, not justfunction_name + 0xNNN. Without it, the event detail page shows a symbolicated stack with empty source context.
For Sentry to actually walk the loaded shared libraries at runtime and
attach each image's debug-id to events, the sentry crate is built with
the debug-images feature in both Cargo.toml files. This registers
DebugImagesIntegration as part of the default integration set — events
arrive with debug_meta.images populated, and Sentry's symbolicator
resolves those debug-ids against uploaded DIFs to attach pre_context /
context_line / post_context to each frame.
The script drives the per-project release lifecycle for the project it's called against:
sentry-cli releases new "$SENTRY_RELEASE"— creates / no-ops the release.sentry-cli releases set-commits --auto --ignore-missing— associates commits using the GitHub-provided range.--ignore-missingkeeps shallow CI checkouts from failing.sentry-cli upload-dif --include-sources— DIFs +.src.zip.sentry-cli releases finalize "$SENTRY_RELEASE"— marks the release complete (used by Sentry to compute "regression" / "new in release").
releases new, set-commits, and finalize are idempotent — re-running
on the same SHA reuses the existing release and DIFs are deduplicated by
debug-ID. The deploy marker is not in this script — it lives in
release.yml's "Record Sentry deploy marker" step, which fires once per
matrix target after the upload step. sentry-cli releases deploys ... new
does not deduplicate by (release, env), so re-running CI for the same
release intentionally adds another deploy row representing a separate
deploy attempt.
CI configuration
release.yml + release-packages.yml thread the following through to the
build steps. Any subset can be set on a per-environment basis in the
Production / Staging GitHub Actions environment:
Required for upload to work
| Name | Type | Scope | Purpose |
|---|---|---|---|
secrets.SENTRY_AUTH_TOKEN |
secret | build-desktop | Auth for @sentry/vite-plugin + sentry-cli |
vars.SENTRY_ORG |
variable | build-desktop | Sentry org slug |
vars.SENTRY_PROJECT_REACT |
variable | build-desktop (Vite) | Project slug for the frontend bundle + source maps |
vars.SENTRY_PROJECT_CORE |
variable | symbols-upload | Project slug for the Rust DIFs + source bundle |
vars.SENTRY_PROJECT_TAURI |
variable | (reserved) | Reserved for the Tauri shell when symbol-uploads split |
vars.OPENHUMAN_REACT_SENTRY_DSN |
variable | build-desktop (Vite) | Frontend DSN (baked by Vite define) |
vars.OPENHUMAN_CORE_SENTRY_DSN |
variable | build-desktop (Rust) | Core sidecar DSN (baked via option_env!) |
vars.OPENHUMAN_TAURI_SENTRY_DSN |
variable | build-desktop (Tauri) | Tauri shell DSN (baked via option_env!) |
The legacy vars.OPENHUMAN_SENTRY_DSN, vars.VITE_SENTRY_DSN,
vars.SENTRY_PROJECT, and vars.SENTRY_PROJECT_FRONTEND are no longer
read by release.yml — they were superseded by the per-surface variables
above as part of #1032. Safe to delete from any configured GitHub Actions
environment that still has them set.
Provided automatically
| Name | Source |
|---|---|
VITE_BUILD_SHA |
needs.prepare-build.outputs.sha (tag commit, full 40 chars) |
OPENHUMAN_BUILD_SHA |
Same — passed to cargo build for the Rust core / Tauri shell |
SENTRY_RELEASE |
openhuman@<version>+<short_sha> — short_sha is sha[:12], matches the truncation config.ts / vite.config.ts / main.rs / app/src-tauri/src/lib.rs apply at runtime. Same value on Vite, symbols upload, and the deploy-marker steps |
SENTRY_ENVIRONMENT |
staging / production from the workflow's build_target — only consumed by the deploy-marker step |
Personal Sentry DSN (local)
Drop the DSN into your repo-local .env:
# .env
OPENHUMAN_SENTRY_DSN=https://<key>@o<org>.ingest.sentry.io/<project>
src/main.rs now loads .env before sentry::init, so the runtime
env var is visible to the client at startup without needing a manual
source scripts/load-dotenv.sh.
For the frontend, put VITE_SENTRY_DSN in app/.env.local.
Verification runbook
- Event arrives. Trigger a test event from the core CLI:
The command prints an event UUID on success; search it in the Sentry dashboard.
./target/release/openhuman-core sentry-test # or on an installed release (Windows): # "%LOCALAPPDATA%\Programs\OpenHuman\OpenHuman.exe" core sentry-test # or (macOS): # /Applications/OpenHuman.app/Contents/MacOS/openhuman-core-* sentry-test - Release tag is right. On the event detail page, the
Releasefield should readopenhuman@<version>+<short_sha>(matching the tag that cut the release). - Environment tag is right. Production CI dispatch →
production. Staging dispatch →staging. Localpnpm tauri dev→development. - Stack traces are symbolicated. Force a frontend error from the
installed app; the event's stack trace should show original
TypeScript file names and line numbers (not hashed
assets/index-*.js). - CI failure is loud when misconfigured. If
SENTRY_AUTH_TOKENis missing and the release is supposed to upload source maps, the CI run will warn in the Vite build log rather than silently producing an un-symbolicated release.
Troubleshooting
- Events arrive without a release tag — check the Vite build log for
SENTRY_RELEASE; if empty, the CI workflow didn't pass it through. - Events arrive without symbolication — open the release in Sentry →
"Source Maps" tab. Missing artifacts mean either
SENTRY_AUTH_TOKENwas empty, or the plugin ran but theassets:glob didn't match (inspect the upload summary printed duringpnpm build). - Frontend and core show different releases — verify
needs.prepare-build.outputs.shais identical between the core build step (OPENHUMAN_BUILD_SHA) and the frontend build step (VITE_BUILD_SHA/SENTRY_RELEASE). - No events from a release build, only from local —
vars.*probably isn't defined on theProductionenvironment. Set it and re-cut the release. - Rust frames show function name but no source — the
.src.zipfor this release didn't upload, OR thedebug-imagesintegration isn't active. Check the "Upload core sidecar debug symbols to Sentry" workflow log forBundled N source files; absence means--include-sourcesdidn't take effect or DWARF wasn't emitted (verify the[profile.release] debug = "line-tables-only"block inCargo.toml). If the bundle uploaded but events still render blank, confirm thesentrycrate has thedebug-imagesfeature enabled in bothCargo.tomlfiles. - DIFs uploaded but events still report a release with no artifacts
— verify
SENTRY_RELEASEwas set toopenhuman@<version>+<short_sha>in all three places that construct it (Vite build step, symbols-upload step, deploy-marker step). All three must referenceneeds.prepare-build.outputs.short_sha, not the fullsha. - No deploy marker on the release page — confirm the dedicated
"Record Sentry deploy marker" step ran and
SENTRY_ENVIRONMENTresolved to a non-empty value (release.ymlderives it frominputs.build_target).