Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Debug Ids support for JSC and Hermes bundles #3164

Merged
merged 40 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a80b3c2
POC: Add preModule for debugIds
krystofwoldrich Jun 29, 2023
ca91181
Add debug id steps
krystofwoldrich Jun 29, 2023
2467d0d
!wip
krystofwoldrich Jun 29, 2023
aaa789a
Fix serialized to work with the dev server
krystofwoldrich Jun 30, 2023
b093f67
add debug id before source map comment, fix debug id snippet
krystofwoldrich Jun 30, 2023
e0c46a6
Update sentry.gradle to use debug ids
krystofwoldrich Jul 5, 2023
5466a4e
Merge remote-tracking branch 'origin/main' into kw-debug-id-metro-plugin
krystofwoldrich Sep 14, 2023
4fa6068
Copy debug id from `debugId` and `debug_id`
krystofwoldrich Sep 14, 2023
7f5514d
Add sentry metro serializer to the SDK project
krystofwoldrich Sep 19, 2023
9a408fe
Merge remote-tracking branch 'origin/main' into kw-debug-id-metro-plugin
krystofwoldrich Sep 19, 2023
b12997f
fix yarn lock
krystofwoldrich Sep 19, 2023
3bb9a53
Exclude metro plugin types from react native sdk build
krystofwoldrich Sep 19, 2023
ec34509
Add deterministic debug id generator
krystofwoldrich Sep 19, 2023
157cd42
Add sentry bundle callback and js docs
krystofwoldrich Sep 20, 2023
a998824
Add backward compatible Debug ID upload to `sentry.gradle`
krystofwoldrich Sep 21, 2023
0955f2d
Add new script to published package
krystofwoldrich Sep 21, 2023
6efb620
Move xcode build phase script to rn sdk package
krystofwoldrich Sep 21, 2023
731af02
Remove extra sentry cli args
krystofwoldrich Sep 21, 2023
d248b72
Use sourcemap file to enable hermesc source maps and fallback to rele…
krystofwoldrich Sep 22, 2023
9fce536
Fix lint metro serializer
krystofwoldrich Sep 22, 2023
1c0d3ef
Add metro serializer tests
krystofwoldrich Sep 25, 2023
4335923
Fix sample debug files upload
krystofwoldrich Sep 25, 2023
19f22e9
Use debug ids in e2e ios apps
krystofwoldrich Sep 25, 2023
f764255
Add sentry serializer to e2e tests
krystofwoldrich Sep 25, 2023
02cd404
Fix metro config e2e patch
krystofwoldrich Sep 26, 2023
79d9d8c
Update debug id snippet for multiple bundles in one runtime
krystofwoldrich Sep 26, 2023
9add523
Remove node_modules from metro config patch
krystofwoldrich Sep 26, 2023
889265e
Merge branch 'main' into kw-debug-id-metro-plugin
krystofwoldrich Sep 26, 2023
6f981cc
Add changelog
krystofwoldrich Sep 26, 2023
daebe29
Update has sourcemap debugid script name
krystofwoldrich Sep 26, 2023
e68cd87
TMP: Remove no auto release to run ios e2e tests
krystofwoldrich Sep 26, 2023
661e8ae
Add metro typings license
krystofwoldrich Sep 27, 2023
ad0a8e8
Fix review comments
krystofwoldrich Sep 27, 2023
c9ff331
Add virtual js output docs
krystofwoldrich Sep 27, 2023
7baf4c0
Apply suggestions from code review
krystofwoldrich Sep 28, 2023
6beff46
Merge branch 'main' into kw-debug-id-metro-plugin
krystofwoldrich Oct 4, 2023
e1c6409
Fix lint
krystofwoldrich Oct 4, 2023
58bfbfd
Merge remote-tracking branch 'origin/main' into kw-debug-id-metro-plugin
krystofwoldrich Oct 4, 2023
f845642
Revert "TMP: Remove no auto release to run ios e2e tests"
krystofwoldrich Oct 4, 2023
2826fc4
Fix e2e metro config patch
krystofwoldrich Oct 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ jobs:
run: |
patch --verbose --strip=0 --force --ignore-whitespace --fuzz 4 < ../../../rn.patch
../../../rn.patch.app.js --app .
../../../rn.patch.metro.config.js --path metro.config.js

