@sentry/wizard 3.14.1 → 3.16.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 -4
- package/dist/lib/Steps/ChooseIntegration.js +1 -1
- package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
- package/dist/lib/Steps/Integrations/ReactNative.d.ts +7 -32
- package/dist/lib/Steps/Integrations/ReactNative.js +17 -485
- package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/android/android-wizard.js +13 -18
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/apple/apple-wizard.js +11 -4
- package/dist/src/apple/apple-wizard.js.map +1 -1
- package/dist/src/apple/cocoapod.d.ts +1 -0
- package/dist/src/apple/cocoapod.js +36 -13
- package/dist/src/apple/cocoapod.js.map +1 -1
- package/dist/src/nextjs/nextjs-wizard.js +1 -1
- package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
- package/dist/src/react-native/glob.d.ts +3 -0
- package/dist/src/react-native/glob.js +18 -0
- package/dist/src/react-native/glob.js.map +1 -0
- package/dist/src/react-native/gradle.d.ts +4 -0
- package/dist/src/react-native/gradle.js +49 -0
- package/dist/src/react-native/gradle.js.map +1 -0
- package/dist/src/react-native/javascript.d.ts +8 -0
- package/dist/src/react-native/javascript.js +25 -0
- package/dist/src/react-native/javascript.js.map +1 -0
- package/dist/src/react-native/options.d.ts +4 -0
- package/dist/src/react-native/options.js +3 -0
- package/dist/src/react-native/options.js.map +1 -0
- package/dist/src/react-native/react-native-wizard.d.ts +9 -0
- package/dist/src/react-native/react-native-wizard.js +356 -0
- package/dist/src/react-native/react-native-wizard.js.map +1 -0
- package/dist/src/react-native/uninstall.d.ts +2 -0
- package/dist/src/react-native/uninstall.js +130 -0
- package/dist/src/react-native/uninstall.js.map +1 -0
- package/dist/src/react-native/xcode.d.ts +18 -0
- package/dist/src/react-native/xcode.js +170 -0
- package/dist/src/react-native/xcode.js.map +1 -0
- package/dist/src/remix/codemods/handle-error.js +28 -0
- package/dist/src/remix/codemods/handle-error.js.map +1 -1
- package/dist/src/remix/codemods/root-common.d.ts +2 -0
- package/dist/src/remix/codemods/root-common.js +70 -0
- package/dist/src/remix/codemods/root-common.js.map +1 -0
- package/dist/src/remix/codemods/root-v1.js +5 -36
- package/dist/src/remix/codemods/root-v1.js.map +1 -1
- package/dist/src/remix/codemods/root-v2.js +53 -4
- package/dist/src/remix/codemods/root-v2.js.map +1 -1
- package/dist/src/remix/remix-wizard.js +8 -5
- package/dist/src/remix/remix-wizard.js.map +1 -1
- package/dist/src/remix/sdk-setup.d.ts +1 -0
- package/dist/src/remix/sdk-setup.js +10 -6
- package/dist/src/remix/sdk-setup.js.map +1 -1
- package/dist/src/remix/templates.d.ts +1 -1
- package/dist/src/remix/templates.js +1 -1
- package/dist/src/remix/templates.js.map +1 -1
- package/dist/src/remix/utils.d.ts +2 -0
- package/dist/src/remix/utils.js +6 -1
- package/dist/src/remix/utils.js.map +1 -1
- package/dist/src/sourcemaps/tools/nextjs.js +3 -3
- package/dist/src/sourcemaps/tools/nextjs.js.map +1 -1
- package/dist/src/sourcemaps/tools/sentry-cli.js +1 -1
- package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js +1 -1
- package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +19 -3
- package/dist/src/utils/clack-utils.js +141 -39
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/src/utils/semver.d.ts +5 -0
- package/dist/src/utils/semver.js +27 -0
- package/dist/src/utils/semver.js.map +1 -0
- package/dist/src/utils/sentrycli-utils.js +4 -1
- package/dist/src/utils/sentrycli-utils.js.map +1 -1
- package/dist/src/utils/types.d.ts +3 -0
- package/dist/src/utils/types.js.map +1 -1
- package/dist/test/react-native/gradle.test.js +57 -0
- package/dist/test/react-native/gradle.test.js.map +1 -0
- package/dist/test/react-native/javascript.test.js +47 -0
- package/dist/test/react-native/javascript.test.js.map +1 -0
- package/dist/test/react-native/xcode.test.d.ts +1 -0
- package/dist/test/react-native/xcode.test.js +144 -0
- package/dist/test/react-native/xcode.test.js.map +1 -0
- package/lib/Steps/ChooseIntegration.ts +1 -1
- package/lib/Steps/Integrations/ReactNative.ts +17 -573
- package/package.json +1 -1
- package/src/android/android-wizard.ts +3 -18
- package/src/apple/apple-wizard.ts +12 -3
- package/src/apple/cocoapod.ts +20 -9
- package/src/nextjs/nextjs-wizard.ts +1 -1
- package/src/react-native/glob.ts +13 -0
- package/src/react-native/gradle.ts +26 -0
- package/src/react-native/javascript.ts +33 -0
- package/src/react-native/options.ts +5 -0
- package/src/react-native/react-native-wizard.ts +369 -0
- package/src/react-native/uninstall.ts +107 -0
- package/src/react-native/xcode.ts +228 -0
- package/src/remix/codemods/handle-error.ts +30 -0
- package/src/remix/codemods/root-common.ts +63 -0
- package/src/remix/codemods/root-v1.ts +3 -53
- package/src/remix/codemods/root-v2.ts +71 -2
- package/src/remix/remix-wizard.ts +9 -6
- package/src/remix/sdk-setup.ts +14 -6
- package/src/remix/templates.ts +2 -6
- package/src/remix/utils.ts +5 -0
- package/src/sourcemaps/tools/nextjs.ts +6 -6
- package/src/sourcemaps/tools/sentry-cli.ts +1 -1
- package/src/sveltekit/sveltekit-wizard.ts +1 -1
- package/src/utils/clack-utils.ts +229 -74
- package/src/utils/semver.ts +33 -0
- package/src/utils/sentrycli-utils.ts +3 -1
- package/src/utils/types.ts +3 -0
- package/test/react-native/gradle.test.ts +310 -0
- package/test/react-native/javascript.test.ts +131 -0
- package/test/react-native/xcode.test.ts +238 -0
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js +0 -198
- package/dist/lib/Steps/Integrations/__tests__/ReactNative.js.map +0 -1
- package/dist/lib/__tests__/Setup.js +0 -57
- package/dist/lib/__tests__/Setup.js.map +0 -1
- package/lib/Steps/Integrations/__tests__/ReactNative.ts +0 -136
- package/lib/__tests__/Setup.ts +0 -42
- /package/dist/{lib/Steps/Integrations/__tests__/ReactNative.d.ts → test/react-native/gradle.test.d.ts} +0 -0
- /package/dist/{lib/__tests__/Setup.d.ts → test/react-native/javascript.test.d.ts} +0 -0
|
@@ -109,6 +109,7 @@ async function runAppleWizardWithTelementry(
|
|
|
109
109
|
|
|
110
110
|
if (availableTargets.length == 0) {
|
|
111
111
|
clack.log.error(`No suttable target found in ${xcodeProjFile}`);
|
|
112
|
+
Sentry.setTag('No-Target', true);
|
|
112
113
|
await abort();
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
@@ -131,6 +132,7 @@ async function runAppleWizardWithTelementry(
|
|
|
131
132
|
);
|
|
132
133
|
|
|
133
134
|
let hasCocoa = cocoapod.usesCocoaPod(projectDir);
|
|
135
|
+
Sentry.setTag('cocoapod-exists', hasCocoa);
|
|
134
136
|
|
|
135
137
|
if (hasCocoa) {
|
|
136
138
|
const pm = (
|
|
@@ -147,6 +149,7 @@ async function runAppleWizardWithTelementry(
|
|
|
147
149
|
const podAdded = await traceStep('Add CocoaPods reference', () =>
|
|
148
150
|
cocoapod.addCocoaPods(projectDir),
|
|
149
151
|
);
|
|
152
|
+
Sentry.setTag('cocoapod-added', podAdded);
|
|
150
153
|
if (!podAdded) {
|
|
151
154
|
clack.log.warn(
|
|
152
155
|
"Could not add Sentry pod to your Podfile. You'll have to add it manually.\nPlease follow the instructions at https://docs.sentry.io/platforms/apple/guides/ios/#install",
|
|
@@ -155,11 +158,11 @@ async function runAppleWizardWithTelementry(
|
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
Sentry.setTag('package-manager', hasCocoa ? 'cocoapods' : 'SPM');
|
|
158
162
|
traceStep('Update Xcode project', () => {
|
|
159
163
|
xcProject.updateXcodeProject(project, target, apiKey, !hasCocoa, true);
|
|
160
164
|
});
|
|
161
165
|
|
|
162
|
-
Sentry.setTag('package-manager', hasCocoa ? 'cocoapods' : 'SPM');
|
|
163
166
|
const codeAdded = traceStep('Add code snippet', () => {
|
|
164
167
|
const files = xcProject.filesForTarget(target);
|
|
165
168
|
if (files === undefined || files.length == 0) return false;
|
|
@@ -170,18 +173,23 @@ async function runAppleWizardWithTelementry(
|
|
|
170
173
|
project.keys[0].dsn.public,
|
|
171
174
|
);
|
|
172
175
|
});
|
|
176
|
+
|
|
177
|
+
Sentry.setTag('Snippet-Added', codeAdded);
|
|
178
|
+
|
|
173
179
|
if (!codeAdded) {
|
|
174
180
|
clack.log.warn(
|
|
175
181
|
'Added the Sentry dependency to your project but could not add the Sentry code snippet. Please add the code snipped manually by following the docs: https://docs.sentry.io/platforms/apple/guides/ios/#configure',
|
|
176
182
|
);
|
|
177
|
-
return;
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
const hasFastlane = fastlane.fastFile(projectDir);
|
|
186
|
+
Sentry.setTag('fastlane-exists', hasFastlane);
|
|
187
|
+
if (hasFastlane) {
|
|
181
188
|
const addLane = await clack.confirm({
|
|
182
189
|
message:
|
|
183
190
|
'Found a Fastfile in your project. Do you want to configure a lane to upload debug symbols to Sentry?',
|
|
184
191
|
});
|
|
192
|
+
Sentry.setTag('fastlane-desired', addLane);
|
|
185
193
|
if (addLane) {
|
|
186
194
|
const added = await traceStep('Configure fastlane', () =>
|
|
187
195
|
fastlane.addSentryToFastlane(
|
|
@@ -190,6 +198,7 @@ async function runAppleWizardWithTelementry(
|
|
|
190
198
|
project.slug,
|
|
191
199
|
),
|
|
192
200
|
);
|
|
201
|
+
Sentry.setTag('fastlane-added', added);
|
|
193
202
|
if (added) {
|
|
194
203
|
clack.log.step(
|
|
195
204
|
'A new step was added to your fastlane file. Now and you build your project with fastlane, debug symbols and source context will be uploaded to Sentry.',
|
package/src/apple/cocoapod.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as bash from '../utils/bash';
|
|
|
4
4
|
import * as Sentry from '@sentry/node';
|
|
5
5
|
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
6
6
|
import * as clack from '@clack/prompts';
|
|
7
|
+
import chalk from 'chalk';
|
|
7
8
|
|
|
8
9
|
export function usesCocoaPod(projPath: string): boolean {
|
|
9
10
|
return fs.existsSync(path.join(projPath, 'Podfile'));
|
|
@@ -40,21 +41,31 @@ export async function addCocoaPods(projPath: string): Promise<boolean> {
|
|
|
40
41
|
podContent.slice(insertIndex);
|
|
41
42
|
fs.writeFileSync(podfile, newFileContent, 'utf8');
|
|
42
43
|
|
|
43
|
-
const loginSpinner = clack.spinner();
|
|
44
|
-
|
|
45
44
|
clack.log.step('Sentry pod added to the project podFile.');
|
|
46
|
-
|
|
45
|
+
|
|
46
|
+
await podInstall();
|
|
47
|
+
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function podInstall(dir = '.') {
|
|
52
|
+
const installSpinner = clack.spinner();
|
|
53
|
+
installSpinner.start("Running 'pod install'. This may take a few minutes...");
|
|
47
54
|
|
|
48
55
|
try {
|
|
49
|
-
await bash.execute(
|
|
50
|
-
|
|
56
|
+
await bash.execute(`cd ${dir} && pod repo update`);
|
|
57
|
+
await bash.execute(`cd ${dir} && pod install --silent`);
|
|
58
|
+
installSpinner.stop('Pods installed.');
|
|
51
59
|
} catch (e) {
|
|
52
|
-
|
|
60
|
+
installSpinner.stop('Failed to install pods.');
|
|
53
61
|
clack.log.error(
|
|
54
|
-
|
|
62
|
+
`${chalk.red(
|
|
63
|
+
'Encountered the following error during pods installation:',
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
65
|
+
)}\n\n${e}\n\n${chalk.dim(
|
|
66
|
+
'If you think this issue is caused by the Sentry wizard, let us know here:\nhttps://github.com/getsentry/sentry-wizard/issues',
|
|
67
|
+
)}`,
|
|
55
68
|
);
|
|
56
69
|
Sentry.captureException('Sentry pod install failed.');
|
|
57
70
|
}
|
|
58
|
-
|
|
59
|
-
return true;
|
|
60
71
|
}
|
|
@@ -87,7 +87,7 @@ export async function runNextjsWizardWithTelemetry(
|
|
|
87
87
|
createExamplePage(selfHosted, selectedProject, sentryUrl),
|
|
88
88
|
);
|
|
89
89
|
|
|
90
|
-
await addSentryCliConfig(authToken);
|
|
90
|
+
await addSentryCliConfig({ authToken });
|
|
91
91
|
|
|
92
92
|
const mightBeUsingVercel = fs.existsSync(
|
|
93
93
|
path.join(process.cwd(), 'vercel.json'),
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import glob from 'glob';
|
|
2
|
+
|
|
3
|
+
export const XCODE_PROJECT = 'ios/*.xcodeproj/project.pbxproj';
|
|
4
|
+
export const APP_BUILD_GRADLE = '**/app/build.gradle';
|
|
5
|
+
|
|
6
|
+
const IGNORE_PATTERNS = ['node_modules/**', 'ios/Pods/**', '**/Pods/**'];
|
|
7
|
+
export function getFirstMatchedPath(pattern: string): string | undefined {
|
|
8
|
+
const matches = glob.sync(pattern, {
|
|
9
|
+
ignore: IGNORE_PATTERNS,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
return matches[0];
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
|
|
3
|
+
const applyFrom = `apply from: new File(["node", "--print", "require.resolve('@sentry/react-native/package.json')"].execute().text.trim(), "../sentry.gradle")`;
|
|
4
|
+
|
|
5
|
+
export function doesAppBuildGradleIncludeRNSentryGradlePlugin(
|
|
6
|
+
content: string,
|
|
7
|
+
): boolean {
|
|
8
|
+
return content.includes('sentry.gradle');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function addRNSentryGradlePlugin(content: string): string {
|
|
12
|
+
return content.replace(/^android {/m, (match) => `${applyFrom}\n${match}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function removeRNSentryGradlePlugin(content: string): string {
|
|
16
|
+
return content.replace(/^\s*apply from:.*sentry\.gradle.*;?\s*?\r?\n/m, '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function writeAppBuildGradle(path: string, newContent: string): void {
|
|
20
|
+
const currentContent = fs.readFileSync(path, 'utf-8');
|
|
21
|
+
if (newContent === currentContent) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fs.writeFileSync(path, newContent, 'utf-8');
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { makeCodeSnippet } from '../utils/clack-utils';
|
|
2
|
+
|
|
3
|
+
export function addSentryInitWithSdkImport(
|
|
4
|
+
js: string,
|
|
5
|
+
{ dsn }: { dsn: string },
|
|
6
|
+
): string {
|
|
7
|
+
return js.replace(
|
|
8
|
+
/^([^]*)(import\s+[^;]*?;$)/m,
|
|
9
|
+
(match: string) => `${match}
|
|
10
|
+
${getSentryInitPlainTextSnippet(dsn)}`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function doesJsCodeIncludeSdkSentryImport(
|
|
15
|
+
js: string,
|
|
16
|
+
{ sdkPackageName }: { sdkPackageName: string },
|
|
17
|
+
): boolean {
|
|
18
|
+
return !!js.match(sdkPackageName);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getSentryInitColoredCodeSnippet(dsn: string) {
|
|
22
|
+
return makeCodeSnippet(true, (_unchanged, plus, _minus) => {
|
|
23
|
+
return plus(getSentryInitPlainTextSnippet(dsn));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getSentryInitPlainTextSnippet(dsn: string) {
|
|
28
|
+
return `import * as Sentry from '@sentry/react-native';
|
|
29
|
+
|
|
30
|
+
Sentry.init({
|
|
31
|
+
dsn: '${dsn}',
|
|
32
|
+
});`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/* eslint-disable max-lines */
|
|
2
|
+
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
3
|
+
import clack from '@clack/prompts';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as process from 'process';
|
|
8
|
+
import {
|
|
9
|
+
CliSetupConfigContent,
|
|
10
|
+
addSentryCliConfig,
|
|
11
|
+
confirmContinueIfNoOrDirtyGitRepo,
|
|
12
|
+
confirmContinueIfPackageVersionNotSupported,
|
|
13
|
+
ensurePackageIsInstalled,
|
|
14
|
+
getOrAskForProjectData,
|
|
15
|
+
getPackageDotJson,
|
|
16
|
+
installPackage,
|
|
17
|
+
printWelcome,
|
|
18
|
+
propertiesCliSetupConfig,
|
|
19
|
+
showCopyPasteInstructions,
|
|
20
|
+
} from '../utils/clack-utils';
|
|
21
|
+
import { getPackageVersion, hasPackageInstalled } from '../utils/package-json';
|
|
22
|
+
import { podInstall } from '../apple/cocoapod';
|
|
23
|
+
import { platform } from 'os';
|
|
24
|
+
import {
|
|
25
|
+
getValidExistingBuildPhases,
|
|
26
|
+
findBundlePhase,
|
|
27
|
+
patchBundlePhase,
|
|
28
|
+
findDebugFilesUploadPhase,
|
|
29
|
+
addDebugFilesUploadPhase,
|
|
30
|
+
writeXcodeProject,
|
|
31
|
+
} from './xcode';
|
|
32
|
+
import {
|
|
33
|
+
doesAppBuildGradleIncludeRNSentryGradlePlugin,
|
|
34
|
+
addRNSentryGradlePlugin,
|
|
35
|
+
writeAppBuildGradle,
|
|
36
|
+
} from './gradle';
|
|
37
|
+
import { runReactNativeUninstall } from './uninstall';
|
|
38
|
+
import { APP_BUILD_GRADLE, XCODE_PROJECT, getFirstMatchedPath } from './glob';
|
|
39
|
+
import { ReactNativeWizardOptions } from './options';
|
|
40
|
+
import { SentryProjectData } from '../utils/types';
|
|
41
|
+
import {
|
|
42
|
+
addSentryInitWithSdkImport,
|
|
43
|
+
doesJsCodeIncludeSdkSentryImport,
|
|
44
|
+
getSentryInitColoredCodeSnippet,
|
|
45
|
+
} from './javascript';
|
|
46
|
+
import { traceStep, withTelemetry } from '../telemetry';
|
|
47
|
+
import * as Sentry from '@sentry/node';
|
|
48
|
+
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
50
|
+
const xcode = require('xcode');
|
|
51
|
+
|
|
52
|
+
export const RN_SDK_PACKAGE = '@sentry/react-native';
|
|
53
|
+
|
|
54
|
+
export const RN_PACKAGE = 'react-native';
|
|
55
|
+
export const RN_HUMAN_NAME = 'React Native';
|
|
56
|
+
|
|
57
|
+
export const SUPPORTED_RN_RANGE = '>=0.69.0';
|
|
58
|
+
|
|
59
|
+
export type RNCliSetupConfigContent = Pick<
|
|
60
|
+
Required<CliSetupConfigContent>,
|
|
61
|
+
'authToken' | 'org' | 'project' | 'url'
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
export async function runReactNativeWizard(
|
|
65
|
+
params: ReactNativeWizardOptions,
|
|
66
|
+
): Promise<void> {
|
|
67
|
+
return withTelemetry(
|
|
68
|
+
{
|
|
69
|
+
enabled: params.telemetryEnabled,
|
|
70
|
+
integration: 'react-native',
|
|
71
|
+
},
|
|
72
|
+
() => runReactNativeWizardWithTelemetry(params),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function runReactNativeWizardWithTelemetry(
|
|
77
|
+
options: ReactNativeWizardOptions,
|
|
78
|
+
): Promise<void> {
|
|
79
|
+
if (options.uninstall) {
|
|
80
|
+
Sentry.setTag('uninstall', true);
|
|
81
|
+
return runReactNativeUninstall(options);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
printWelcome({
|
|
85
|
+
wizardName: 'Sentry React Native Wizard',
|
|
86
|
+
promoCode: options.promoCode,
|
|
87
|
+
telemetryEnabled: options.telemetryEnabled,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
await confirmContinueIfNoOrDirtyGitRepo();
|
|
91
|
+
|
|
92
|
+
const packageJson = await getPackageDotJson();
|
|
93
|
+
|
|
94
|
+
await ensurePackageIsInstalled(packageJson, RN_PACKAGE, RN_HUMAN_NAME);
|
|
95
|
+
|
|
96
|
+
const rnVersion = getPackageVersion(RN_PACKAGE, packageJson);
|
|
97
|
+
if (rnVersion) {
|
|
98
|
+
await confirmContinueIfPackageVersionNotSupported({
|
|
99
|
+
packageName: RN_HUMAN_NAME,
|
|
100
|
+
packageVersion: rnVersion,
|
|
101
|
+
packageId: RN_PACKAGE,
|
|
102
|
+
acceptableVersions: SUPPORTED_RN_RANGE,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const { selectedProject, authToken, sentryUrl } =
|
|
107
|
+
await getOrAskForProjectData(options, 'react-native');
|
|
108
|
+
const orgSlug = selectedProject.organization.slug;
|
|
109
|
+
const projectSlug = selectedProject.slug;
|
|
110
|
+
const cliConfig: RNCliSetupConfigContent = {
|
|
111
|
+
authToken,
|
|
112
|
+
org: orgSlug,
|
|
113
|
+
project: projectSlug,
|
|
114
|
+
url: sentryUrl,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
await installPackage({
|
|
118
|
+
packageName: RN_SDK_PACKAGE,
|
|
119
|
+
alreadyInstalled: hasPackageInstalled(RN_SDK_PACKAGE, packageJson),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await traceStep('patch-js', () =>
|
|
123
|
+
addSentryInit({ dsn: selectedProject.keys[0].dsn.public }),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
if (fs.existsSync('ios')) {
|
|
127
|
+
Sentry.setTag('patch-ios', true);
|
|
128
|
+
await traceStep('patch-xcode-files', () => patchXcodeFiles(cliConfig));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (fs.existsSync('android')) {
|
|
132
|
+
Sentry.setTag('patch-android', true);
|
|
133
|
+
await traceStep('patch-android-files', () => patchAndroidFiles(cliConfig));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const confirmedFirstException = await confirmFirstSentryException(
|
|
137
|
+
selectedProject,
|
|
138
|
+
);
|
|
139
|
+
Sentry.setTag('user-confirmed-first-error', confirmedFirstException);
|
|
140
|
+
|
|
141
|
+
if (confirmedFirstException) {
|
|
142
|
+
clack.outro(
|
|
143
|
+
`${chalk.green('Everything is set up!')}
|
|
144
|
+
|
|
145
|
+
${chalk.dim(
|
|
146
|
+
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-react-native/issues',
|
|
147
|
+
)}`,
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
clack.outro(
|
|
151
|
+
`${chalk.dim(
|
|
152
|
+
'Let us know here: https://github.com/getsentry/sentry-react-native/issues',
|
|
153
|
+
)}`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function addSentryInit({ dsn }: { dsn: string }) {
|
|
159
|
+
const prefixGlob = '{.,./src}';
|
|
160
|
+
const suffixGlob = '@(j|t|cj|mj)s?(x)';
|
|
161
|
+
const universalGlob = `App.${suffixGlob}`;
|
|
162
|
+
const jsFileGlob = `${prefixGlob}/+(${universalGlob})`;
|
|
163
|
+
const jsPath = traceStep('find-app-js-file', () =>
|
|
164
|
+
getFirstMatchedPath(jsFileGlob),
|
|
165
|
+
);
|
|
166
|
+
Sentry.setTag('app-js-file-status', jsPath ? 'found' : 'not-found');
|
|
167
|
+
if (!jsPath) {
|
|
168
|
+
clack.log.warn(
|
|
169
|
+
`Could not find main App file using ${chalk.cyan(jsFileGlob)}.`,
|
|
170
|
+
);
|
|
171
|
+
await showCopyPasteInstructions(
|
|
172
|
+
'App.js',
|
|
173
|
+
getSentryInitColoredCodeSnippet(dsn),
|
|
174
|
+
'This ensures the Sentry SDK is ready to capture errors.',
|
|
175
|
+
);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const jsRelativePath = path.relative(process.cwd(), jsPath);
|
|
179
|
+
|
|
180
|
+
const js = fs.readFileSync(jsPath, 'utf-8');
|
|
181
|
+
const includesSentry = doesJsCodeIncludeSdkSentryImport(js, {
|
|
182
|
+
sdkPackageName: RN_SDK_PACKAGE,
|
|
183
|
+
});
|
|
184
|
+
if (includesSentry) {
|
|
185
|
+
Sentry.setTag('app-js-file-status', 'already-includes-sentry');
|
|
186
|
+
clack.log.warn(
|
|
187
|
+
`${chalk.cyan(
|
|
188
|
+
jsRelativePath,
|
|
189
|
+
)} already includes Sentry. We wont't add it again.`,
|
|
190
|
+
);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
traceStep('add-sentry-init', () => {
|
|
195
|
+
const newContent = addSentryInitWithSdkImport(js, { dsn });
|
|
196
|
+
|
|
197
|
+
clack.log.success(
|
|
198
|
+
`Added ${chalk.cyan('Sentry.init')} to ${chalk.cyan(jsRelativePath)}.`,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
fs.writeFileSync(jsPath, newContent, 'utf-8');
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
Sentry.setTag('app-js-file-status', 'added-sentry-init');
|
|
205
|
+
clack.log.success(
|
|
206
|
+
chalk.green(`${chalk.cyan(jsRelativePath)} changes saved.`),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function confirmFirstSentryException(project: SentryProjectData) {
|
|
211
|
+
const projectsIssuesUrl = `${project.organization.links.organizationUrl}/issues/?project=${project.id}`;
|
|
212
|
+
|
|
213
|
+
clack.log
|
|
214
|
+
.step(`To make sure everything is set up correctly, put the following code snippet into your application.
|
|
215
|
+
The snippet will create a button that, when tapped, sends a test event to Sentry.
|
|
216
|
+
|
|
217
|
+
After that check your project issues:
|
|
218
|
+
|
|
219
|
+
${chalk.cyan(projectsIssuesUrl)}`);
|
|
220
|
+
|
|
221
|
+
// We want the code snippet to be easily copy-pasteable, without any clack artifacts
|
|
222
|
+
// eslint-disable-next-line no-console
|
|
223
|
+
console.log(
|
|
224
|
+
chalk.greenBright(`
|
|
225
|
+
<Button title='Try!' onPress={ () => { Sentry.captureException(new Error('First error')) }}/>
|
|
226
|
+
`),
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
const firstErrorConfirmed = clack.confirm({
|
|
230
|
+
message: `Have you successfully sent a test event?`,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
return firstErrorConfirmed;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function patchXcodeFiles(config: RNCliSetupConfigContent) {
|
|
237
|
+
await addSentryCliConfig(config, {
|
|
238
|
+
...propertiesCliSetupConfig,
|
|
239
|
+
name: 'source maps and iOS debug files',
|
|
240
|
+
filename: 'ios/sentry.properties',
|
|
241
|
+
gitignore: false,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
if (platform() === 'darwin') {
|
|
245
|
+
await traceStep('pod-install', () => podInstall('ios'));
|
|
246
|
+
Sentry.setTag('pods-installed', true);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const xcodeProjectPath = traceStep('find-xcode-project', () =>
|
|
250
|
+
getFirstMatchedPath(XCODE_PROJECT),
|
|
251
|
+
);
|
|
252
|
+
Sentry.setTag(
|
|
253
|
+
'xcode-project-status',
|
|
254
|
+
xcodeProjectPath ? 'found' : 'not-found',
|
|
255
|
+
);
|
|
256
|
+
if (!xcodeProjectPath) {
|
|
257
|
+
clack.log.warn(
|
|
258
|
+
`Could not find Xcode project file using ${chalk.cyan(XCODE_PROJECT)}.`,
|
|
259
|
+
);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
264
|
+
const [xcodeProject, buildPhasesMap] = traceStep(
|
|
265
|
+
'parse-xcode-project',
|
|
266
|
+
() => {
|
|
267
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
268
|
+
const project = xcode.project(xcodeProjectPath);
|
|
269
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
270
|
+
project.parseSync();
|
|
271
|
+
|
|
272
|
+
const map = getValidExistingBuildPhases(project);
|
|
273
|
+
return [project, map];
|
|
274
|
+
},
|
|
275
|
+
);
|
|
276
|
+
Sentry.setTag('xcode-project-status', 'parsed');
|
|
277
|
+
|
|
278
|
+
traceStep('patch-bundle-phase', () => {
|
|
279
|
+
const bundlePhase = findBundlePhase(buildPhasesMap);
|
|
280
|
+
Sentry.setTag(
|
|
281
|
+
'xcode-bundle-phase-status',
|
|
282
|
+
bundlePhase ? 'found' : 'not-found',
|
|
283
|
+
);
|
|
284
|
+
patchBundlePhase(bundlePhase);
|
|
285
|
+
Sentry.setTag('xcode-bundle-phase-status', 'patched');
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
traceStep('add-debug-files-upload-phase', () => {
|
|
289
|
+
const debugFilesUploadPhaseExists =
|
|
290
|
+
!!findDebugFilesUploadPhase(buildPhasesMap);
|
|
291
|
+
Sentry.setTag(
|
|
292
|
+
'xcode-debug-files-upload-phase-status',
|
|
293
|
+
debugFilesUploadPhaseExists ? 'already-exists' : undefined,
|
|
294
|
+
);
|
|
295
|
+
addDebugFilesUploadPhase(xcodeProject, { debugFilesUploadPhaseExists });
|
|
296
|
+
Sentry.setTag('xcode-debug-files-upload-phase-status', 'added');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
traceStep('write-xcode-project', () => {
|
|
300
|
+
writeXcodeProject(xcodeProjectPath, xcodeProject);
|
|
301
|
+
});
|
|
302
|
+
Sentry.setTag('xcode-project-status', 'patched');
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function patchAndroidFiles(config: RNCliSetupConfigContent) {
|
|
306
|
+
await addSentryCliConfig(config, {
|
|
307
|
+
...propertiesCliSetupConfig,
|
|
308
|
+
name: 'source maps and iOS debug files',
|
|
309
|
+
filename: 'android/sentry.properties',
|
|
310
|
+
gitignore: false,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const appBuildGradlePath = traceStep('find-app-build-gradle', () =>
|
|
314
|
+
getFirstMatchedPath(APP_BUILD_GRADLE),
|
|
315
|
+
);
|
|
316
|
+
Sentry.setTag(
|
|
317
|
+
'app-build-gradle-status',
|
|
318
|
+
appBuildGradlePath ? 'found' : 'not-found',
|
|
319
|
+
);
|
|
320
|
+
if (!appBuildGradlePath) {
|
|
321
|
+
clack.log.warn(
|
|
322
|
+
`Could not find Android ${chalk.cyan(
|
|
323
|
+
'app/build.gradle',
|
|
324
|
+
)} file using ${chalk.cyan(APP_BUILD_GRADLE)}.`,
|
|
325
|
+
);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const appBuildGradle = traceStep('read-app-build-gradle', () =>
|
|
330
|
+
fs.readFileSync(appBuildGradlePath, 'utf-8'),
|
|
331
|
+
);
|
|
332
|
+
const includesSentry =
|
|
333
|
+
doesAppBuildGradleIncludeRNSentryGradlePlugin(appBuildGradle);
|
|
334
|
+
if (includesSentry) {
|
|
335
|
+
Sentry.setTag('app-build-gradle-status', 'already-includes-sentry');
|
|
336
|
+
clack.log.warn(
|
|
337
|
+
`Android ${chalk.cyan('app/build.gradle')} file already includes Sentry.`,
|
|
338
|
+
);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const patchedAppBuildGradle = traceStep('add-rn-sentry-gradle-plugin', () =>
|
|
343
|
+
addRNSentryGradlePlugin(appBuildGradle),
|
|
344
|
+
);
|
|
345
|
+
if (!doesAppBuildGradleIncludeRNSentryGradlePlugin(patchedAppBuildGradle)) {
|
|
346
|
+
Sentry.setTag(
|
|
347
|
+
'app-build-gradle-status',
|
|
348
|
+
'failed-to-add-rn-sentry-gradle-plugin',
|
|
349
|
+
);
|
|
350
|
+
clack.log.warn(
|
|
351
|
+
`Could not add Sentry RN Gradle Plugin to ${chalk.cyan(
|
|
352
|
+
'app/build.gradle',
|
|
353
|
+
)}.`,
|
|
354
|
+
);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
Sentry.setTag('app-build-gradle-status', 'added-rn-sentry-gradle-plugin');
|
|
359
|
+
clack.log.success(
|
|
360
|
+
`Added Sentry RN Gradle Plugin to ${chalk.bold('app/build.gradle')}.`,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
traceStep('write-app-build-gradle', () =>
|
|
364
|
+
writeAppBuildGradle(appBuildGradlePath, patchedAppBuildGradle),
|
|
365
|
+
);
|
|
366
|
+
clack.log.success(
|
|
367
|
+
chalk.green(`Android ${chalk.cyan('app/build.gradle')} saved.`),
|
|
368
|
+
);
|
|
369
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
// @ts-ignore - clack is ESM and TS complains about that. It works though
|
|
3
|
+
import clack from '@clack/prompts';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
confirmContinueIfNoOrDirtyGitRepo,
|
|
8
|
+
printWelcome,
|
|
9
|
+
} from '../utils/clack-utils';
|
|
10
|
+
import {
|
|
11
|
+
findBundlePhase,
|
|
12
|
+
getValidExistingBuildPhases,
|
|
13
|
+
unPatchBundlePhase,
|
|
14
|
+
unPatchDebugFilesUploadPhase,
|
|
15
|
+
writeXcodeProject,
|
|
16
|
+
} from './xcode';
|
|
17
|
+
import { APP_BUILD_GRADLE, XCODE_PROJECT, getFirstMatchedPath } from './glob';
|
|
18
|
+
import {
|
|
19
|
+
doesAppBuildGradleIncludeRNSentryGradlePlugin,
|
|
20
|
+
removeRNSentryGradlePlugin,
|
|
21
|
+
writeAppBuildGradle,
|
|
22
|
+
} from './gradle';
|
|
23
|
+
import { ReactNativeWizardOptions } from './options';
|
|
24
|
+
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
26
|
+
const xcode = require('xcode');
|
|
27
|
+
|
|
28
|
+
export async function runReactNativeUninstall(
|
|
29
|
+
options: ReactNativeWizardOptions,
|
|
30
|
+
): Promise<void> {
|
|
31
|
+
printWelcome({
|
|
32
|
+
wizardName: 'Sentry React Native Uninstall Wizard',
|
|
33
|
+
message: 'This wizard will remove Sentry from your React Native project.',
|
|
34
|
+
telemetryEnabled: options.telemetryEnabled,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await confirmContinueIfNoOrDirtyGitRepo();
|
|
38
|
+
|
|
39
|
+
unPatchXcodeFiles();
|
|
40
|
+
|
|
41
|
+
unPatchAndroidFiles();
|
|
42
|
+
|
|
43
|
+
clack.note(
|
|
44
|
+
`To make sure your project builds after removing Sentry please run:
|
|
45
|
+
|
|
46
|
+
1. ${chalk.bold('yarn remove @sentry/react-native')}
|
|
47
|
+
2. ${chalk.bold('cd ios && pod install')}
|
|
48
|
+
3. Remove all occurrences of ${chalk.bold(
|
|
49
|
+
'@sentry/react-native',
|
|
50
|
+
)} from your application code.`,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
clack.outro(
|
|
54
|
+
`${chalk.green('Uninstall is done!')}
|
|
55
|
+
|
|
56
|
+
${chalk.dim(
|
|
57
|
+
'If you encounter any issues, let us know here: https://github.com/getsentry/sentry-react-native/issues',
|
|
58
|
+
)}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function unPatchXcodeFiles() {
|
|
63
|
+
const xcodeProjectPath = getFirstMatchedPath(XCODE_PROJECT);
|
|
64
|
+
if (!xcodeProjectPath) {
|
|
65
|
+
clack.log.warn(
|
|
66
|
+
`Could not find Xcode project file using ${chalk.bold(XCODE_PROJECT)}.`,
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
72
|
+
const xcodeProject = xcode.project(xcodeProjectPath);
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
|
|
74
|
+
xcodeProject.parseSync();
|
|
75
|
+
const buildPhases = getValidExistingBuildPhases(xcodeProject);
|
|
76
|
+
|
|
77
|
+
const bundlePhase = findBundlePhase(buildPhases);
|
|
78
|
+
unPatchBundlePhase(bundlePhase);
|
|
79
|
+
|
|
80
|
+
unPatchDebugFilesUploadPhase(xcodeProject);
|
|
81
|
+
|
|
82
|
+
writeXcodeProject(xcodeProjectPath, xcodeProject);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function unPatchAndroidFiles() {
|
|
86
|
+
const appBuildGradlePath = getFirstMatchedPath(APP_BUILD_GRADLE);
|
|
87
|
+
if (!appBuildGradlePath) {
|
|
88
|
+
clack.log.warn(
|
|
89
|
+
`Could not find Android app/build.gradle file using ${chalk.bold(
|
|
90
|
+
APP_BUILD_GRADLE,
|
|
91
|
+
)}.`,
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const appBuildGradle = fs.readFileSync(appBuildGradlePath, 'utf-8');
|
|
97
|
+
const includesSentry =
|
|
98
|
+
doesAppBuildGradleIncludeRNSentryGradlePlugin(appBuildGradle);
|
|
99
|
+
if (!includesSentry) {
|
|
100
|
+
clack.log.warn(`Sentry not found in Android app/build.gradle.`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const patchedAppBuildGradle = removeRNSentryGradlePlugin(appBuildGradle);
|
|
105
|
+
|
|
106
|
+
writeAppBuildGradle(appBuildGradlePath, patchedAppBuildGradle);
|
|
107
|
+
}
|