fix(sentry): restore OS context + guard source-map upload (#1403) (#1405)

This commit is contained in:
Steven Enamakel
2026-05-09 13:06:37 -07:00
committed by GitHub
parent 90e9f48f3a
commit 3f86bcd69d
5 changed files with 248 additions and 26 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env node
// Post-build guard for #1403: verify that @sentry/vite-plugin actually
// uploaded source maps and injected debug-IDs into the production bundle.
//
// Failure modes this catches:
// - SENTRY_AUTH_TOKEN missing at build time -> plugin returned null and
// nothing was uploaded (bundle has no debug-ID comments).
// - sourcemap.assets glob mismatched cwd -> plugin logged "Didn't find
// any matching sources for debug ID upload" and exited 0; bundle has
// no debug-IDs and Sentry can't symbolicate.
//
// Run after `cargo tauri build` (which invokes Vite). Exits non-zero if
// fewer than `MIN_DEBUG_IDS` JS chunks under app/dist/assets/ contain a
// `// debugId=...` comment, or none of them reference `_sentryDebugIds`.
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, resolve } from 'node:path';
const ROOT = resolve(new URL('..', import.meta.url).pathname, '..');
const ASSETS = join(ROOT, 'app', 'dist', 'assets');
const MIN_DEBUG_IDS = 1;
// `// debugId=<uuid>` is the marker @sentry/vite-plugin appends to every
// chunk it processes. The plugin also writes `globalThis._sentryDebugIds`
// at startup so the runtime can match captured stacks to uploaded maps.
const DEBUG_ID_RE = /\/\/[ #]?\s*debugId=[0-9a-f-]{36}/i;
const RUNTIME_MAP_RE = /_sentryDebugIds/;
function listJsFiles(dir) {
let out = [];
for (const entry of readdirSync(dir)) {
const p = join(dir, entry);
const st = statSync(p);
if (st.isDirectory()) out = out.concat(listJsFiles(p));
else if (entry.endsWith('.js')) out.push(p);
}
return out;
}
function main() {
let files;
try {
files = listJsFiles(ASSETS);
} catch (err) {
console.error(`[verify-sentry-sourcemaps] ${ASSETS} not found — did Vite build succeed?`);
console.error(err.message);
process.exit(2);
}
if (files.length === 0) {
console.error(`[verify-sentry-sourcemaps] no .js files under ${ASSETS}`);
process.exit(2);
}
let withDebugId = 0;
let withRuntimeMap = 0;
for (const f of files) {
const src = readFileSync(f, 'utf8');
if (DEBUG_ID_RE.test(src)) withDebugId += 1;
if (RUNTIME_MAP_RE.test(src)) withRuntimeMap += 1;
}
console.log(
`[verify-sentry-sourcemaps] scanned ${files.length} files; ${withDebugId} carry debug-IDs; ${withRuntimeMap} reference _sentryDebugIds.`
);
if (withDebugId < MIN_DEBUG_IDS || withRuntimeMap < 1) {
console.error(
'[verify-sentry-sourcemaps] FAIL — Sentry source-map upload did not run or did not inject debug-IDs.\n' +
' Likely causes:\n' +
' - SENTRY_AUTH_TOKEN missing/empty at vite build time\n' +
' - sourcemap.assets glob did not match dist/assets\n' +
' - SENTRY_RELEASE / VITE_BUILD_SHA mismatch between upload and runtime\n' +
' Without debug-IDs, production stack traces cannot be symbolicated. (#1403)'
);
process.exit(1);
}
}
main();
+45 -11
View File
@@ -234,17 +234,51 @@ upload_symbols() {
"--log-level=warn"
)
# Find and upload all debug symbol files
if [[ -d "${symbols_path}" ]]; then
# Upload .dwp (dwarf packages), .debug files, and .pdb files
# Debug symbols are indexed by debug-ID, not release-scoped
log_info "Scanning for debug symbols in ${symbols_path}..."
sentry-cli "${upload_args[@]}" "${symbols_path}" || {
log_warn "Some debug symbols may have failed to upload"
}
else
log_warn "Symbols path does not exist: ${symbols_path}"
log_info "Looking for any release artifacts..."
# Find and upload all debug symbol files. The output is captured so the
# script can verify *something* was actually uploaded — sentry-cli exits
# 0 even when it found zero DIFs, which silently breaks symbolication
# for the Tauri shell and standalone core CLI exactly the way #1403
# caught for the frontend. Fail loudly here so CI catches it on the
# build that produced the empty target dir, not weeks later when an
# event arrives unsymbolicated.
if [[ ! -d "${symbols_path}" ]]; then
log_error "Symbols path does not exist: ${symbols_path}"
log_error "Expected Cargo target dir with build artifacts. Did the build step complete?"
exit 1
fi
log_info "Scanning for debug symbols in ${symbols_path}..."
local upload_log
upload_log="$(mktemp)"
if ! sentry-cli "${upload_args[@]}" "${symbols_path}" 2>&1 | tee "${upload_log}"; then
log_error "sentry-cli upload-dif exited non-zero"
rm -f "${upload_log}"
exit 1
fi
# sentry-cli prints "Found N debug information files" when scanning, and
# "Uploaded N missing debug information files" / "No new debug
# information files to upload" after the upload phase. We accept either
# "Found > 0" or "Uploaded > 0" — the second covers re-runs where the
# DIFs are already on Sentry's side. Empty input dirs print neither and
# fall through to the failure branch.
local found=0 uploaded=0 already=0
if grep -qE 'Found [1-9][0-9]* debug information' "${upload_log}"; then
found=1
fi
if grep -qE 'Uploaded [1-9][0-9]* missing debug information' "${upload_log}"; then
uploaded=1
fi
if grep -qE 'No new debug information files to upload|already exist' "${upload_log}"; then
already=1
fi
rm -f "${upload_log}"
if [[ "${found}" -eq 0 && "${uploaded}" -eq 0 && "${already}" -eq 0 ]]; then
log_error "sentry-cli upload-dif found zero debug information files in ${symbols_path}"
log_error "Production Sentry events from this build will NOT be symbolicated. (#1403)"
log_error "Likely causes: build profile produced no DWARF/PDB/dSYM, wrong target dir, or ${symbols_path} was cleaned before this step."
exit 1
fi
# Finalize the release