This is basically a bug report on the insufficiency of the Manual Codepush setup setup doc.
I tried to fill in the gaps from this comment: https://github.com/getsentry/sentry-react-native/issues/79#issuecomment-573288459
The solution proposed in the comment uses Sentry.setRelease function which is deprecated (along with setDist), while the docs suggests not waiting for codePush.getUpdateMetadata(), because Sentry.init might not be called before any related crash might happen.
I don't have question over how to upload sourcemaps, or create a release, rather what is the source of truth I need to use and is there a preferred order or not (from the docs the only thing clear that the codePush.getUpdateMetadata() solution is not).
As per my understanding, this is the broken solution. Feel free if I misunderstood any part how things work in either react-native or Codepush or Sentry.
version following semverI wish to use one semver following version to identify my application version (e.g. 1.2.3) and upload its sourcemaps to Sentry. I use Codepush for minor updates.
After every Codepush update I would like to see Sentry.init being called with this version specified above.
package.jsonIn this case I bump the version number in package.json. Let's say current version is 1.2.2, I manually bump the version to 1.2.3.
The flow in this case:
1.2.2 in package.json to 1.2.3appcenter codepush react-release...sentry-cli releases...import version from '../package.json'Sentry.init({ release: version })codepush updatesThe problem is that version from package.json is not updated!
(see issue: Version from package.json not updated)
This way even though the right version has the right sourcemap the application itself tells otherwise.
version into description of codepush updateIn this scenario I pass version from package.json to the description of codepush release:
appcenter codepush release-react --app <my-org/my-app> \
--description "Version: [<1.2.3>]; my random release information" # dinamically set it from package.json
--sourcemap-output
# ...
The flow in this case:
1.2.2 in package.json to 1.2.3appcenter codepush react-release... with description having the versionsentry-cli releases...codePush.getUpdateMetadata()localPackage exists unpack version from result through hacky string regexinglocalPackage does not exist import version from '../package.json'Sentry.init({ release: version })codepush updatesAt least this solution updates version dynamically.
The problem is using description is hacky. Any new colleague might see description and rewrite it, maybe omitting version altogether, or deciding that [] is ugly not knowing that the regex was looking for it...
.json file somewhere which contains the versionIn this scenario I create a version.json file lying around somewhere, manually bump it for every release and gets updated with every codepush release.
The flow in this case:
1.2.2 in version.json to 1.2.3appcenter codepush react-release...sentry-cli releases... with release name [email protected]import version from '../version.json'Sentry.init({ release: 'my-app@' + version })codepush updatesThe problem with this scenario that version in package.json and version in version.json might go inconsistent (can you delete version from package.json?). Again, new colleague comes, see the duplication and/or inconsistency, decides to refactor by dropping the duplication and things go 馃挜 .
codepush labelThis is tricky again. First I will lose the relevant semver version about my app. Second this solution again only works with hope until the first update.
The flow in this case:
version.json containing latest codepushLabel: { "latestCodepushLabel": "v13" } "v13" in version.json to "v14" and hope this will coincide with the next labelappcenter codepush react-release...codepushLabel from appcenter by running appcenter codepush deployment list --app <org/project> --output jsonsentry-cli releases... with release name my-app@<latestCodepushLabel>codePush.getUpdateMetadata()localPackage exists unpack latestCodepushLabel from resultlocalPackage does not exist import latestCodepushLabel from '../version.json'Sentry.init({ release: 'my-app@' + latestCodepushLabel })codepush updates and we update continuously the release propertycodePush.getUpdateMetadata(), therefore we need to bundle the _next_ version label into the codepush even before the version label itself existssemver version completely as the release string will be something like my-app@v14; so for identifying the proper version, I need to go diggingversion.json and codepushLabelThis results in the fabled release string: [email protected]+codepush:v14.
In this case we have both of the disadvantages of Solution 3 and Solution 4: semver version comes from version.json while codepushLabel is either coming from version.json or from the latest package, while I need to guess the _next_ codepushLabel version and bundle it into version.json for apps that were not yet updated through codepush.
codePush.getUpdateMetadata() before I call Sentry.init if I specify codepush version number? The reason is that since calling codepush is async code, I might miss crashes that happen before the code completes. For instance if there is a fatal flaw in codepush.getUpdateMetadata function, Sentry.init will never run and I would never get a report.
SDK:
@sentry/react-nativeSDK version: 2.4.2
react-native version: 0.63.2
Are you using Expo?
Are you using sentry.io or on-premise?
Yes, the issue surrounding this is as long as getUpdateMetadata is async, it wouldn't be a good idea to hold off the Sentry.init call otherwise you could miss out on potential early crashes. Most of the solutions have disadvantages, and in the most ideal world we should be able to just use getUpdateMetadata like CodePush suggests, but as long as it's async it won't work.
To answer your questions,
@jennmueng , we have checked the package.json solution and it really is getting updated, so we have known an old issue, thanks for that.
We are currently ironing out this solution, that package.json is the source of truth and we ignore the Codepush versionLabel entirely. I am on sick leave though, so probably I will update this issue on Monday, Tuesday most likely. Thanks for your patience.
Alright, sounds good @latobibor if it ends up working when you're back from leave you can close the issue yourself. Get well soon!
@jennmueng
Thank you for your patience and the consistent contact regarding this issue, we greatly appreciate it!
I have implemented solution 1, but we still face wrong labelling of issues pointing to an earlier version.
The test setup we had was this:
4.0.2 in package.jsonCodepush to our dev environment there4.0.0 (1))Sentry.init using the _exact same_ release string I passed into it: const release = `${packageJson.name}@${packageJson.version}`;
Sentry.init({
// ... private fields
release,
});
Sentry.setTag('appVersion', VersionInfo.appVersion);
throw new Error(`test error for [${release}]`);
Link to the issue:
https://sentry.io/organizations/acasus-bp/issues/2391846871/?project=5465163&query=is%3Aunresolved
Link to the release appearing as it is (sourcemaps are uploaded):
https://sentry.io/organizations/acasus-bp/releases/AcasusData%404.0.2/?project=5465163&statsPeriod=14d&unselectedSeries=Healthy

