expo / sentry-expo

MIT License
202 stars 83 forks source link

Uploading source maps for updates not working #319

Closed karbone4 closed 1 year ago

karbone4 commented 1 year ago

Summary

I use expo-dev-client. When I build my app, source maps are uploaded, and I can see clearly the error on sentry thanks to postPublish script. I followed instructions on the doc https://docs.expo.dev/guides/using-sentry/#uploading-source-maps-for-updates for uploading source maps, but it isn't working 😔. I can't see where is the error due to wrong source maps : image

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

managed

What platform(s) does this occur on?

Android, iOS

SDK Version (managed workflow only)

47

Environment

expo-env-info 1.0.5 environment info: System: OS: macOS 13.2 Shell: 5.8.1 - /bin/zsh Binaries: Node: 16.17.1 - ~/.nvm/versions/node/v16.17.1/bin/node Yarn: 1.22.19 - ~/projects/cocolis-react-native/node_modules/.bin/yarn npm: 9.3.1 - ~/.nvm/versions/node/v16.17.1/bin/npm Watchman: 2022.11.07.00 - /opt/homebrew/bin/watchman Managers: CocoaPods: 1.11.3 - /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: DriverKit 22.2, iOS 16.2, macOS 13.1, tvOS 16.1, watchOS 9.1 IDEs: Android Studio: 2021.3 AI-213.7172.25.2113.9014738 Xcode: 14.2/14C18 - /usr/bin/xcodebuild npmPackages: expo: ^47.0.0 => 47.0.8 react: 18.1.0 => 18.1.0 react-dom: 18.1.0 => 18.1.0 react-native: 0.70.5 => 0.70.5 npmGlobalPackages: eas-cli: 3.5.2 expo-cli: 6.1.0 Expo Workflow: managed

"@sentry/react-native": "4.9.0",
"sentry-expo": "~6.0.0",

Reproducible demo or steps to reproduce from a blank project

  1. eas build with postPublish hook script → upload source maps to sentry
  2. Error stack trace is ok image
  3. eas update (generate bundles in dist folder) It generates :
    • ios-$hash.js
    • ios-$hash.map
    • android-$hash.js
    • android-$hash.map
  4. Upload source maps using doc :
    node_modules/@sentry/cli/bin/sentry-cli releases \
    files com.and.test@1.0+14 \
    upload-sourcemaps \
    --dist 14 \
    --rewrite \
    dist/bundles/android-$hash.js dist/bundles/android-$hash.map
    node_modules/@sentry/cli/bin/sentry-cli releases \
    files com.ios.test@1.0+8 \
    upload-sourcemaps \
    --dist 8 \
    --rewrite \
    dist/bundles/ios-$hash.js dist/bundles/ios-$hash.map

    I use nativeVersion as dist because in my Sentry.init, dist is set to nativeVersion. I tried to change it to eas update id but it does the same. I already tested to rename bundle files for matching build bundle files, but it didn't work either.

    • ios-$hash.js → main.jsbundle
    • ios-$hash.map → main.jsbundle.map
    • android-$hash.js → index.android.bundle
    • android-$hash.map → index.android.bundle.map
  5. Now the source maps are uploaded but js is 0B for both os. image image
  6. No stack trace : image
kbrandwijk commented 1 year ago

As per the instructions in our docs, you should rename the bundles only, not the map files.

fuelkoy commented 1 year ago

Related to https://github.com/expo/sentry-expo/issues/313?

karbone4 commented 1 year ago

I misunderstood the doc. Now it works, thanks !

I did a script in order to upload source map from eas update if anyone wants it (run it ts-node my-script.ts) :

import { exec } from 'child_process';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Command } from 'commander';
import util from 'util';

import { expo } from '../../app.json';
import { build } from '../../eas.json';
import { version } from '../../package.json';

const promisifiedExec = util.promisify(exec);

const uploadAndroidSourceMap = async (easBuild: any, updates: any) => {
    const appVersion = version;

    const androidVersionCode = expo.android.versionCode;
    const androidPackageName = easBuild.env.ANDROID_PACKAGE_NAME;
    const androidUpdateId = updates.find((update: any) => update.platform === 'android').id;
    await promisifiedExec(`mv dist/bundles/android*.js dist/bundles/index.android.bundle`);
    await promisifiedExec(`node_modules/@sentry/cli/bin/sentry-cli \
        releases \
        files ${androidPackageName}@${appVersion}+${androidVersionCode} \
        upload-sourcemaps \
        --dist ${androidUpdateId} \
        --rewrite \
        dist/bundles/index.android.bundle dist/bundles/android-*.map`);
};

const uploadIosSourceMap = async (easBuild: any, updates: any) => {
    const appVersion = version;

    const iosBuildNumber = expo.ios.buildNumber;
    const iosBundleID = easBuild.env.IOS_BUNDLE_ID;
    const iosUpdateId = updates.find((update: any) => update.platform === 'ios').id;
    await promisifiedExec(`mv dist/bundles/ios*.js dist/bundles/main.jsbundle`);
    await promisifiedExec(`node_modules/@sentry/cli/bin/sentry-cli \
        releases \
        files ${iosBundleID}@${appVersion}+${iosBuildNumber} \
        upload-sourcemaps \
        --dist ${iosUpdateId} \
        --rewrite \
        dist/bundles/main.jsbundle dist/bundles/ios-*.map`);
};

const program = new Command().requiredOption('-p, --profile  [value]', 'EAS profile').action(async (options) => {
    if (!Object.keys(build).includes(options.profile)) {
        console.error('Profile must be includes in : ', Object.keys(build));
    }
    const easBuild = build[options.profile as keyof typeof build];
    const { channel } = easBuild;

    const channelUpdates = await promisifiedExec(`eas update:list --branch ${channel} --non-interactive --json`);
    if (channelUpdates.stderr) {
        console.error(channelUpdates.stderr);
    }
    const groupID = JSON.parse(channelUpdates.stdout).currentPage[0].group;
    const updates = await promisifiedExec(`eas update:view ${groupID} --json`);
    if (updates.stderr) {
        console.error(updates.stderr);
    }

    await uploadAndroidSourceMap(easBuild, JSON.parse(updates.stdout));
    await uploadIosSourceMap(easBuild, JSON.parse(updates.stdout));
});

program.parse();
federico-bubble commented 6 months ago

I misunderstood the doc. Now it works, thanks !

I did a script in order to upload source map from eas update if anyone wants it (run it ts-node my-script.ts) :

import { exec } from 'child_process';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Command } from 'commander';
import util from 'util';

import { expo } from '../../app.json';
import { build } from '../../eas.json';
import { version } from '../../package.json';

const promisifiedExec = util.promisify(exec);

const uploadAndroidSourceMap = async (easBuild: any, updates: any) => {
    const appVersion = version;

    const androidVersionCode = expo.android.versionCode;
    const androidPackageName = easBuild.env.ANDROID_PACKAGE_NAME;
    const androidUpdateId = updates.find((update: any) => update.platform === 'android').id;
    await promisifiedExec(`mv dist/bundles/android*.js dist/bundles/index.android.bundle`);
    await promisifiedExec(`node_modules/@sentry/cli/bin/sentry-cli \
        releases \
        files ${androidPackageName}@${appVersion}+${androidVersionCode} \
        upload-sourcemaps \
        --dist ${androidUpdateId} \
        --rewrite \
        dist/bundles/index.android.bundle dist/bundles/android-*.map`);
};

const uploadIosSourceMap = async (easBuild: any, updates: any) => {
    const appVersion = version;

    const iosBuildNumber = expo.ios.buildNumber;
    const iosBundleID = easBuild.env.IOS_BUNDLE_ID;
    const iosUpdateId = updates.find((update: any) => update.platform === 'ios').id;
    await promisifiedExec(`mv dist/bundles/ios*.js dist/bundles/main.jsbundle`);
    await promisifiedExec(`node_modules/@sentry/cli/bin/sentry-cli \
        releases \
        files ${iosBundleID}@${appVersion}+${iosBuildNumber} \
        upload-sourcemaps \
        --dist ${iosUpdateId} \
        --rewrite \
        dist/bundles/main.jsbundle dist/bundles/ios-*.map`);
};

const program = new Command().requiredOption('-p, --profile  [value]', 'EAS profile').action(async (options) => {
    if (!Object.keys(build).includes(options.profile)) {
        console.error('Profile must be includes in : ', Object.keys(build));
    }
    const easBuild = build[options.profile as keyof typeof build];
    const { channel } = easBuild;

    const channelUpdates = await promisifiedExec(`eas update:list --branch ${channel} --non-interactive --json`);
    if (channelUpdates.stderr) {
        console.error(channelUpdates.stderr);
    }
    const groupID = JSON.parse(channelUpdates.stdout).currentPage[0].group;
    const updates = await promisifiedExec(`eas update:view ${groupID} --json`);
    if (updates.stderr) {
        console.error(updates.stderr);
    }

    await uploadAndroidSourceMap(easBuild, JSON.parse(updates.stdout));
    await uploadIosSourceMap(easBuild, JSON.parse(updates.stdout));
});

program.parse();

Worked for us 🙌