- name: Patch Android App RN
if: ${{ matrix.platform == 'android' }}
Expand Down
4 changes: 4 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@
!/android/**/*
!src/js/NativeRNSentry.ts
!scripts/collect-modules.sh
!scripts/copy-debugid.js
!scripts/has-sourcemap-debugid.js
!scripts/sentry-xcode.sh
!scripts/sentry-xcode-debug-files.sh
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@
### Features

- Add `buildFeatures.buildConfig=true` to support AGP 8 ([#3298](https://github.com/getsentry/sentry-react-native/pull/3298))
- Add Debug ID support ([#3164](https://github.com/getsentry/sentry-react-native/pull/3164))

This is optional to use Debug IDs. Your current setup will keep working as is.

Add Sentry Metro Serializer to `metro.config.js` to generate Debug ID for the application bundle and source map.

```javascript
const {createSentryMetroSerializer} = require('@sentry/react-native/dist/js/tools/sentryMetroSerializer');
const config = {serializer: createSentryMetroSerializer()};
```

On iOS update `Bundle React Native Code and Images` and `Upload Debug Symbols to Sentry` build phases.

```bash
set -e
WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
REACT_NATIVE_XCODE="../node_modules/react-native/scripts/react-native-xcode.sh"

/bin/sh -c "$WITH_ENVIRONMENT \"/bin/sh ../scripts/sentry-xcode.sh $REACT_NATIVE_XCODE\""
```

```bash
/bin/sh ../../scripts/sentry-xcode-debug-files.sh
```

More information about the new setup [can be found here](https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/).

### Fixes

Expand Down
16 changes: 16 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module.exports = {
collectCoverage: true,
preset: 'react-native',
setupFilesAfterEnv: ['<rootDir>/test/mockConsole.ts'],
globals: {
__DEV__: true,
'ts-jest': {
tsConfig: './tsconfig.json',
diagnostics: false,
},
},
moduleFileExtensions: ['ts', 'tsx', 'js'],
testPathIgnorePatterns: ['<rootDir>/test/e2e/', '<rootDir>/test/tools/', '<rootDir>/test/react-native/versions'],
testEnvironment: 'node',
testMatch: ['**/*.test.(ts|tsx)'],
};
18 changes: 18 additions & 0 deletions jest.config.tools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
collectCoverage: true,
preset: 'ts-jest',
setupFilesAfterEnv: ['<rootDir>/test/mockConsole.ts'],
globals: {
__DEV__: true,
},
testMatch: ['**/test/tools/**/*.ts'],
transform: {
'^.+\\.(ts|tsx)$': [
'ts-jest',
{
tsconfig: './tsconfig.build.tools.json',
diagnostics: false,
},
],
},
};
38 changes: 9 additions & 29 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
"build:tools": "tsc -p tsconfig.build.tools.json",
"downlevel": "downlevel-dts dist ts3.8/dist --to=3.8",
"clean": "rimraf dist coverage",
"test": "jest",
"test": "yarn test:sdk && yarn test:tools",
"test:sdk": "jest",
"test:tools": "jest --config jest.config.tools.js",
"fix": "yarn fix:eslint && yarn fix:prettier",
"fix:eslint": "eslint --config .eslintrc.js --fix .",
"fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"",
Expand Down Expand Up @@ -72,20 +74,25 @@
"@sentry/wizard": "3.13.0",
"@types/jest": "^29.5.3",
"@types/react": "^18.2.14",
"@types/uglify-js": "^3.17.2",
"@types/uuid": "^9.0.4",
"babel-jest": "^29.6.2",
"downlevel-dts": "^0.11.0",
"eslint": "^7.6.0",
"eslint-plugin-react": "^7.20.6",
"eslint-plugin-react-native": "^3.8.1",
"jest": "^29.6.2",
"jest-environment-jsdom": "^29.6.2",
"metro": "0.76",
"prettier": "^2.0.5",
"react": "18.2.0",
"react-native": "0.72.4",
"replace-in-file": "^7.0.1",
"rimraf": "^4.1.1",
"ts-jest": "^29.1.1",
"typescript": "4.9.5"
"typescript": "4.9.5",
"uglify-js": "^3.17.4",
"uuid": "^9.0.1"
},
"rnpm": {
"commands": {},
Expand All @@ -95,33 +102,6 @@
},
"ios": {}
},
"jest": {
"collectCoverage": true,
"preset": "react-native",
"setupFilesAfterEnv": [
"<rootDir>/test/mockConsole.ts"
],
"globals": {
"__DEV__": true,
"ts-jest": {
"tsConfig": "./tsconfig.json",
"diagnostics": false
}
},
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"testPathIgnorePatterns": [
"<rootDir>/test/e2e/",
"<rootDir>/test/react-native/versions"
],
"testEnvironment": "node",
"testMatch": [
"**/*.test.(ts|tsx)"
]
},
"codegenConfig": {
"name": "RNSentrySpec",
"type": "modules",
Expand Down
2 changes: 2 additions & 0 deletions sample-new-architecture/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ project.ext.sentryCli = [
"../..",
],
skipCollectModules: false,
copyDebugIdScript: "../../../scripts/copy-debugid.js",
hasSourceMapDebugIdScript: "../../../scripts/has-sourcemap-debugid.js",
]

apply from: "../../../sentry.gradle"
Expand Down
1 change: 1 addition & 0 deletions sample-new-architecture/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
org.gradle.logging.level=lifecycle

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
Expand Down
8 changes: 8 additions & 0 deletions sample-new-architecture/ios/.xcode.env
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@
# For example, to use nvm with brew, add the following line
#. "$(brew --prefix nvm)/nvm.sh" --no-use
export NODE_BINARY=$(command -v node)

export EXTRA_COMPILER_ARGS="-w"

export SENTRY_CLI_EXECUTABLE="../../node_modules/@sentry/cli/bin/sentry-cli"
export SENTRY_CLI_EXTRA_ARGS="--force-foreground"
export SENTRY_CLI_DEBUG_FILES_UPLOAD_EXTRA_ARGS=""
export SENTRY_CLI_RN_XCODE_EXTRA_ARGS=""
export MODULES_PATHS="$PWD/../node_modules,$PWD/../../.."
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\n\nexport SENTRY_PROPERTIES=sentry.properties\nexport EXTRA_PACKAGER_ARGS=\"--sourcemap-output $DERIVED_FILE_DIR/main.jsbundle.map\"\n\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\nSENTRY_CLI=\"../../node_modules/@sentry/cli/bin/sentry-cli\"\nBUNDLE_REACT_NATIVE=\"$SENTRY_CLI react-native xcode --force-foreground $REACT_NATIVE_XCODE\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT \\\"$BUNDLE_REACT_NATIVE\\\"\"\n\nexport MODULES_PATHS=\"$PWD/../node_modules,$PWD/../../..\"\nCOLLECT_MODULES=\"../../scripts/collect-modules.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT \\\"$COLLECT_MODULES\\\"\"\n";
shellScript = "set -e\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\nBUNDLE_REACT_NATIVE=\"/bin/sh ../../scripts/sentry-xcode.sh $REACT_NATIVE_XCODE\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT \\\"$BUNDLE_REACT_NATIVE\\\"\"\n";
};
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
Expand Down Expand Up @@ -303,7 +303,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "set -e\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\n\nexport SENTRY_PROPERTIES=sentry.properties\n\n[[ $SENTRY_INCLUDE_NATIVE_SOURCES == \"true\" ]] && INCLUDE_SOURCES_FLAG=\"--include-sources\" || INCLUDE_SOURCES_FLAG=\"\"\nSENTRY_CLI=\"../../node_modules/@sentry/cli/bin/sentry-cli\"\nFLAGS=\"--force-foreground $INCLUDE_SOURCES_FLAG\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT \\\"$UPLOAD_DEBUG_FILES\\\"\"\n";
shellScript = "/bin/sh ../../scripts/sentry-xcode-debug-files.sh\n";
};
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
Expand Down
9 changes: 8 additions & 1 deletion sample-new-architecture/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
const path = require('path');
const blacklist = require('metro-config/src/defaults/exclusionList');

