Skip to content

fix(ci): inject Sentry debug IDs before upload-artifact#21

Closed
FedeZara wants to merge 1 commit into
mainfrom
FedeZara/inject-sentry-debug-ids
Closed

fix(ci): inject Sentry debug IDs before upload-artifact#21
FedeZara wants to merge 1 commit into
mainfrom
FedeZara/inject-sentry-debug-ids

Conversation

@FedeZara
Copy link
Copy Markdown
Contributor

@FedeZara FedeZara commented May 20, 2026

Follow-up to PRs #15-#20. Stack frames in Sentry stayed minified despite source-map upload succeeding — fixes the missing piece.

Root cause

getsentry/action-release@v1 runs sentry-cli 2.x's sourcemaps upload, which uploads as an artifact bundle. The Sentry log from the failing release confirms it:

> Uploaded files to Sentry
> Upload type: artifact bundle
> Release: verify-token@v0.0.1-test

Artifact bundles are matched against runtime stack frames via debug IDs — a UUID embedded in both the JS bundle (as //# debugId=…) and the sourcemap ("debugId": …). The legacy filename + release-files fallback only kicks in when sentry-cli detects no debug IDs and falls back to the older upload path. With sentry-cli 2.x defaulting to artifact bundles, that fallback never triggered, and our bundle had no debug ID, so the matcher had nothing to match on.

Verified by inspecting the published bundle on dist/verify-token: //# sourceMappingURL=index.js.map was present, but no debugId pragma; the sourcemap had sourcesContent and 942 source entries, but debugId: <unset>.

Fix

Run sentry-cli sourcemaps inject against the built bundle in the build job, before upload-artifact. Both downstream jobs (sentry-release, publish-dist) download the same artifact, so:

  • sentry-release uploads the injected bundle + sourcemap to Sentry — debug ID baked in both;
  • publish-dist commits the injected JS to dist/<action> — so the bundle consumers actually execute at runtime carries the same debug ID, and the Sentry SDK's stack frames reference it.

Verified locally on a fresh build:

$ sentry-cli sourcemaps inject verify-token/dist
> Found 2 files
> Analyzing 2 sources
> Injecting debug ids
Modified: verify-token/dist/index.js          0e2441e4-9669-58ae-aaa8-bc853561d4c0
Modified: verify-token/dist/index.js.map      0e2441e4-9669-58ae-aaa8-bc853561d4c0

$ tail -1 verify-token/dist/index.js
//# debugId=0e2441e4-9669-58ae-aaa8-bc853561d4c0

The new step also has a sanity check: it greps for the debugId= pragma in the JS and the "debugId": key in the map, and fails the job if either is missing — so a silent regression in tsup/sentry-cli compatibility wouldn't get past the build job.

Why inject in build and not in sentry-release

getsentry/action-release@v1 accepts inject: true and would inject in its own workspace before uploading. But:

  • publish-dist runs in parallel-ish — it downloads its own copy of dist-<action> from upload-artifact (the pre-inject version) and pushes that to dist/<action>.
  • So the consumer at runtime would execute the un-injected bundle, reporting no debug ID, and Sentry would never match — same failure mode as today.

Injecting upstream of upload-artifact is the only place where one inject covers both consumers of the artifact.

inject: false is set explicitly on the sentry-release step now, so it doesn't re-inject and generate fresh UUIDs that wouldn't match the bundle on the dist branch.

Test plan

  • Local: pnpm typecheck clean, actionlint clean
  • Local: sentry-cli sourcemaps inject against a fresh pnpm build of verify-token — confirms inject succeeds, the JS gets a //# debugId=… pragma, and the .map gets a matching "debugId" field with the same UUID
  • Dispatch release.yml for verify-token with v0.0.2-test, prerelease=true. Verify in the run log:
    • the new Inject Sentry debug IDs step emits OK: debug IDs injected into verify-token/dist/{index.js,index.js.map}
    • the Create Sentry release + upload sourcemaps step still reports Upload type: artifact bundle
  • On the published dist branch, confirm the bundle carries a debug ID: git show verify-token/v0.0.2-test:dist/index.js | tail -2 | grep debugId
  • Trigger the federico-automations-tests workflow in failure mode (so the wrapper throws and Sentry receives an event tagged verify-token@v0.0.2-test). In the Sentry UI:
    • Open the new event → Stack trace
    • The frames should resolve to TypeScript source (e.g. getRequiredFernToken in packages/shared/src/index.ts), not to minified dist/index.js
  • Sentry UI → Releases → verify-token@v0.0.2-test → Source Maps tab: should show the artifact bundle with the debug ID

Out of scope

  • The existing verify-token/v0.0.1-test release will keep showing minified frames — it was uploaded without a debug ID. Cut a new prerelease (v0.0.2-test) to validate this PR end-to-end. The old release's frames can't be retroactively deobfuscated unless the artifact bundle is re-uploaded with debug IDs, which isn't worth the effort for a smoke-test release.

Sourcemap deobfuscation wasn't resolving for events emitted from the
released bundle. Root cause: getsentry/action-release@v1 (sentry-cli
2.x) uploads as an *artifact bundle*, which Sentry matches against
runtime stack frames using debug IDs — not filename + release-files
fallback. Our bundle had no debug ID, so the matcher had nothing to
match on.

Fix: run `sentry-cli sourcemaps inject` against the built bundle in
the `build` job *before* upload-artifact. Both downstream jobs
download the same artifact:

  - sentry-release uploads the injected bundle + sourcemap with the
    debug ID embedded in both;
  - publish-dist commits the injected JS to dist/<action> — so the
    code the consumer actually runs at runtime carries the same
    debug ID, and the Sentry SDK reports it in stack frames.

Doing the inject inside sentry-release alone would NOT fix this:
publish-dist downloads its own copy of the (un-injected) artifact,
so runtime bundles would still ship without debug IDs.

Also sets `inject: false` on the sentry-release action explicitly, so
it doesn't re-inject (generating new IDs that wouldn't match what
publish-dist published).

Verified locally: `sentry-cli sourcemaps inject` adds a
`//# debugId=<uuid>` pragma to the JS and a matching `"debugId"`
field to the .map, with the same UUID on both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@FedeZara
Copy link
Copy Markdown
Contributor Author

Folding into #18 to release ASAP. Will rebase #18 onto current main and add the inject change there.

@FedeZara FedeZara closed this May 20, 2026
@FedeZara FedeZara deleted the FedeZara/inject-sentry-debug-ids branch May 20, 2026 16:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant