@sentry/wizard 2.6.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -2
- package/dist/NextJs/configs/next.config.template.js +1 -1
- package/dist/bin.js +7 -3
- package/dist/bin.js.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/Constants.js +1 -1
- package/dist/lib/Constants.js.map +1 -1
- package/dist/lib/Helper/BottomBar.js +2 -2
- package/dist/lib/Helper/BottomBar.js.map +1 -1
- package/dist/lib/Helper/File.js +10 -12
- package/dist/lib/Helper/File.js.map +1 -1
- package/dist/lib/Helper/Logging.js +1 -1
- package/dist/lib/Helper/Logging.js.map +1 -1
- package/dist/lib/Helper/Package.d.ts +1 -0
- package/dist/lib/Helper/Package.js +46 -0
- package/dist/lib/Helper/Package.js.map +1 -0
- package/dist/lib/Helper/PackageManager.d.ts +22 -0
- package/dist/lib/Helper/PackageManager.js +132 -0
- package/dist/lib/Helper/PackageManager.js.map +1 -0
- package/dist/lib/Helper/SentryCli.d.ts +3 -3
- package/dist/lib/Helper/SentryCli.js +3 -3
- package/dist/lib/Helper/SentryCli.js.map +1 -1
- package/dist/lib/Helper/Wizard.d.ts +4 -4
- package/dist/lib/Helper/Wizard.js +13 -12
- package/dist/lib/Helper/Wizard.js.map +1 -1
- package/dist/lib/Helper/__tests__/File.js +5 -5
- package/dist/lib/Helper/__tests__/File.js.map +1 -1
- package/dist/lib/Helper/__tests__/MergeConfig.js +29 -18
- package/dist/lib/Helper/__tests__/MergeConfig.js.map +1 -1
- package/dist/lib/Helper/__tests__/SentryCli.js.map +1 -1
- package/dist/lib/Setup.js +11 -9
- package/dist/lib/Setup.js.map +1 -1
- package/dist/lib/Steps/BaseStep.d.ts +2 -2
- package/dist/lib/Steps/BaseStep.js +3 -3
- package/dist/lib/Steps/BaseStep.js.map +1 -1
- package/dist/lib/Steps/ChooseIntegration.d.ts +1 -1
- package/dist/lib/Steps/ChooseIntegration.js +7 -5
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/lib/Steps/ConfigureProject.d.ts +1 -1
- package/dist/lib/Steps/ConfigureProject.js +5 -3
- package/dist/lib/Steps/ConfigureProject.js.map +1 -1
- package/dist/lib/Steps/Initial.d.ts +1 -1
- package/dist/lib/Steps/Initial.js +6 -4
- package/dist/lib/Steps/Initial.js.map +1 -1
- package/dist/lib/Steps/Integrations/BaseIntegration.d.ts +2 -2
- package/dist/lib/Steps/Integrations/BaseIntegration.js +5 -4
- package/dist/lib/Steps/Integrations/BaseIntegration.js.map +1 -1
- package/dist/lib/Steps/Integrations/Cordova.d.ts +2 -2
- package/dist/lib/Steps/Integrations/Cordova.js +12 -10
- package/dist/lib/Steps/Integrations/Cordova.js.map +1 -1
- package/dist/lib/Steps/Integrations/Electron.d.ts +2 -2
- package/dist/lib/Steps/Integrations/Electron.js +23 -21
- package/dist/lib/Steps/Integrations/Electron.js.map +1 -1
- package/dist/lib/Steps/Integrations/MobileProject.d.ts +1 -1
- package/dist/lib/Steps/Integrations/MobileProject.js +7 -5
- package/dist/lib/Steps/Integrations/MobileProject.js.map +1 -1
- package/dist/lib/Steps/Integrations/NextJs.d.ts +2 -7
- package/dist/lib/Steps/Integrations/NextJs.js +59 -134
- package/dist/lib/Steps/Integrations/NextJs.js.map +1 -1
- package/dist/lib/Steps/Integrations/ReactNative.d.ts +9 -2
- package/dist/lib/Steps/Integrations/ReactNative.js +148 -78
- package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js +43 -1
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js.map +1 -1
- package/dist/lib/Steps/OpenSentry.d.ts +1 -1
- package/dist/lib/Steps/OpenSentry.js +19 -17
- package/dist/lib/Steps/OpenSentry.js.map +1 -1
- package/dist/lib/Steps/PromptForParameters.d.ts +1 -1
- package/dist/lib/Steps/PromptForParameters.js +21 -19
- package/dist/lib/Steps/PromptForParameters.js.map +1 -1
- package/dist/lib/Steps/Result.d.ts +1 -1
- package/dist/lib/Steps/Result.js +7 -5
- package/dist/lib/Steps/Result.js.map +1 -1
- package/dist/lib/Steps/SentryProjectSelector.d.ts +1 -1
- package/dist/lib/Steps/SentryProjectSelector.js +6 -4
- package/dist/lib/Steps/SentryProjectSelector.js.map +1 -1
- package/dist/lib/Steps/ShouldConfigure.d.ts +1 -1
- package/dist/lib/Steps/ShouldConfigure.js +5 -3
- package/dist/lib/Steps/ShouldConfigure.js.map +1 -1
- package/dist/lib/Steps/WaitForSentry.d.ts +1 -1
- package/dist/lib/Steps/WaitForSentry.js +11 -9
- package/dist/lib/Steps/WaitForSentry.js.map +1 -1
- package/dist/lib/Steps/Welcome.d.ts +1 -1
- package/dist/lib/Steps/Welcome.js +7 -6
- package/dist/lib/Steps/Welcome.js.map +1 -1
- package/dist/lib/Steps/index.js +1 -0
- package/dist/lib/Steps/index.js.map +1 -1
- package/dist/lib/__tests__/Env.js +1 -1
- package/dist/lib/__tests__/Env.js.map +1 -1
- package/dist/lib/__tests__/Setup.js +16 -1
- package/dist/lib/__tests__/Setup.js.map +1 -1
- package/lib/Helper/File.ts +2 -8
- package/lib/Helper/Package.ts +61 -0
- package/lib/Helper/PackageManager.ts +64 -0
- package/lib/Helper/SentryCli.ts +3 -3
- package/lib/Helper/Wizard.ts +7 -6
- package/lib/Helper/__tests__/File.ts +5 -5
- package/lib/Helper/__tests__/MergeConfig.ts +36 -20
- package/lib/Helper/__tests__/SentryCli.ts +3 -2
- package/lib/Helper/test-fixtures/next.config.1-merged.js +1 -1
- package/lib/Helper/test-fixtures/next.config.3-merged.js +1 -1
- package/lib/Helper/test-fixtures/next.config.4-merged.js +1 -1
- package/lib/Steps/BaseStep.ts +3 -3
- package/lib/Steps/ChooseIntegration.ts +2 -1
- package/lib/Steps/ConfigureProject.ts +1 -1
- package/lib/Steps/Initial.ts +1 -1
- package/lib/Steps/Integrations/BaseIntegration.ts +3 -3
- package/lib/Steps/Integrations/Cordova.ts +5 -5
- package/lib/Steps/Integrations/Electron.ts +7 -6
- package/lib/Steps/Integrations/MobileProject.ts +2 -1
- package/lib/Steps/Integrations/NextJs.ts +15 -114
- package/lib/Steps/Integrations/ReactNative.ts +143 -52
- package/lib/Steps/Integrations/__tests__/ReactNative.ts +37 -2
- package/lib/Steps/OpenSentry.ts +1 -1
- package/lib/Steps/PromptForParameters.ts +3 -2
- package/lib/Steps/Result.ts +1 -1
- package/lib/Steps/SentryProjectSelector.ts +3 -2
- package/lib/Steps/ShouldConfigure.ts +1 -1
- package/lib/Steps/WaitForSentry.ts +3 -3
- package/lib/Steps/Welcome.ts +1 -1
- package/lib/__tests__/Setup.ts +23 -0
- package/package.json +9 -13
- package/scripts/NextJs/configs/next.config.template.js +1 -1
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
2
|
import Chalk from 'chalk';
|
|
3
|
-
import { exec } from 'child_process';
|
|
4
3
|
import * as fs from 'fs';
|
|
5
|
-
import { Answers
|
|
4
|
+
import type { Answers } from 'inquirer';
|
|
5
|
+
import { prompt } from 'inquirer';
|
|
6
6
|
import * as _ from 'lodash';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import { satisfies, subset, valid, validRange } from 'semver';
|
|
9
|
-
import { promisify } from 'util';
|
|
10
8
|
|
|
11
|
-
import { Args } from '../../Constants';
|
|
9
|
+
import type { Args } from '../../Constants';
|
|
12
10
|
import { debug, green, l, nl, red } from '../../Helper/Logging';
|
|
13
11
|
import { mergeConfigFile } from '../../Helper/MergeConfig';
|
|
14
|
-
import {
|
|
12
|
+
import { checkPackageVersion } from '../../Helper/Package';
|
|
13
|
+
import { getPackageMangerChoice } from '../../Helper/PackageManager';
|
|
14
|
+
import type { SentryCliProps } from '../../Helper/SentryCli';
|
|
15
|
+
import { SentryCli } from '../../Helper/SentryCli';
|
|
15
16
|
import { BaseIntegration } from './BaseIntegration';
|
|
16
17
|
|
|
17
|
-
type PackageManager = 'yarn' | 'npm' | 'pnpm';
|
|
18
|
-
|
|
19
18
|
const COMPATIBLE_NEXTJS_VERSIONS = '>=10.0.8 <14.0.0';
|
|
20
19
|
const COMPATIBLE_SDK_VERSIONS = '>=7.3.0';
|
|
21
20
|
const PROPERTIES_FILENAME = 'sentry.properties';
|
|
@@ -45,7 +44,7 @@ try {
|
|
|
45
44
|
export class NextJs extends BaseIntegration {
|
|
46
45
|
protected _sentryCli: SentryCli;
|
|
47
46
|
|
|
48
|
-
constructor(protected _argv: Args) {
|
|
47
|
+
public constructor(protected _argv: Args) {
|
|
49
48
|
super(_argv);
|
|
50
49
|
this._sentryCli = new SentryCli(this._argv);
|
|
51
50
|
}
|
|
@@ -110,24 +109,26 @@ export class NextJs extends BaseIntegration {
|
|
|
110
109
|
nl();
|
|
111
110
|
|
|
112
111
|
let userAnswers: Answers = { continue: true };
|
|
113
|
-
const hasCompatibleNextjsVersion =
|
|
112
|
+
const hasCompatibleNextjsVersion = checkPackageVersion(
|
|
113
|
+
appPackage,
|
|
114
114
|
'next',
|
|
115
115
|
COMPATIBLE_NEXTJS_VERSIONS,
|
|
116
116
|
true,
|
|
117
117
|
);
|
|
118
118
|
|
|
119
|
-
const packageManager =
|
|
119
|
+
const packageManager = getPackageMangerChoice();
|
|
120
120
|
const hasSdkInstalled = this._hasPackageInstalled('@sentry/nextjs');
|
|
121
121
|
|
|
122
122
|
let hasCompatibleSdkVersion = false;
|
|
123
123
|
// if no package but we have nextjs, let's add it if we can
|
|
124
124
|
if (!hasSdkInstalled && packageManager && hasCompatibleNextjsVersion) {
|
|
125
|
-
await
|
|
125
|
+
await packageManager.installPackage('@sentry/nextjs');
|
|
126
126
|
// can assume it's compatible since we just installed it
|
|
127
127
|
hasCompatibleSdkVersion = true;
|
|
128
128
|
} else {
|
|
129
129
|
// otherwise, let's check the version and spit out the appropriate error
|
|
130
|
-
hasCompatibleSdkVersion =
|
|
130
|
+
hasCompatibleSdkVersion = checkPackageVersion(
|
|
131
|
+
appPackage,
|
|
131
132
|
'@sentry/nextjs',
|
|
132
133
|
COMPATIBLE_SDK_VERSIONS,
|
|
133
134
|
true,
|
|
@@ -206,7 +207,7 @@ export class NextJs extends BaseIntegration {
|
|
|
206
207
|
`./${PROPERTIES_FILENAME}`,
|
|
207
208
|
this._sentryCli.dumpProperties(cliPropsToWrite),
|
|
208
209
|
);
|
|
209
|
-
green(
|
|
210
|
+
green('✓ Successfully created sentry.properties');
|
|
210
211
|
} catch {
|
|
211
212
|
red(`⚠ Could not add org and project data to ${PROPERTIES_FILENAME}`);
|
|
212
213
|
l(
|
|
@@ -348,106 +349,6 @@ export class NextJs extends BaseIntegration {
|
|
|
348
349
|
return !!depsVersion || !!devDepsVersion;
|
|
349
350
|
}
|
|
350
351
|
|
|
351
|
-
private _getPackageMangerChoice(): PackageManager | null {
|
|
352
|
-
if (fs.existsSync(path.join(process.cwd(), 'yarn.lock'))) {
|
|
353
|
-
return 'yarn';
|
|
354
|
-
}
|
|
355
|
-
if (fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml'))) {
|
|
356
|
-
return 'pnpm';
|
|
357
|
-
}
|
|
358
|
-
if (fs.existsSync(path.join(process.cwd(), 'package-lock.json'))) {
|
|
359
|
-
return 'npm';
|
|
360
|
-
}
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
private _getInstallCommand(packageManager: PackageManager): string {
|
|
365
|
-
switch (packageManager) {
|
|
366
|
-
case 'yarn':
|
|
367
|
-
return 'yarn add';
|
|
368
|
-
case 'pnpm':
|
|
369
|
-
return 'pnpm add';
|
|
370
|
-
case 'npm':
|
|
371
|
-
return 'npm install';
|
|
372
|
-
default:
|
|
373
|
-
throw new Error(`Unknown package manager: ${packageManager}`);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
private async _installPackage(
|
|
378
|
-
packageName: string,
|
|
379
|
-
packageManager: PackageManager,
|
|
380
|
-
): Promise<void> {
|
|
381
|
-
const command = this._getInstallCommand(packageManager);
|
|
382
|
-
await promisify(exec)(`${command} ${packageName}`);
|
|
383
|
-
green(`✓ Added \`${packageName}\` using \`${command}\`.`);
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
private _checkPackageVersion(
|
|
388
|
-
packageName: string,
|
|
389
|
-
acceptableVersions: string,
|
|
390
|
-
canBeLatest: boolean,
|
|
391
|
-
): boolean {
|
|
392
|
-
const depsVersion = _.get(appPackage, ['dependencies', packageName]);
|
|
393
|
-
const devDepsVersion = _.get(appPackage, ['devDependencies', packageName]);
|
|
394
|
-
|
|
395
|
-
if (!depsVersion && !devDepsVersion) {
|
|
396
|
-
red(`✗ ${packageName} isn't in your dependencies.`);
|
|
397
|
-
red(' Please install it with yarn/npm.');
|
|
398
|
-
return false;
|
|
399
|
-
} else if (
|
|
400
|
-
!this._fulfillsVersionRange(
|
|
401
|
-
depsVersion,
|
|
402
|
-
acceptableVersions,
|
|
403
|
-
canBeLatest,
|
|
404
|
-
) &&
|
|
405
|
-
!this._fulfillsVersionRange(
|
|
406
|
-
devDepsVersion,
|
|
407
|
-
acceptableVersions,
|
|
408
|
-
canBeLatest,
|
|
409
|
-
)
|
|
410
|
-
) {
|
|
411
|
-
red(
|
|
412
|
-
`✗ Your \`package.json\` specifies a version of \`${packageName}\` outside of the compatible version range ${acceptableVersions}.\n`,
|
|
413
|
-
);
|
|
414
|
-
return false;
|
|
415
|
-
} else {
|
|
416
|
-
green(
|
|
417
|
-
`✓ A compatible version of \`${packageName}\` is specified in \`package.json\`.`,
|
|
418
|
-
);
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
private _fulfillsVersionRange(
|
|
424
|
-
version: string,
|
|
425
|
-
acceptableVersions: string,
|
|
426
|
-
canBeLatest: boolean,
|
|
427
|
-
): boolean {
|
|
428
|
-
if (version === 'latest') {
|
|
429
|
-
return canBeLatest;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
let cleanedUserVersion, isRange;
|
|
433
|
-
|
|
434
|
-
if (valid(version)) {
|
|
435
|
-
cleanedUserVersion = valid(version);
|
|
436
|
-
isRange = false;
|
|
437
|
-
} else if (validRange(version)) {
|
|
438
|
-
cleanedUserVersion = validRange(version);
|
|
439
|
-
isRange = true;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
return (
|
|
443
|
-
// If the given version is a bogus format, this will still be undefined and we'll automatically reject it
|
|
444
|
-
!!cleanedUserVersion &&
|
|
445
|
-
(isRange
|
|
446
|
-
? subset(cleanedUserVersion, acceptableVersions)
|
|
447
|
-
: satisfies(cleanedUserVersion, acceptableVersions))
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
352
|
private _spliceInPlace(
|
|
452
353
|
arr: Array<any>,
|
|
453
354
|
start: number,
|
|
@@ -1,19 +1,37 @@
|
|
|
1
1
|
/* eslint-disable max-lines */
|
|
2
|
+
import { exec } from 'child_process';
|
|
2
3
|
import * as fs from 'fs';
|
|
3
|
-
import { Answers
|
|
4
|
+
import type { Answers} from 'inquirer';
|
|
5
|
+
import { prompt } from 'inquirer';
|
|
4
6
|
import * as _ from 'lodash';
|
|
5
7
|
import * as path from 'path';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
|
|
10
|
+
import type { Args } from '../../Constants';
|
|
11
|
+
import {
|
|
12
|
+
exists,
|
|
13
|
+
matchesContent,
|
|
14
|
+
matchFiles,
|
|
15
|
+
patchMatchingFile,
|
|
16
|
+
} from '../../Helper/File';
|
|
17
|
+
import { dim, green, nl, red } from '../../Helper/Logging';
|
|
18
|
+
import { checkPackageVersion } from '../../Helper/Package';
|
|
19
|
+
import { getPackageMangerChoice } from '../../Helper/PackageManager';
|
|
10
20
|
import { SentryCli } from '../../Helper/SentryCli';
|
|
11
21
|
import { MobileProject } from './MobileProject';
|
|
12
22
|
|
|
13
23
|
const xcode = require('xcode');
|
|
14
24
|
|
|
15
|
-
export
|
|
25
|
+
export const COMPATIBLE_REACT_NATIVE_VERSIONS = '>=0.69.0';
|
|
26
|
+
export const COMPATIBLE_SDK_VERSION = '>= 5.0.0';
|
|
27
|
+
|
|
28
|
+
export const SENTRY_REACT_NATIVE_PACKAGE = '@sentry/react-native';
|
|
29
|
+
export const REACT_NATIVE_PACKAGE = 'react-native';
|
|
16
30
|
|
|
31
|
+
export const DOCS_MANUAL_STEPS =
|
|
32
|
+
'https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/';
|
|
33
|
+
|
|
34
|
+
export class ReactNative extends MobileProject {
|
|
17
35
|
/**
|
|
18
36
|
* All React Native versions have app/build.gradle with android section.
|
|
19
37
|
*/
|
|
@@ -22,7 +40,7 @@ export class ReactNative extends MobileProject {
|
|
|
22
40
|
protected _answers: Answers;
|
|
23
41
|
protected _sentryCli: SentryCli;
|
|
24
42
|
|
|
25
|
-
constructor(protected _argv: Args) {
|
|
43
|
+
public constructor(protected _argv: Args) {
|
|
26
44
|
super(_argv);
|
|
27
45
|
this._sentryCli = new SentryCli(this._argv);
|
|
28
46
|
}
|
|
@@ -34,43 +52,90 @@ export class ReactNative extends MobileProject {
|
|
|
34
52
|
if (!(await this.shouldEmit(answers))) {
|
|
35
53
|
return {};
|
|
36
54
|
}
|
|
55
|
+
nl();
|
|
56
|
+
|
|
57
|
+
let userAnswers: Answers = { continue: true };
|
|
58
|
+
const packageManager = getPackageMangerChoice();
|
|
59
|
+
|
|
60
|
+
const hasCompatibleReactNativeVersion = checkPackageVersion(
|
|
61
|
+
this._readAppPackage(),
|
|
62
|
+
REACT_NATIVE_PACKAGE,
|
|
63
|
+
COMPATIBLE_REACT_NATIVE_VERSIONS,
|
|
64
|
+
true,
|
|
65
|
+
);
|
|
66
|
+
if (!hasCompatibleReactNativeVersion && !this._argv.quiet) {
|
|
67
|
+
userAnswers = await prompt({
|
|
68
|
+
message: 'Your version of React Native is not compatible with Sentry\'s React Native SDK. Do you want to continue?',
|
|
69
|
+
name: 'continue',
|
|
70
|
+
default: false,
|
|
71
|
+
type: 'confirm',
|
|
72
|
+
});
|
|
73
|
+
nl();
|
|
74
|
+
}
|
|
75
|
+
if (!userAnswers.continue) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Please upgrade to a version that is compatible with ${COMPATIBLE_REACT_NATIVE_VERSIONS}. Or use ${DOCS_MANUAL_STEPS}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (packageManager) {
|
|
82
|
+
await packageManager.installPackage(SENTRY_REACT_NATIVE_PACKAGE);
|
|
83
|
+
}
|
|
84
|
+
const hasCompatibleSentryReactNativeVersion = checkPackageVersion(
|
|
85
|
+
this._readAppPackage(),
|
|
86
|
+
SENTRY_REACT_NATIVE_PACKAGE,
|
|
87
|
+
COMPATIBLE_SDK_VERSION,
|
|
88
|
+
true,
|
|
89
|
+
);
|
|
90
|
+
if (!hasCompatibleSentryReactNativeVersion && !this._argv.quiet) {
|
|
91
|
+
userAnswers = await prompt({
|
|
92
|
+
message: `Your version of ${SENTRY_REACT_NATIVE_PACKAGE} is not compatible with this wizard. Do you want to continue?`,
|
|
93
|
+
name: 'continue',
|
|
94
|
+
default: false,
|
|
95
|
+
type: 'confirm',
|
|
96
|
+
});
|
|
97
|
+
nl();
|
|
98
|
+
}
|
|
99
|
+
if (!userAnswers.continue) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Please upgrade to a version that is compatible with ${COMPATIBLE_SDK_VERSION}.`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
37
104
|
|
|
38
105
|
const sentryCliProperties = this._sentryCli.convertAnswersToProperties(
|
|
39
106
|
answers,
|
|
40
107
|
);
|
|
41
108
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
await this._patchJsSentryInit(platform, answers);
|
|
61
|
-
await this._addSentryProperties(platform, sentryCliProperties);
|
|
62
|
-
dim(`✅ Added sentry.properties file to ${platform}`);
|
|
63
|
-
|
|
64
|
-
green(`Successfully set up ${platform} for react-native`);
|
|
65
|
-
} catch (e) {
|
|
66
|
-
red(e);
|
|
109
|
+
const promises = this.getPlatforms(answers).map(
|
|
110
|
+
async (platform: string) => {
|
|
111
|
+
try {
|
|
112
|
+
if (platform === 'ios') {
|
|
113
|
+
await patchMatchingFile(
|
|
114
|
+
'ios/*.xcodeproj/project.pbxproj',
|
|
115
|
+
this._patchXcodeProj.bind(this),
|
|
116
|
+
);
|
|
117
|
+
green('✓ Patched build script in Xcode project.');
|
|
118
|
+
await this._podInstall();
|
|
119
|
+
green('✓ Pods installed.');
|
|
120
|
+
} else {
|
|
121
|
+
await patchMatchingFile(
|
|
122
|
+
'**/app/build.gradle',
|
|
123
|
+
this._patchBuildGradle.bind(this),
|
|
124
|
+
);
|
|
125
|
+
green('✓ Patched build.gradle file.');
|
|
67
126
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
127
|
+
await this._patchJsSentryInit(platform, answers);
|
|
128
|
+
await this._addSentryProperties(platform, sentryCliProperties);
|
|
129
|
+
green(`✓ Added sentry.properties file to ${platform}`);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
red(e);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await Promise.all(promises);
|
|
137
|
+
|
|
138
|
+
return answers;
|
|
74
139
|
}
|
|
75
140
|
|
|
76
141
|
public async uninstall(_answers: Answers): Promise<Answers> {
|
|
@@ -125,6 +190,24 @@ export class ReactNative extends MobileProject {
|
|
|
125
190
|
return result;
|
|
126
191
|
}
|
|
127
192
|
|
|
193
|
+
private _readAppPackage(): Record<string, unknown> {
|
|
194
|
+
let appPackage: Record<string, unknown> = {};
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
appPackage = JSON.parse(
|
|
198
|
+
fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'),
|
|
199
|
+
);
|
|
200
|
+
} catch {
|
|
201
|
+
// We don't need to have this
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return appPackage;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private async _podInstall(): Promise<void> {
|
|
208
|
+
await promisify(exec)('npx --yes pod-install --non-interactive --quiet');
|
|
209
|
+
}
|
|
210
|
+
|
|
128
211
|
private async _patchJsSentryInit(
|
|
129
212
|
platform: string,
|
|
130
213
|
answers: Answers,
|
|
@@ -144,10 +227,10 @@ export class ReactNative extends MobileProject {
|
|
|
144
227
|
answers,
|
|
145
228
|
platform,
|
|
146
229
|
);
|
|
147
|
-
|
|
230
|
+
green(`✓ Patched ${jsFileToPatch.join(', ')} file(s).`);
|
|
148
231
|
} else {
|
|
149
|
-
|
|
150
|
-
|
|
232
|
+
red(`✗ Could not find ${platformGlob} nor ${universalGlob} files.`);
|
|
233
|
+
red('✗ Please, visit https://docs.sentry.io/platforms/react-native');
|
|
151
234
|
}
|
|
152
235
|
}
|
|
153
236
|
|
|
@@ -211,9 +294,9 @@ export class ReactNative extends MobileProject {
|
|
|
211
294
|
// eslint-disable-next-line prefer-template
|
|
212
295
|
match +
|
|
213
296
|
"\n\nimport * as Sentry from '@sentry/react-native';\n\n" +
|
|
214
|
-
|
|
297
|
+
'Sentry.init({ \n' +
|
|
215
298
|
` dsn: '${dsn}', \n` +
|
|
216
|
-
|
|
299
|
+
'});\n',
|
|
217
300
|
),
|
|
218
301
|
);
|
|
219
302
|
}
|
|
@@ -250,9 +333,7 @@ export class ReactNative extends MobileProject {
|
|
|
250
333
|
private _patchExistingXcodeBuildScripts(buildScripts: any): void {
|
|
251
334
|
for (const script of buildScripts) {
|
|
252
335
|
if (
|
|
253
|
-
!script.shellScript.match(
|
|
254
|
-
/\/scripts\/react-native-xcode\.sh/i,
|
|
255
|
-
) ||
|
|
336
|
+
!script.shellScript.match(/\/scripts\/react-native-xcode\.sh/i) ||
|
|
256
337
|
script.shellScript.match(/sentry-cli\s+react-native\s+xcode/i)
|
|
257
338
|
) {
|
|
258
339
|
continue;
|
|
@@ -266,7 +347,7 @@ export class ReactNative extends MobileProject {
|
|
|
266
347
|
'$REACT_NATIVE_XCODE',
|
|
267
348
|
() =>
|
|
268
349
|
// eslint-disable-next-line no-useless-escape
|
|
269
|
-
'
|
|
350
|
+
'\\"../node_modules/@sentry/cli/bin/sentry-cli react-native xcode $REACT_NATIVE_XCODE\\"',
|
|
270
351
|
) +
|
|
271
352
|
'\n/bin/sh ../node_modules/@sentry/react-native/scripts/collect-modules.sh\n';
|
|
272
353
|
script.shellScript = JSON.stringify(code);
|
|
@@ -275,7 +356,11 @@ export class ReactNative extends MobileProject {
|
|
|
275
356
|
|
|
276
357
|
private _addNewXcodeBuildPhaseForSymbols(buildScripts: any, proj: any): void {
|
|
277
358
|
for (const script of buildScripts) {
|
|
278
|
-
if (
|
|
359
|
+
if (
|
|
360
|
+
script.shellScript.match(
|
|
361
|
+
/sentry-cli\s+(upload-dsym|debug-files upload)/,
|
|
362
|
+
)
|
|
363
|
+
) {
|
|
279
364
|
return;
|
|
280
365
|
}
|
|
281
366
|
}
|
|
@@ -287,7 +372,7 @@ export class ReactNative extends MobileProject {
|
|
|
287
372
|
null,
|
|
288
373
|
{
|
|
289
374
|
shellPath: '/bin/sh',
|
|
290
|
-
shellScript
|
|
375
|
+
shellScript: `
|
|
291
376
|
export SENTRY_PROPERTIES=sentry.properties
|
|
292
377
|
[[ $SENTRY_INCLUDE_NATIVE_SOURCES == "true" ]] && INCLUDE_SOURCES_FLAG="--include-sources" || INCLUDE_SOURCES_FLAG=""
|
|
293
378
|
../node_modules/@sentry/cli/bin/sentry-cli debug-files upload "$INCLUDE_SOURCES_FLAG" "$DWARF_DSYM_FOLDER_PATH"
|
|
@@ -296,7 +381,10 @@ export SENTRY_PROPERTIES=sentry.properties
|
|
|
296
381
|
);
|
|
297
382
|
}
|
|
298
383
|
|
|
299
|
-
private _patchXcodeProj(
|
|
384
|
+
private _patchXcodeProj(
|
|
385
|
+
contents: string,
|
|
386
|
+
filename: string,
|
|
387
|
+
): Promise<string | undefined> {
|
|
300
388
|
const proj = xcode.project(filename);
|
|
301
389
|
return new Promise((resolve, reject) => {
|
|
302
390
|
proj.parse((err: any) => {
|
|
@@ -338,7 +426,7 @@ export SENTRY_PROPERTIES=sentry.properties
|
|
|
338
426
|
// continue prompt.
|
|
339
427
|
const newContents = proj.writeSync();
|
|
340
428
|
if (newContents === contents) {
|
|
341
|
-
resolve();
|
|
429
|
+
resolve(undefined);
|
|
342
430
|
} else {
|
|
343
431
|
resolve(newContents);
|
|
344
432
|
}
|
|
@@ -371,7 +459,10 @@ export SENTRY_PROPERTIES=sentry.properties
|
|
|
371
459
|
JSON.parse(script.shellScript)
|
|
372
460
|
// remove sentry properties export
|
|
373
461
|
.replace(/^export SENTRY_PROPERTIES=sentry.properties\r?\n/m, '')
|
|
374
|
-
.replace(
|
|
462
|
+
.replace(
|
|
463
|
+
/^\/bin\/sh ..\/node_modules\/@sentry\/react-native\/scripts\/collect-modules.sh\r?\n/m,
|
|
464
|
+
'',
|
|
465
|
+
)
|
|
375
466
|
// unwrap react-native-xcode.sh command. In case someone replaced it
|
|
376
467
|
// entirely with the sentry-cli command we need to put the original
|
|
377
468
|
// version back in.
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
jest.mock('../../../Helper/Logging.ts'); // We mock logging to not pollute the output
|
|
2
|
+
jest.mock('child_process');
|
|
3
|
+
import * as child_process from 'child_process';
|
|
2
4
|
import * as fs from 'fs';
|
|
3
|
-
import { Answers } from 'inquirer';
|
|
5
|
+
import type { Answers } from 'inquirer';
|
|
4
6
|
import * as path from 'path';
|
|
5
7
|
import * as process from 'process';
|
|
6
8
|
import * as rimraf from 'rimraf';
|
|
7
9
|
|
|
8
|
-
import { Args
|
|
10
|
+
import type { Args} from '../../../Constants';
|
|
11
|
+
import { Integration, Platform } from '../../../Constants';
|
|
9
12
|
import { ReactNative } from '../ReactNative';
|
|
10
13
|
|
|
11
14
|
const testDir = 'rn-test';
|
|
12
15
|
const iosIndexJs = 'index.ios.js';
|
|
13
16
|
const appTsx = 'src/App.tsx';
|
|
14
17
|
const appBuildGradle = 'android/app/build.gradle';
|
|
18
|
+
const yarnLock = 'yarn.lock';
|
|
15
19
|
|
|
16
20
|
const dummyJsContent = 'import React from "react";\n';
|
|
17
21
|
const dummyAppBuildGradleContent = 'apply plugin: "com.facebook.react"\n\nandroid {\n}\n';
|
|
@@ -44,6 +48,17 @@ const mockAndroidAnswers: Answers = {
|
|
|
44
48
|
},
|
|
45
49
|
};
|
|
46
50
|
|
|
51
|
+
const originalExec = child_process.exec;
|
|
52
|
+
|
|
53
|
+
const restoreExec = (): void => {
|
|
54
|
+
(child_process as any).exec = originalExec;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const mockExec = (): void => {
|
|
58
|
+
(child_process.exec as unknown as jest.Mock)
|
|
59
|
+
.mockImplementation((_command, callback) => callback(null, { stdout: '' }));
|
|
60
|
+
}
|
|
61
|
+
|
|
47
62
|
describe('ReactNative', () => {
|
|
48
63
|
|
|
49
64
|
const defaultCwd = process.cwd();
|
|
@@ -57,9 +72,12 @@ describe('ReactNative', () => {
|
|
|
57
72
|
fs.writeFileSync(appTsx, dummyJsContent);
|
|
58
73
|
fs.mkdirSync(path.dirname(appBuildGradle), { recursive: true });
|
|
59
74
|
fs.writeFileSync(appBuildGradle, dummyAppBuildGradleContent);
|
|
75
|
+
fs.writeFileSync(yarnLock, '');
|
|
76
|
+
mockExec();
|
|
60
77
|
});
|
|
61
78
|
|
|
62
79
|
afterEach(() => {
|
|
80
|
+
restoreExec();
|
|
63
81
|
process.chdir(defaultCwd);
|
|
64
82
|
rimraf.sync(testDir);
|
|
65
83
|
});
|
|
@@ -81,6 +99,7 @@ describe('ReactNative', () => {
|
|
|
81
99
|
|
|
82
100
|
test('patches android app build gradle file', async () => {
|
|
83
101
|
const project = new ReactNative(testArgs as Args);
|
|
102
|
+
|
|
84
103
|
await project.emit(mockAndroidAnswers);
|
|
85
104
|
|
|
86
105
|
const patchedAppBuildGradle = fs.readFileSync(appBuildGradle, 'utf8');
|
|
@@ -89,4 +108,20 @@ describe('ReactNative', () => {
|
|
|
89
108
|
'android {\n}\n';
|
|
90
109
|
expect(patchedAppBuildGradle).toEqual(expectedPatch);
|
|
91
110
|
});
|
|
111
|
+
|
|
112
|
+
test('does install sentry sdk', async () => {
|
|
113
|
+
const project = new ReactNative(testArgs as Args);
|
|
114
|
+
|
|
115
|
+
await project.emit(mockIosAnswers);
|
|
116
|
+
|
|
117
|
+
expect(child_process.exec).toHaveBeenCalledWith('yarn add @sentry/react-native', expect.anything());
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('executes pod install', async () => {
|
|
121
|
+
const project = new ReactNative(testArgs as Args);
|
|
122
|
+
|
|
123
|
+
await project.emit(mockIosAnswers);
|
|
124
|
+
|
|
125
|
+
expect(child_process.exec).toHaveBeenCalledWith('npx --yes pod-install --non-interactive --quiet', expect.anything());
|
|
126
|
+
});
|
|
92
127
|
});
|
package/lib/Steps/OpenSentry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Answers
|
|
1
|
+
import type { Answers } from 'inquirer';
|
|
2
|
+
import { prompt } from 'inquirer';
|
|
2
3
|
import * as _ from 'lodash';
|
|
3
4
|
|
|
4
5
|
import { dim } from '../Helper/Logging';
|
|
@@ -130,7 +131,7 @@ export class PromptForParameters extends BaseStep {
|
|
|
130
131
|
return 'Please copy the slug from the url, it should be all lowercase';
|
|
131
132
|
}
|
|
132
133
|
if (input.length === 0) {
|
|
133
|
-
return
|
|
134
|
+
return "Can't be empty";
|
|
134
135
|
}
|
|
135
136
|
return true;
|
|
136
137
|
}
|
package/lib/Steps/Result.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Answers
|
|
1
|
+
import type { Answers } from 'inquirer';
|
|
2
|
+
import { prompt } from 'inquirer';
|
|
2
3
|
import * as _ from 'lodash';
|
|
3
4
|
|
|
4
5
|
import { BaseStep } from './BaseStep';
|
|
5
6
|
|
|
6
7
|
function sleep(n: number): Promise<void> {
|
|
7
|
-
return new Promise(resolve => setTimeout(resolve, n));
|
|
8
|
+
return new Promise((resolve) => setTimeout(resolve, n));
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
export class SentryProjectSelector extends BaseStep {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Answers } from 'inquirer';
|
|
1
|
+
import type { Answers } from 'inquirer';
|
|
2
2
|
|
|
3
3
|
import { BottomBar } from '../Helper/BottomBar';
|
|
4
4
|
import { getCurrentIntegration } from '../Helper/Wizard';
|
|
@@ -28,7 +28,7 @@ export class WaitForSentry extends BaseStep {
|
|
|
28
28
|
const response = await r2.get(
|
|
29
29
|
`${baseUrl}api/0/wizard/${answers.hash}/`,
|
|
30
30
|
).response;
|
|
31
|
-
this.debug(
|
|
31
|
+
this.debug('Polling received data');
|
|
32
32
|
if (response.status !== 200) {
|
|
33
33
|
throw new Error(`Received status ${response.status}`);
|
|
34
34
|
}
|
|
@@ -36,7 +36,7 @@ export class WaitForSentry extends BaseStep {
|
|
|
36
36
|
// Delete the wizard hash since we were able to fetch the data
|
|
37
37
|
await r2.delete(`${baseUrl}api/0/wizard/${answers.hash}/`);
|
|
38
38
|
BottomBar.hide();
|
|
39
|
-
this.debug(
|
|
39
|
+
this.debug('Polling Success!');
|
|
40
40
|
resolve({ wizard: data });
|
|
41
41
|
} catch (e) {
|
|
42
42
|
this.debug('Polling received:');
|
package/lib/Steps/Welcome.ts
CHANGED
package/lib/__tests__/Setup.ts
CHANGED
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
jest.mock('../Helper/Logging'); // We mock logging to not pollute the output
|
|
2
|
+
jest.mock('child_process');
|
|
3
|
+
import * as child_process from 'child_process';
|
|
4
|
+
|
|
2
5
|
import { Integration, Platform } from '../Constants';
|
|
3
6
|
import { run } from '../Setup';
|
|
4
7
|
|
|
8
|
+
const originalExec = child_process.exec;
|
|
9
|
+
|
|
10
|
+
const restoreExec = (): void => {
|
|
11
|
+
(child_process as any).exec = originalExec;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const mockExec = (): void => {
|
|
15
|
+
(child_process.exec as unknown as jest.Mock).mockImplementation(
|
|
16
|
+
(_command, callback) => callback(null, { stdout: '' }),
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
5
20
|
describe('Wizard', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
mockExec();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
restoreExec();
|
|
27
|
+
});
|
|
28
|
+
|
|
6
29
|
describe('React Native', () => {
|
|
7
30
|
test('run', () => {
|
|
8
31
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|