const {
createSentryMetroSerializer,
} = require('../dist/js/tools/sentryMetroSerializer');
const parentDir = path.resolve(__dirname, '..');

/**
Expand Down Expand Up @@ -41,6 +44,10 @@ const config = {
},
),
},
serializer: {
customSerializer: createSentryMetroSerializer(),
},
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
const m = mergeConfig(getDefaultConfig(__dirname), config);
module.exports = m;
52 changes: 52 additions & 0 deletions scripts/copy-debugid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const process = require('process');
const fs = require('fs');

console.log('Copy `debugId` from packager source map to Hermes source map...');

const packagerSourceMapPath = process.argv[2];
const hermesSourceMapPath = process.argv[3];

if (!packagerSourceMapPath) {
console.log('Please provide packager source map path (A path to copy `debugId` from).');
process.exit(0);
}
if (!hermesSourceMapPath) {
console.log('Please provide Hermes source map path. ((A path to copy `debugId` to))');
process.exit(0);
}
if (!fs.existsSync(packagerSourceMapPath)) {
console.log('Packager source map path (A path to copy `debugId` from).');
process.exit(0);
}
if (!fs.existsSync(hermesSourceMapPath)) {
console.log('Hermes source map not found. ((A path to copy `debugId` to))');
process.exit(0);
}

const from = fs.readFileSync(process.argv[2], 'utf8');
const to = fs.readFileSync(process.argv[3], 'utf8');

const fromParsed = JSON.parse(from);
const toParsed = JSON.parse(to);

if (!fromParsed.debugId && !fromParsed.debug_id) {
console.log('Packager source map does not have `debugId`.');
process.exit(0);
}

if (toParsed.debugId || toParsed.debug_id) {
console.log('Hermes combined source map already has `debugId`.');
process.exit(0);
}

if (fromParsed.debugId) {
toParsed.debugId = fromParsed.debugId;
toParsed.debug_id = fromParsed.debugId;
} else if (fromParsed.debug_id) {
toParsed.debugId = fromParsed.debug_id;
toParsed.debug_id = fromParsed.debug_id;
}

fs.writeFileSync(process.argv[3], JSON.stringify(toParsed));

console.log('Done.');
31 changes: 31 additions & 0 deletions scripts/has-sourcemap-debugid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const process = require('process');
const fs = require('fs');

const sourceMapPath = process.argv[2];

if (!sourceMapPath) {
console.log('Add source map path as first argument of the script.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we console.warn or console.error these? Ditto for the console logs that are below this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that's more correct, but I had issues reading std err output in https://github.com/getsentry/sentry-react-native/pull/3164/files#diff-01c94c11105e37de1b11a82973b605f625fff5959b0386c32fd1e42a14325208R125-R126

So that's why I kept it console.log. More important is that we have the log.

process.exit(1);
}

if (!fs.existsSync(sourceMapPath)) {
console.log(`${sourceMapPath} does not exist.`);
process.exit(1);
}

let sourceMap;
try {
sourceMap = JSON.parse(fs.readFileSync(sourceMapPath, 'utf8'));
} catch (e) {
console.log(`Sourcemap at ${sourceMapPath} was unable to be read.`, e);
process.exist(1);
}

if (typeof sourceMap.debugId === 'string' && sourceMap.debugId.length > 0) {
console.log(sourceMap.debugId);
} else if (typeof sourceMap.debug_id === 'string' && sourceMap.debug_id.length > 0) {
console.log(sourceMap.debug_id);
} else {
console.log(`${sourceMapPath} does not contain 'debugId' nor 'debug_id'.`);
process.exist(1);
}
18 changes: 18 additions & 0 deletions scripts/sentry-debugid-injection-snippet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This is non minified version the debug id injection snippet used in the Metro plugin.
var _sentryDebugIds = {};
var _sentryDebugIdIdentifier = '';

if (typeof _sentryDebugIds === 'undefined') {
_sentryDebugIds = {};
}

try {
var stack = new Error().stack;
if (stack) {
_sentryDebugIds[stack] = '__SENTRY_DEBUG_ID__';
// eslint-disable-next-line no-unused-vars
_sentryDebugIdIdentifier = 'sentry-dbid-__SENTRY_DEBUG_ID__';
}
} catch (e) {
/**/
}
22 changes: 22 additions & 0 deletions scripts/sentry-xcode-debug-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash
# Upload Debug Symbols to Sentry Xcode Build Phase
# PWD=ios

