Skip to content

Commit c0d80f9

Browse files
committed
fix(bundle): resolve native Sentry release for OTA sourcemaps
1 parent 1acf0a2 commit c0d80f9

9 files changed

Lines changed: 880 additions & 38 deletions

File tree

cli.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,30 @@
297297
},
298298
"dryRun": {
299299
"default": false
300+
},
301+
"sentry-release": {
302+
"hasValue": true,
303+
"description": "Sentry release for source map upload"
304+
},
305+
"sentryRelease": {
306+
"hasValue": true,
307+
"description": "Sentry release for source map upload"
308+
},
309+
"sentry-dist": {
310+
"hasValue": true,
311+
"description": "Sentry dist for source map upload"
312+
},
313+
"sentryDist": {
314+
"hasValue": true,
315+
"description": "Sentry dist for source map upload"
316+
},
317+
"sentry-flavor": {
318+
"hasValue": true,
319+
"description": "Android product flavor or variant for native Sentry release/dist"
320+
},
321+
"sentryFlavor": {
322+
"hasValue": true,
323+
"description": "Android product flavor or variant for native Sentry release/dist"
300324
}
301325
}
302326
},

src/bundle-runner.ts

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import {
99
spawnJavaScript,
1010
spawnJavaScriptSync,
1111
} from './utils/runtime';
12+
import {
13+
resolveSentryReleaseAndDist,
14+
type SentryReleaseOptions,
15+
} from './utils/sentry-release';
1216

1317
const g2js = require('gradle-to-js/lib/parser');
1418
const properties = require('properties');
@@ -55,6 +59,23 @@ type ResolvedExpoCli = {
5559
usingExpo: boolean;
5660
};
5761

