feat(core): Introduce @sentry/expo-upload-sourcemaps package#6027
feat(core): Introduce @sentry/expo-upload-sourcemaps package#6027
Conversation
Moves the Expo sourcemap upload script into a new scoped workspace package @sentry/expo-upload-sourcemaps and replaces the original scripts/expo-upload-sourcemaps.js in @sentry/react-native with a shim that forwards to it. The existing sentry-expo-upload-sourcemaps bin in @sentry/react-native keeps working unchanged, so projects with the bin referenced in package.json scripts or invoked via npx are not affected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
🤖 This preview updates automatically when you update the PR. |
|
…ckage Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…d-sourcemaps bin Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Drop the callback passed to fs.writeFileSync. writeFileSync is synchronous and silently discards the callback argument; the error branch never fired and the success log never printed. - Preserve a non-zero exit code when sentry-cli is terminated by a signal. spawnSync returns result.status === null in that case, and process.exit(null) coerces to 0, previously reporting success after an aborted upload. Both issues were inherited verbatim from the script's prior location inside @sentry/react-native. Fixed now that the script has moved to its own package. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f4a48df. Configure here.
…arball Yarn 3's built-in shell errors on unmatched globs, so the inlined "rm -f *.tgz && npm pack" fails in CI where no prior tarball exists. Match packages/core's pattern and run through real bash via a script file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ype check Yalc rewrites the workspace:* dep to 8.8.0 when publishing @sentry/react-native, so ts3.8-test's yarn install tries to fetch @sentry/expo-upload-sourcemaps@8.8.0 from the npm registry before the package is published and fails with 404. Publish and yalc-add the new package alongside @sentry/react-native, and pin the transitive dep to the same local file reference via resolutions so yarn doesn't try to resolve it from the registry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
New CLI package lacks test coverage
The new @sentry/expo-upload-sourcemaps package contains significant business logic (expo config parsing, sentry.properties file parsing, asset grouping, upload orchestration) but has no test files. The skill guidelines require appropriate test coverage for new functionality, including functional tests for business logic and integration tests for component interactions. Key functions like getSentryPluginPropertiesFromExpoConfig, getSentryPropertiesFromFile, groupAssets, and the upload loop should have tests.
Identified by Warden code-review
|
Re: #6027 (review) The business logic Warden flags is covered by the existing 19-test suite at The tests are intentionally kept in the consumer's test suite because migrating them here would require introducing a new jest/ts-jest/tsconfig pipeline in this package just for one test file. If standalone coverage becomes important later (e.g. if the new package is used independently of |
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 414.09 ms | 438.47 ms | 24.38 ms |
| 04207c4+dirty | 459.19 ms | 518.54 ms | 59.35 ms |
| 3d377b5+dirty | 406.18 ms | 453.52 ms | 47.34 ms |
| 3ce5254+dirty | 410.57 ms | 448.48 ms | 37.91 ms |
| df5d108+dirty | 527.06 ms | 603.58 ms | 76.52 ms |
| a50b33d+dirty | 500.81 ms | 532.11 ms | 31.30 ms |
| 0d9949d+dirty | 403.57 ms | 437.00 ms | 33.43 ms |
| 7ac3378+dirty | 404.78 ms | 439.84 ms | 35.06 ms |
| 5fe1c6c+dirty | 401.62 ms | 445.28 ms | 43.66 ms |
| 890d145+dirty | 504.54 ms | 491.55 ms | -12.99 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 04207c4+dirty | 43.75 MiB | 48.12 MiB | 4.37 MiB |
| 3d377b5+dirty | 43.75 MiB | 48.14 MiB | 4.39 MiB |
| 3ce5254+dirty | 43.75 MiB | 48.12 MiB | 4.37 MiB |
| df5d108+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| a50b33d+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 0d9949d+dirty | 43.75 MiB | 48.13 MiB | 4.37 MiB |
| 7ac3378+dirty | 43.75 MiB | 48.13 MiB | 4.37 MiB |
| 5fe1c6c+dirty | 43.75 MiB | 48.14 MiB | 4.39 MiB |
| 890d145+dirty | 43.75 MiB | 48.14 MiB | 4.39 MiB |
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 1229.67 ms | 1221.50 ms | -8.17 ms |
| 3d377b5+dirty | 1218.48 ms | 1219.51 ms | 1.03 ms |
| df5d108+dirty | 1225.90 ms | 1220.14 ms | -5.76 ms |
| 3817909+dirty | 1183.90 ms | 1187.50 ms | 3.60 ms |
| 5fe1c6c+dirty | 1220.79 ms | 1217.63 ms | -3.16 ms |
| 3ce5254+dirty | 1219.93 ms | 1221.90 ms | 1.96 ms |
| 04207c4+dirty | 1191.27 ms | 1189.78 ms | -1.48 ms |
| 7ac3378+dirty | 1213.37 ms | 1218.15 ms | 4.78 ms |
| 890d145+dirty | 1223.59 ms | 1231.37 ms | 7.78 ms |
| 5c1e987+dirty | 1204.30 ms | 1222.15 ms | 17.85 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 3.38 MiB | 4.74 MiB | 1.35 MiB |
| 3d377b5+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| df5d108+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 3817909+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 5fe1c6c+dirty | 3.38 MiB | 4.77 MiB | 1.39 MiB |
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 04207c4+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 7ac3378+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 890d145+dirty | 3.38 MiB | 4.77 MiB | 1.38 MiB |
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
📲 Install BuildsAndroid
|
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 1223.33 ms | 1224.38 ms | 1.04 ms |
| 3d377b5+dirty | 1201.55 ms | 1201.80 ms | 0.25 ms |
| df5d108+dirty | 1207.34 ms | 1210.50 ms | 3.16 ms |
| 3817909+dirty | 1210.76 ms | 1215.64 ms | 4.89 ms |
| 5fe1c6c+dirty | 1201.36 ms | 1209.15 ms | 7.78 ms |
| 3ce5254+dirty | 1217.70 ms | 1224.69 ms | 6.99 ms |
| 04207c4+dirty | 1228.55 ms | 1226.04 ms | -2.51 ms |
| 7ac3378+dirty | 1202.35 ms | 1198.31 ms | -4.04 ms |
| 890d145+dirty | 1212.98 ms | 1220.10 ms | 7.12 ms |
| 5c1e987+dirty | 1208.43 ms | 1220.72 ms | 12.29 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 3.38 MiB | 4.74 MiB | 1.35 MiB |
| 3d377b5+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| df5d108+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 3817909+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 5fe1c6c+dirty | 3.38 MiB | 4.77 MiB | 1.39 MiB |
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 04207c4+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 7ac3378+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 890d145+dirty | 3.38 MiB | 4.77 MiB | 1.38 MiB |
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
…etup The e2e CLI (dev-packages/e2e-tests/cli.mjs) yalc-publishes @sentry/react-native and yalc-adds it into each matrix RN app. Yalc rewrites the workspace:* dep to a concrete 8.8.0, so yarn install in the sample app tries to fetch @sentry/expo-upload-sourcemaps@8.8.0 from the npm registry and 404s. Mirror the fix applied to the TypeScript 3.8 type check: also yalc-publish the new package, yalc-add it in the sample, and pin the transitive dep to the file reference via resolutions so yarn does not touch the registry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 435.20 ms | 459.48 ms | 24.28 ms |
| 04207c4+dirty | 395.40 ms | 456.55 ms | 61.15 ms |
| 3d377b5+dirty | 425.38 ms | 440.67 ms | 15.30 ms |
| 3ce5254+dirty | 373.90 ms | 427.84 ms | 53.94 ms |
| df5d108+dirty | 434.82 ms | 447.39 ms | 12.57 ms |
| a50b33d+dirty | 353.21 ms | 398.48 ms | 45.27 ms |
| 0d9949d+dirty | 414.88 ms | 428.68 ms | 13.81 ms |
| 7ac3378+dirty | 410.67 ms | 442.60 ms | 31.92 ms |
| 5fe1c6c+dirty | 365.84 ms | 408.62 ms | 42.78 ms |
| 890d145+dirty | 486.42 ms | 514.85 ms | 28.43 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 2c735cc+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 04207c4+dirty | 43.94 MiB | 48.98 MiB | 5.04 MiB |
| 3d377b5+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
| 3ce5254+dirty | 43.94 MiB | 48.98 MiB | 5.04 MiB |
| df5d108+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| a50b33d+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 0d9949d+dirty | 43.94 MiB | 48.99 MiB | 5.05 MiB |
| 7ac3378+dirty | 43.94 MiB | 48.99 MiB | 5.05 MiB |
| 5fe1c6c+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
| 890d145+dirty | 43.94 MiB | 49.00 MiB | 5.06 MiB |
📢 Type of change
📜 Description
Adds a new scoped workspace package
@sentry/expo-upload-sourcemapscontaining the CLI that uploads JavaScript bundles and source maps from Expo builds to Sentry. The script previously lived atpackages/core/scripts/expo-upload-sourcemaps.jsinside@sentry/react-native; it is moved verbatim topackages/expo-upload-sourcemaps/cli.js, and the original path is replaced by a tiny shim that forwards to the new package.After this lands, users can invoke the CLI two ways, both producing identical behavior:
Changes at a glance:
packages/expo-upload-sourcemaps/— new workspace:package.json,cli.js(moved),README.md,LICENSE.md.packages/core/scripts/expo-upload-sourcemaps.js— now a shim:require.resolveguard with a friendlyMODULE_NOT_FOUNDmessage, thenrequire('@sentry/expo-upload-sourcemaps/cli.js').packages/core/package.json— adds"@sentry/expo-upload-sourcemaps": "workspace:*"todependencies. Bin surface is unchanged;sentry-expo-upload-sourcemapsis retained.package.json— registers the new workspace..craft.yml— adds a secondregistryentry so the new tarball lands in the Sentry release registry.yarn.lock— 19-line refresh (workspace entry + reference).💡 Motivation and Context
The current docs at docs.sentry.io/platforms/react-native/sourcemaps/uploading/expo/ instruct users to run
npx sentry-expo-upload-sourcemaps dist. That unscoped name resolves against the public npm registry when no matching local bin is found, and it is currently held by a third-party account (see https://www.npmjs.com/package/sentry-expo-upload-sourcemaps). The payload is a benign forwarder today, but it remains a third-party entry point on the documented happy path.The first mitigation already shipped in sentry-docs (getsentry/sentry-docs#17391), routing the command through
npx --package=@sentry/react-native sentry-expo-upload-sourcemaps dist. This PR is the long-term fix: publishing a Sentry-owned scoped package whose name (@sentry/*) cannot be squatted and whose invocation form is shorter and more idiomatic.A follow-up docs PR will switch the recommended command to
npx @sentry/expo-upload-sourcemaps distonce the new package is published to npm. Until then, the existing scoped command continues to work.💚 How did you test it?
Locally on this branch:
yarn install— workspace linked;packages/core/node_modules/@sentry/expo-upload-sourcemapspoints topackages/expo-upload-sourcemaps.yarn build— 2 projects built without error.yarn lint:lerna— 4 projects passed (0 errors; one pre-existing warning insamples/react-nativeunrelated to this change).yarn circularDepCheck— no cycles.yarn test:packages/coreSDK: 108 suites / 1302 tests passed.packages/coretools: 11 suites / 210 tests passed.samples/react-native: 1/1.packages/core/test/scripts/expo-upload-sourcemaps.test.ts(19 tests covering basic functionality, command-injection safety, Hermes flag handling, env-var validation,sentry.propertiesfallbacks, and_internal.sentryBuildPropertiesfallbacks) all pass through the shim → new package chain.npm pack --dry-runon the new package producessentry-expo-upload-sourcemaps-8.8.0.tgzwithcli.js,package.json,README.md,LICENSE.md— filename matches the.craft.ymlregistry regex../node_modules/.bin/sentry-expo-upload-sourcemapsfromsamples/exposuccessfully routes through the shim to the new package'scli.js, confirmed via the require stack.📝 Checklist
sendDefaultPIIis enabled🔮 Next steps