# print commands before executing them and stop on first error
set -x -e

# load envs if loader file exists (since rn 0.68)
WITH_ENVIRONMENT="../node_modules/react-native/scripts/xcode/with-environment.sh"
if [ -f "$WITH_ENVIRONMENT" ]; then
. "$WITH_ENVIRONMENT"
fi

[ -z "$SENTRY_PROPERTIES" ] && export SENTRY_PROPERTIES=sentry.properties
[ -z "$SENTRY_CLI_EXECUTABLE" ] && SENTRY_CLI_EXECUTABLE="../node_modules/@sentry/cli/bin/sentry-cli"

[[ $SENTRY_INCLUDE_NATIVE_SOURCES == "true" ]] && INCLUDE_SOURCES_FLAG="--include-sources" || INCLUDE_SOURCES_FLAG=""

EXTRA_ARGS="$SENTRY_CLI_EXTRA_ARGS $SENTRY_CLI_DEBUG_FILES_UPLOAD_EXTRA_ARGS $INCLUDE_SOURCES_FLAG"

UPLOAD_DEBUG_FILES="\"$SENTRY_CLI_EXECUTABLE\" debug-files upload $EXTRA_ARGS \"$DWARF_DSYM_FOLDER_PATH\""
/bin/sh -c "$UPLOAD_DEBUG_FILES"
27 changes: 27 additions & 0 deletions scripts/sentry-xcode.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
# Sentry Bundle React Native code and images
# PWD=ios