If you specify the CodePush version number yourself you don't have to rely on the method.
As far as I know I can't set the version number for CodePush. If I am creating a release there I will receive an automatically assigned version number, like v13. I have found no flag in the CLI to specify a custom version number. Therefore I will have this version number only after I have bundled and released the code through CodePush already.
The version can be modified in beforeSend:
Sentry.init({
beforeSend: async (event) => {
const release = await getFromCodepush();
event.release = release;
return event;
}
});
@hermanho Do not do that. The release health sessions and the native layer errors will not be assigned to the correct release.
@jennmueng
Thank you for your patience and the consistent contact regarding this issue, we greatly appreciate it!I have implemented solution 1, but we still face wrong labelling of issues pointing to an earlier version.
The test setup we had was this:
- pull latest master
- set version number to
4.0.2inpackage.json- created a test release using
Codepushto our dev environment there- then created an error in the codebase while running a debug build through Metro server (I know that sourcemap won't be accurate here, since I am running a different codebase)
- checked if it is pointing to the right release (it is showing
4.0.0 (1))- the error was thrown right after
Sentry.initusing the _exact same_ release string I passed into it:const release = `${packageJson.name}@${packageJson.version}`; Sentry.init({ // ... private fields release, }); Sentry.setTag('appVersion', VersionInfo.appVersion); throw new Error(`test error for [${release}]`);Link to the issue:
https://sentry.io/organizations/acasus-bp/issues/2391846871/?project=5465163&query=is%3AunresolvedLink to the release appearing as it is (sourcemaps are uploaded):
https://sentry.io/organizations/acasus-bp/releases/AcasusData%404.0.2/?project=5465163&statsPeriod=14d&unselectedSeries=HealthyScreenshot of
Answer to your second question:
If you specify the CodePush version number yourself you don't have to rely on the method.
As far as I know I can't set the version number for
CodePush. If I am creating a release there I will receive an automatically assigned version number, likev13. I have found no flag in the CLI to specify a custom version number. Therefore I will have this version number only after I have bundled and released the code throughCodePushalready.
I will have to look into this further, I haven't seen this issue with the package version not being updated before.
@jennmueng
After some testing and considerations this is our current solution:
Sentry.init({
dsn: '...',
release: `${packageJson.name}@${packageJson.version}`,
dist: packageJson.version,
});
Without setting an explicit dist the release version was not followed correctly in issues.
@jennmueng
After some testing and considerations this is our current solution:Sentry.init({ dsn: '...', release: `${packageJson.name}@${packageJson.version}`, dist: packageJson.version, });Without setting an explicit
distthe release version was not followed correctly in issues.
@latobibor Oh yes you will have to set the dist to for custom releases to work as well. I did not mention that because I assumed it was being set already. Hmm do you have any suggestions for how we can make the docs more clear?
@jennmueng
Yes, after all this I would recommend the following:
The entire part between CodePush title and Making releases. However this part needs a lot of clarification on "why do you need to use codePush.getUpdateMetadata".
If you need to use codePush.getUpdateMetadata, you will have to wait to initialize the Sentry SDK until the Promise is resolved.
Releases are tracked by matching both the release label and the dist label. These two must be set together. Here you can have several strategies:
version from package.jsonIn this case we ignore the version label from CodePush and solely use the version property of package.json (or a custom version file).
Your code would look largely like this:
import packageJson from '../package.json';
Sentry.init({
dsn: '...',
release: `${packageJson.name}@${packageJson.version}`,
dist: packageJson.version,
});
version from package.json and version label from CodePushIn this case we recommend the following format for release property: ${BUNDLE_ID}@${APP_VERSION}+codepush:${DIST}.
To accurately obtain the version label (named here as DIST) you have to asynchronously obtain it using codePush.getUpdateMetadata(). This solution delays initialization of Sentry, so be cautious that you might lose accurate information on crashes that happen in this time.
import packageJson from '../package.json';
import currentCodePushLabel from '../my-first-codepush-version.json';
async initSentry() {
const update = await codePush.getUpdateMetadata();
// before the first update `currentCodePushLabel()` will return null, so you need to set this manually
const dist = update ? update.label : currentCodePushLabel;
Sentry.init({
// ...
release: `${packageJson.name}@${packageJson.version}+codepush:${dist}`,
dist: dist
});
}
});
In this case just make sure that both release and dist properties are updated and dist accurately represents the code running currently on the device.
@latobibor Thank you so much for the suggestion! I will take exactly this and work it into our docs.
Thank you for the amazing support @jennmueng ! We appreciated it!
Most helpful comment
@jennmueng
Yes, after all this I would recommend the following:
Docs that I would change
The entire part between
CodePushtitle andMaking releases. However this part needs a lot of clarification on "why do you need to use codePush.getUpdateMetadata".My version would be
Releases are tracked by matching both the
releaselabel and thedistlabel. These two must be set together. Here you can have several strategies:1. Use
versionfrompackage.jsonIn this case we ignore the version label from CodePush and solely use the
versionproperty ofpackage.json(or a custom version file).Your code would look largely like this:
2. Use
versionfrompackage.jsonand version label from CodePushIn this case we recommend the following format for
releaseproperty:${BUNDLE_ID}@${APP_VERSION}+codepush:${DIST}.To accurately obtain the version label (named here as
DIST) you have to asynchronously obtain it usingcodePush.getUpdateMetadata(). This solution delays initialization of Sentry, so be cautious that you might lose accurate information on crashes that happen in this time.3. Totally custom values
In this case just make sure that both
releaseanddistproperties are updated anddistaccurately represents the code running currently on the device.