62+
type SentryUploadArtifacts = {
63+
bundlePath: string;
64+
sourcemapPath: string;
65+
};
66+
67+
type BuildSentrySourcemapsUploadArgsOptions = {
68+
sentryCliPath: string;
69+
bundlePath: string;
70+
sourcemapPath: string;
71+
release: string;
72+
dist?: string;
73+
stripPrefix?: string;
74+
useStandaloneSourcemapsCommand?: boolean;
75+
};
76+
77+
const ANDROID_SENTRY_BUNDLE_NAME = 'index.android.bundle';
78+
5879
export function hasProjectDependency(
5980
dependencyName: string,
6081
projectRoot = process.cwd(),
@@ -557,27 +578,59 @@ export async function copyDebugidForSentry(
557578
fs.removeSync(path.join(outputFolder, `${bundleName}.txt.map`));
558579
}
559580

560-
export function buildSentrySourcemapsUploadArgs(
561-
sentryCliPath: string,
581+
export function prepareSentryUploadArtifacts(
562582
bundleName: string,
563583
outputFolder: string,
564-
version: string,
584+
platform: string,
585+
): SentryUploadArtifacts {
586+
const bundlePath = path.join(outputFolder, bundleName);
587+
const sourcemapPath = path.join(outputFolder, `${bundleName}.map`);
588+
589+
if (platform !== 'android' || bundleName === ANDROID_SENTRY_BUNDLE_NAME) {
590+
return {
591+
bundlePath,
592+
sourcemapPath,
593+
};
594+
}
595+
596+
const androidBundlePath = path.join(outputFolder, ANDROID_SENTRY_BUNDLE_NAME);
597+
const androidSourcemapPath = path.join(
598+
outputFolder,
599+
`${ANDROID_SENTRY_BUNDLE_NAME}.map`,
600+
);
601+
fs.copyFileSync(bundlePath, androidBundlePath);
602+
603+
const sourcemap = JSON.parse(
604+
fs.readFileSync(sourcemapPath, 'utf8'),
605+
) as Record<string, unknown>;
606+
sourcemap.file = ANDROID_SENTRY_BUNDLE_NAME;
607+
fs.writeFileSync(androidSourcemapPath, JSON.stringify(sourcemap));
608+
609+
return {
610+
bundlePath: androidBundlePath,
611+
sourcemapPath: androidSourcemapPath,
612+
};
613+
}
614+
615+
export function buildSentrySourcemapsUploadArgs({
616+
sentryCliPath,
617+
bundlePath,
618+
sourcemapPath,
619+
release,
620+
dist,
621+
stripPrefix = process.cwd(),
565622
useStandaloneSourcemapsCommand = true,
566-
): string[] {
567-
const uploadArgs = [
568-
'--strip-prefix',
569-
path.join(process.cwd(), outputFolder),
570-
path.join(outputFolder, bundleName),
571-
path.join(outputFolder, `${bundleName}.map`),
572-
];
623+
}: BuildSentrySourcemapsUploadArgsOptions): string[] {
624+
const uploadArgs = ['--strip-prefix', stripPrefix, bundlePath, sourcemapPath];
573625

574626
if (!useStandaloneSourcemapsCommand) {
575627
return [
576628
sentryCliPath,
577629
'releases',
578630
'files',
579-
version,
631+
release,
580632
'upload-sourcemaps',
633+
...(dist ? ['--dist', dist] : []),
581634
...uploadArgs,
582635
];
583636
}
@@ -587,7 +640,8 @@ export function buildSentrySourcemapsUploadArgs(
587640
'sourcemaps',
588641
'upload',
589642
'--release',
590-
version,
643+
release,
644+
...(dist ? ['--dist', dist] : []),
591645
...uploadArgs,
592646
];
593647
}
@@ -607,6 +661,8 @@ export async function uploadSourcemapForSentry(
607661
outputFolder: string,
608662
sourcemapOutput: string,
609663
version: string,
664+
platform = '',
665+
sentryOptions: SentryReleaseOptions = {},
610666
): Promise<void> {
611667
if (!sourcemapOutput) {
612668
return;
@@ -626,27 +682,40 @@ export async function uploadSourcemapForSentry(
626682
return;
627683
}
628684

685+
const { release, dist } = await resolveSentryReleaseAndDist(
686+
platform,
687+
version,
688+
sentryOptions,
689+
);
690+
const { bundlePath, sourcemapPath } = prepareSentryUploadArtifacts(
691+
bundleName,
692+
outputFolder,
693+
platform,
694+
);
695+
629696
assertSuccessfulSyncProcess(
630697
spawnJavaScriptSync(
631-
[sentryCliPath, 'releases', 'set-commits', version, '--auto'],
698+
[sentryCliPath, 'releases', 'set-commits', release, '--auto'],
632699
{
633700
stdio: 'inherit',
634701
},
635702
),
636703
sentryCliPath,
637704
);
638-
console.log(t('sentryReleaseCreated', { version }));
705+
console.log(t('sentryReleaseCreated', { version: release }));
639706

640707
console.log(t('uploadingSourcemap'));
641708
assertSuccessfulSyncProcess(
642709
spawnJavaScriptSync(
643-
buildSentrySourcemapsUploadArgs(
710+
buildSentrySourcemapsUploadArgs({
644711
sentryCliPath,
645-
bundleName,
646-
outputFolder,
647-
version,
648-
supportsStandaloneSentrySourcemapsUpload(sentryCliPath),
649-
),
712+
bundlePath,
713+
sourcemapPath,
714+
release,
715+
dist,
716+
useStandaloneSourcemapsCommand:
717+
supportsStandaloneSentrySourcemapsUpload(sentryCliPath),
718+
}),
650719
{
651720
stdio: 'inherit',
652721
},

src/bundle.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
getOptionalStringOption,
1919
getStringOption,
2020
} from './utils/options';
21+
import type { SentryReleaseOptions } from './utils/sentry-release';
2122
import { versionCommands } from './versions';
2223

2324
type NormalizedBundleOptions = {
@@ -41,6 +42,9 @@ type NormalizedBundleOptions = {
4142
packageVersionRange?: string;
4243
rollout?: string;
4344
dryRun: boolean;
45+
sentryRelease?: string;
46+
sentryDist?: string;
47+
sentryFlavor?: string;
4448
};
4549

4650
type PublishBundlePayload = {
@@ -56,6 +60,17 @@ type PublishBundlePayload = {
5660
dryRun?: boolean;
5761
};
5862

63+
function getAliasedOptionalStringOption(
64+
options: Record<string, unknown>,
65+
key: string,
66+
alias: string,
67+
): string | undefined {
68+
return (
69+
getOptionalStringOption(options, key) ??
70+
getOptionalStringOption(options, alias)
71+
);
72+
}
73+
5974
function normalizeBundleOptions(
6075
translatedOptions: Record<string, unknown>,
6176
platform: string,
@@ -105,6 +120,21 @@ function normalizeBundleOptions(
105120
),
106121
rollout: getOptionalStringOption(translatedOptions, 'rollout'),
107122
dryRun: getBooleanOption(translatedOptions, 'dryRun', false),
123+
sentryRelease: getAliasedOptionalStringOption(
124+
translatedOptions,
125+
'sentry-release',
126+
'sentryRelease',
127+
),
128+
sentryDist: getAliasedOptionalStringOption(
129+
translatedOptions,
130+
'sentry-dist',
131+
'sentryDist',
132+
),
133+
sentryFlavor: getAliasedOptionalStringOption(
134+
translatedOptions,
135+
'sentry-flavor',
136+
'sentryFlavor',
137+
),
108138
};
109139
}
110140

@@ -114,6 +144,8 @@ async function uploadSentryArtifactsIfNeeded(
114144
intermediaDir: string,
115145
sourcemapOutput: string,
116146
versionName: string,
147+
platform: Platform,
148+
sentryOptions: SentryReleaseOptions,
117149
): Promise<void> {
118150
if (!shouldUpload) {
119151
return;
@@ -125,6 +157,8 @@ async function uploadSentryArtifactsIfNeeded(
125157
intermediaDir,
126158
sourcemapOutput,
127159
versionName,
160+
platform,
161+
sentryOptions,
128162
);
129163
}
130164

@@ -217,6 +251,12 @@ export const bundleCommands = {
217251
normalized.intermediaDir,
218252
sourcemapOutput,
219253
versionName,
254+
platform,
255+
{
256+
sentryRelease: normalized.sentryRelease,
257+
sentryDist: normalized.sentryDist,
258+
sentryFlavor: normalized.sentryFlavor,
259+
},
220260
);
221261
return;
222262
}
@@ -235,6 +275,12 @@ export const bundleCommands = {
235275
normalized.intermediaDir,
236276
sourcemapOutput,
237277
versionName,
278+
platform,
279+
{
280+
sentryRelease: normalized.sentryRelease,
281+
sentryDist: normalized.sentryDist,
282+
sentryFlavor: normalized.sentryFlavor,
283+
},
238284
);
239285
}
240286
}

src/provider.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ export class CLIProviderImpl implements CLIProvider {
9090
expo: options.expo || false,
9191
rncli: options.rncli || false,
9292
hermes: options.hermes || false,
93+
sentryRelease: options.sentryRelease,
94+
sentryDist: options.sentryDist,
95+
sentryFlavor: options.sentryFlavor,
9396
});
9497

9598
const { bundleCommands } = await import('./bundle');

src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ export interface BundleOptions {
5656
expo?: boolean;
5757
rncli?: boolean;
5858
hermes?: boolean;
59+
sentryRelease?: string;
60+
sentryDist?: string;
61+
sentryFlavor?: string;
5962
}
6063

6164
export interface PublishOptions {

0 commit comments

Comments
 (0)