# print commands before executing them and stop on first error
set -x -e

# WITH_ENVIRONMENT is executed by React Native

[ -z "$SENTRY_PROPERTIES" ] && export SENTRY_PROPERTIES=sentry.properties
[ -z "$SOURCEMAP_FILE" ] && export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"
[ -z "$SENTRY_CLI_EXECUTABLE" ] && SENTRY_CLI_EXECUTABLE="../node_modules/@sentry/cli/bin/sentry-cli"

REACT_NATIVE_XCODE=$1

[[ "$AUTO_RELEASE" != true ]] && [[ -z "$BUNDLE_COMMAND" || "$BUNDLE_COMMAND" != "ram-bundle" ]] && NO_AUTO_RELEASE="--no-auto-release"
ARGS="$NO_AUTO_RELEASE $SENTRY_CLI_EXTRA_ARGS $SENTRY_CLI_RN_XCODE_EXTRA_ARGS"

BUNDLE_REACT_NATIVE="\"$SENTRY_CLI_EXECUTABLE\" react-native xcode $ARGS \"$REACT_NATIVE_XCODE\""

/bin/sh -c "$BUNDLE_REACT_NATIVE"

[ -z "$SENTRY_COLLECT_MODULES" ] && SENTRY_COLLECT_MODULES="../../scripts/collect-modules.sh"

if [ -f "$SENTRY_COLLECT_MODULES" ]; then
/bin/sh "$SENTRY_COLLECT_MODULES"
fi
Loading
Loading