DataDog / expo-datadog

Datadog SDK for Expo
Apache License 2.0
8 stars 5 forks source link

Support Expo EAS Updates (similar to codepush) #28

Open andrewjack-cat opened 8 months ago

andrewjack-cat commented 8 months ago

Is your feature request related to a problem? Please describe. I'd like a simple way of uploading sourcemaps to datadog when creating an Expo EAS Update.

Describe the solution you'd like New command in datadog-ci similar to the codepush command - https://github.com/DataDog/datadog-ci/tree/master/src/commands/react-native#codepush

Describe alternatives you've considered Sentry & expo have this available already - https://github.com/getsentry/sentry-react-native/blob/main/scripts/expo-upload-sourcemaps.js

Additional context https://docs.expo.dev/guides/using-sentry/#usage-with-eas-update

louiszawadzki commented 8 months ago

Hi @andrewjack-cat, thanks for reaching out!

This is indeed on our roadmap, we'll let you know once this is available :)

andrewjack-cat commented 8 months ago

In the meantime, would it still be possible to use the react-native upload command if the release tags were correctly configured?

datadog-ci react-native upload

https://github.com/DataDog/datadog-ci/tree/master/src/commands/react-native#upload

louiszawadzki commented 8 months ago

Hi @andrewjack-cat, indeed you should be able to sent the sourcemap with the datadog-ci react-native upload correctly configured.

As we match errors with sourcemaps based on their version (and with the highest build number), I would recommend to set a specific version for each EAS Update. For instance, if your app is in version 2.1.4 and you are doing the 3rd EAS Update the version could be 2.1.4-eas.v3.

You will need to set this version when uploading the sourcemap and when initializing the SDK by using the versionSuffix parameter to eas.v3 (the dash - will be added automatically by the SDK).

Let me know if this works for you!

rogerfuentes commented 7 months ago

I'm having a similar issue with this. The problem right now is not only that expo-updates uses expo export to generates a HBC file (Hermes Bytecode) which is uploaded, but also the name of the bundle in the code is loaded from a different folder (.expo-internal) and it uses a hash in the name to manage and track different versions. These 2 factors breaks the mapping between the source code and the source map (since RUM uses bundle_name to do that).

andrewjack-cat commented 7 months ago

I'm seeing the same issue has @rogerfuentes when using eas updates with the uploaded datadog sourcemaps.

Should the bundle file be renamed before uploading or can the datadog-ci/datadog-expo handle the mapping for eas updates?

rogerfuentes commented 6 months ago

@louiszawadzki any updates on this? Source maps don't work at all with expo-updates or HBC files.

viernullvier commented 4 months ago

@rogerfuentes I just managed to make everything work as expected. It still involves a workaround that's required while we wait for a proper upstream fix, but I am now able to consume bytecode sourcemaps both for full Expo builds and EAS Updates.

First, the fact that the bundle gets compiled into bytecode is not an issue after all. The bundle doesn't get uploaded to Datadog in the first place, just the sourcemap (see DataDog/datadog-ci#771 ).

Second, the only reason why Datadog fails to consume the sourcemap is the bundle name mismatch - the Expo plugin uses the JS-based bundle names index.android.bundle and main.jsbundle, which don't match the reported stack traces for Hermes builds. Note that just the file name seems to matter, the path is ignored.

Fortunately, there are two ways to fix this - there's a difficult way and an easy way:

const bundleName = Platform.OS === "android" ? "index.android.bundle" : "main.jsbundle"
config.errorEventMapper = (event) => {
  event.stacktrace = event.stacktrace.replace(/\(.*:(\d+):(\d+)\)$/gm, `(${bundleName}:$1:$2)`)
  return event
}

With this setup, it is now possible to upload additional sourcemaps for EAS updates. Here's a modified version of my script that generates and uploads Android and iOS sourcemaps after publishing an EAS build:

ANDROID_BUNDLE="dist/index.android.bundle"
IOS_BUNDLE="dist/main.jsbundle"
PACKAGE_VERSION="$(jq -r .version package.json)-$EXPO_PUBLIC_UPDATE_VERSION"
mv dist/_expo/static/js/android/*.hbc $ANDROID_BUNDLE
mv dist/_expo/static/js/android/*.hbc.map $ANDROID_BUNDLE.map
mv dist/_expo/static/js/ios/*.hbc $IOS_BUNDLE
mv dist/_expo/static/js/ios/*.hbc.map $IOS_BUNDLE.map
npx datadog-ci react-native upload --platform android --service <redacted> --bundle $ANDROID_BUNDLE --sourcemap $ANDROID_BUNDLE.map --release-version $PACKAGE_VERSION --build-version 1
npx datadog-ci react-native upload --platform ios --service <redacted> --bundle $IOS_BUNDLE --sourcemap $IOS_BUNDLE.map --release-version $PACKAGE_VERSION --build-version 1

Note that EXPO_PUBLIC_UPDATE_VERSION also needs to be consumed in the Datadog config and therefore must be set before running eas update:

if (process.env.EXPO_PUBLIC_UPDATE_VERSION) {
  config.versionSuffix = process.env.EXPO_PUBLIC_UPDATE_VERSION
}

In my setup, I'm using a combination of the total number of commits (for easier identification) and the current 7-character commit hash, but your mileage may vary.

Addendum: It turns out that you might not even go through the hassle of renaming the files to a well-known name for EAS updates - after running eas update, the generated files in dist/_expo/static/js/$PLATFORM/ already have the right hash-based filename (just with the .hbc extension), so it might in fact work without the errorEventMapper workaround after all. I'm not sure though if Datadog drops or keeps the .hbc extension, so it might still be required to remove it before uploading. Note that this only fixes EAS updates, the filename mismatch for full Expo builds still remains.

seanadkinson commented 2 months ago

Thank you @viernullvier, your errorEventMapper was the key to getting this to work for me. By rewriting the stacktraces, datadog is able to properly match the source maps, and give me the correct location in code that the error occurred in. Much appreciated.