@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.
Files changed (120) hide show
  1. package/CHANGELOG.md +19 -4
  2. package/dist/lib/Steps/ChooseIntegration.js +1 -1
  3. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  4. package/dist/lib/Steps/Integrations/ReactNative.d.ts +7 -32
  5. package/dist/lib/Steps/Integrations/ReactNative.js +17 -485
  6. package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
  7. package/dist/package.json +1 -1
  8. package/dist/src/android/android-wizard.js +13 -18
  9. package/dist/src/android/android-wizard.js.map +1 -1
  10. package/dist/src/apple/apple-wizard.js +11 -4
  11. package/dist/src/apple/apple-wizard.js.map +1 -1
  12. package/dist/src/apple/cocoapod.d.ts +1 -0
  13. package/dist/src/apple/cocoapod.js +36 -13
  14. package/dist/src/apple/cocoapod.js.map +1 -1
  15. package/dist/src/nextjs/nextjs-wizard.js +1 -1
  16. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  17. package/dist/src/react-native/glob.d.ts +3 -0
  18. package/dist/src/react-native/glob.js +18 -0
  19. package/dist/src/react-native/glob.js.map +1 -0
  20. package/dist/src/react-native/gradle.d.ts +4 -0
  21. package/dist/src/react-native/gradle.js +49 -0
  22. package/dist/src/react-native/gradle.js.map +1 -0
  23. package/dist/src/react-native/javascript.d.ts +8 -0
  24. package/dist/src/react-native/javascript.js +25 -0
  25. package/dist/src/react-native/javascript.js.map +1 -0
  26. package/dist/src/react-native/options.d.ts +4 -0
  27. package/dist/src/react-native/options.js +3 -0
  28. package/dist/src/react-native/options.js.map +1 -0
  29. package/dist/src/react-native/react-native-wizard.d.ts +9 -0
  30. package/dist/src/react-native/react-native-wizard.js +356 -0
  31. package/dist/src/react-native/react-native-wizard.js.map +1 -0
  32. package/dist/src/react-native/uninstall.d.ts +2 -0
  33. package/dist/src/react-native/uninstall.js +130 -0
  34. package/dist/src/react-native/uninstall.js.map +1 -0
  35. package/dist/src/react-native/xcode.d.ts +18 -0
  36. package/dist/src/react-native/xcode.js +170 -0
  37. package/dist/src/react-native/xcode.js.map +1 -0
  38. package/dist/src/remix/codemods/handle-error.js +28 -0
  39. package/dist/src/remix/codemods/handle-error.js.map +1 -1
  40. package/dist/src/remix/codemods/root-common.d.ts +2 -0
  41. package/dist/src/remix/codemods/root-common.js +70 -0
  42. package/dist/src/remix/codemods/root-common.js.map +1 -0
  43. package/dist/src/remix/codemods/root-v1.js +5 -36
  44. package/dist/src/remix/codemods/root-v1.js.map +1 -1
  45. package/dist/src/remix/codemods/root-v2.js +53 -4
  46. package/dist/src/remix/codemods/root-v2.js.map +1 -1
  47. package/dist/src/remix/remix-wizard.js +8 -5
  48. package/dist/src/remix/remix-wizard.js.map +1 -1
  49. package/dist/src/remix/sdk-setup.d.ts +1 -0
  50. package/dist/src/remix/sdk-setup.js +10 -6
  51. package/dist/src/remix/sdk-setup.js.map +1 -1
  52. package/dist/src/remix/templates.d.ts +1 -1
  53. package/dist/src/remix/templates.js +1 -1
  54. package/dist/src/remix/templates.js.map +1 -1
  55. package/dist/src/remix/utils.d.ts +2 -0
  56. package/dist/src/remix/utils.js +6 -1
  57. package/dist/src/remix/utils.js.map +1 -1
  58. package/dist/src/sourcemaps/tools/nextjs.js +3 -3
  59. package/dist/src/sourcemaps/tools/nextjs.js.map +1 -1
  60. package/dist/src/sourcemaps/tools/sentry-cli.js +1 -1
  61. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  62. package/dist/src/sveltekit/sveltekit-wizard.js +1 -1
  63. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  64. package/dist/src/utils/clack-utils.d.ts +19 -3
  65. package/dist/src/utils/clack-utils.js +141 -39
  66. package/dist/src/utils/clack-utils.js.map +1 -1
  67. package/dist/src/utils/semver.d.ts +5 -0
  68. package/dist/src/utils/semver.js +27 -0
  69. package/dist/src/utils/semver.js.map +1 -0
  70. package/dist/src/utils/sentrycli-utils.js +4 -1
  71. package/dist/src/utils/sentrycli-utils.js.map +1 -1
  72. package/dist/src/utils/types.d.ts +3 -0
  73. package/dist/src/utils/types.js.map +1 -1
  74. package/dist/test/react-native/gradle.test.js +57 -0
  75. package/dist/test/react-native/gradle.test.js.map +1 -0
  76. package/dist/test/react-native/javascript.test.js +47 -0
  77. package/dist/test/react-native/javascript.test.js.map +1 -0
  78. package/dist/test/react-native/xcode.test.d.ts +1 -0
  79. package/dist/test/react-native/xcode.test.js +144 -0
  80. package/dist/test/react-native/xcode.test.js.map +1 -0
  81. package/lib/Steps/ChooseIntegration.ts +1 -1
  82. package/lib/Steps/Integrations/ReactNative.ts +17 -573
  83. package/package.json +1 -1
  84. package/src/android/android-wizard.ts +3 -18
  85. package/src/apple/apple-wizard.ts +12 -3
  86. package/src/apple/cocoapod.ts +20 -9
  87. package/src/nextjs/nextjs-wizard.ts +1 -1
  88. package/src/react-native/glob.ts +13 -0
  89. package/src/react-native/gradle.ts +26 -0
  90. package/src/react-native/javascript.ts +33 -0
  91. package/src/react-native/options.ts +5 -0
  92. package/src/react-native/react-native-wizard.ts +369 -0
  93. package/src/react-native/uninstall.ts +107 -0
  94. package/src/react-native/xcode.ts +228 -0
  95. package/src/remix/codemods/handle-error.ts +30 -0
  96. package/src/remix/codemods/root-common.ts +63 -0
  97. package/src/remix/codemods/root-v1.ts +3 -53
  98. package/src/remix/codemods/root-v2.ts +71 -2
  99. package/src/remix/remix-wizard.ts +9 -6
  100. package/src/remix/sdk-setup.ts +14 -6
  101. package/src/remix/templates.ts +2 -6
  102. package/src/remix/utils.ts +5 -0
  103. package/src/sourcemaps/tools/nextjs.ts +6 -6
  104. package/src/sourcemaps/tools/sentry-cli.ts +1 -1
  105. package/src/sveltekit/sveltekit-wizard.ts +1 -1
  106. package/src/utils/clack-utils.ts +229 -74
  107. package/src/utils/semver.ts +33 -0
  108. package/src/utils/sentrycli-utils.ts +3 -1
  109. package/src/utils/types.ts +3 -0
  110. package/test/react-native/gradle.test.ts +310 -0
  111. package/test/react-native/javascript.test.ts +131 -0
  112. package/test/react-native/xcode.test.ts +238 -0
  113. package/dist/lib/Steps/Integrations/__tests__/ReactNative.js +0 -198
  114. package/dist/lib/Steps/Integrations/__tests__/ReactNative.js.map +0 -1
  115. package/dist/lib/__tests__/Setup.js +0 -57
  116. package/dist/lib/__tests__/Setup.js.map +0 -1
  117. package/lib/Steps/Integrations/__tests__/ReactNative.ts +0 -136
  118. package/lib/__tests__/Setup.ts +0 -42
  119. /package/dist/{lib/Steps/Integrations/__tests__/ReactNative.d.ts → test/react-native/gradle.test.d.ts} +0 -0
  120. /package/dist/{lib/__tests__/Setup.d.ts → test/react-native/javascript.test.d.ts} +0 -0
@@ -19,14 +19,14 @@ const getCodeSnippet = (options: SourceMapUploadToolConfigurationOptions) =>
19
19
  const nextConfig = {
20
20
  // your existing next config
21
21
  };
22
-
22
+
23
23
  ${chalk.greenBright(`const sentryWebpackPluginOptions = {
24
24
  org: "${options.orgSlug}",
25
25
  project: "${options.projectSlug}",${
26
26
  options.selfHosted ? `\n url: "${options.url}",` : ''
27
27
  }
28
28
  };`)}
29
-
29
+
30
30
  ${chalk.greenBright(`const sentryOptions = {
31
31
  // Upload additional client files (increases upload size)
32
32
  widenClientFileUpload: true,
@@ -34,12 +34,12 @@ const getCodeSnippet = (options: SourceMapUploadToolConfigurationOptions) =>
34
34
  // Hides source maps from generated client bundles
35
35
  hideSourceMaps: true,
36
36
  };`)}
37
-
37
+
38
38
  ${chalk.greenBright(`module.exports = withSentryConfig(
39
39
  nextConfig,
40
40
  sentryWebpackPluginOptions,
41
41
  sentryOptions
42
- );`)}
42
+ );`)}
43
43
  `);
44
44
 
45
45
  export const configureNextJsSourceMapsUpload = async (
@@ -99,7 +99,7 @@ In case you already tried the wizard, we can also show you how to configure your
99
99
  );
100
100
 
101
101
  await traceStep('nextjs-manual-sentryclirc', () =>
102
- addSentryCliConfig(options.authToken),
102
+ addSentryCliConfig({ authToken: options.authToken }),
103
103
  );
104
104
  }
105
105
 
@@ -108,7 +108,7 @@ In case you already tried the wizard, we can also show you how to configure your
108
108
 
109
109
  Uploading Source Maps:
110
110
  https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#configure-source-maps
111
-
111
+
112
112
  Troubleshooting Source Maps:
113
113
  https://docs.sentry.io/platforms/javascript/guides/nextjs/troubleshooting/`);
114
114
  };
@@ -96,7 +96,7 @@ export async function configureSentryCLI(
96
96
  );
97
97
  }
98
98
 
99
- await addSentryCliConfig(options.authToken);
99
+ await addSentryCliConfig({ authToken: options.authToken });
100
100
  }
101
101
 
102
102
  export async function setupNpmScriptInCI(): Promise<void> {
@@ -95,7 +95,7 @@ export async function runSvelteKitWizardWithTelemetry(
95
95
  alreadyInstalled: sdkAlreadyInstalled,
96
96
  });
97
97
 
98
- await addSentryCliConfig(authToken);
98
+ await addSentryCliConfig({ authToken });
99
99
 
100
100
  const svelteConfig = await traceStep('load-svelte-config', loadSvelteConfig);
101
101
 
@@ -19,6 +19,7 @@ import {
19
19
  packageManagers,
20
20
  } from './package-manager';
21
21
  import { debug } from './debug';
22
+ import { fulfillsVersionRange } from './semver';
22
23
 
23
24
  const opn = require('opn') as (
24
25
  url: string,
@@ -40,17 +41,29 @@ interface WizardProjectData {
40
41
  export interface CliSetupConfig {
41
42
  filename: string;
42
43
  name: string;
44
+ gitignore: boolean;
43
45
 
44
46
  likelyAlreadyHasAuthToken(contents: string): boolean;
45
47
  tokenContent(authToken: string): string;
46
48
 
47
49
  likelyAlreadyHasOrgAndProject(contents: string): boolean;
48
50
  orgAndProjContent(org: string, project: string): string;
51
+
52
+ likelyAlreadyHasUrl?(contents: string): boolean;
53
+ urlContent?(url: string): string;
54
+ }
55
+
56
+ export interface CliSetupConfigContent {
57
+ authToken: string;
58
+ org?: string;
59
+ project?: string;
60
+ url?: string;
49
61
  }
50
62
 
51
- export const sourceMapsCliSetupConfig: CliSetupConfig = {
63
+ export const rcCliSetupConfig: CliSetupConfig = {
52
64
  filename: SENTRY_CLI_RC_FILE,
53
65
  name: 'source maps',
66
+ gitignore: true,
54
67
  likelyAlreadyHasAuthToken: function (contents: string): boolean {
55
68
  return !!(contents.includes('[auth]') && contents.match(/token=./g));
56
69
  },
@@ -69,6 +82,33 @@ export const sourceMapsCliSetupConfig: CliSetupConfig = {
69
82
  },
70
83
  };
71
84
 
85
+ export const propertiesCliSetupConfig: Required<CliSetupConfig> = {
86
+ filename: SENTRY_PROPERTIES_FILE,
87
+ gitignore: true,
88
+ name: 'debug files',
89
+ likelyAlreadyHasAuthToken(contents: string): boolean {
90
+ return !!contents.match(/auth\.token=./g);
91
+ },
92
+ tokenContent(authToken: string): string {
93
+ return `auth.token=${authToken}`;
94
+ },
95
+ likelyAlreadyHasOrgAndProject(contents: string): boolean {
96
+ return !!(
97
+ contents.match(/defaults\.org=./g) &&
98
+ contents.match(/defaults\.project=./g)
99
+ );
100
+ },
101
+ orgAndProjContent(org: string, project: string): string {
102
+ return `defaults.org=${org}\ndefaults.project=${project}`;
103
+ },
104
+ likelyAlreadyHasUrl(contents: string): boolean {
105
+ return !!contents.match(/defaults\.url=./g);
106
+ },
107
+ urlContent(url: string): string {
108
+ return `defaults.url=${url}`;
109
+ },
110
+ };
111
+
72
112
  export async function abort(message?: string, status?: number): Promise<never> {
73
113
  clack.outro(message ?? 'Wizard setup cancelled.');
74
114
  const sentryHub = Sentry.getCurrentHub();
@@ -248,6 +288,58 @@ export async function askForItemSelection(
248
288
  return selection;
249
289
  }
250
290
 
291
+ export async function confirmContinueIfPackageVersionNotSupported({
292
+ packageId,
293
+ packageName,
294
+ packageVersion,
295
+ acceptableVersions,
296
+ }: {
297
+ packageId: string;
298
+ packageName: string;
299
+ packageVersion: string;
300
+ acceptableVersions: string;
301
+ }): Promise<void> {
302
+ return traceStep(`check-package-version`, async () => {
303
+ Sentry.setTag(`${packageName.toLowerCase()}-version`, packageVersion);
304
+ const isSupportedVersion = fulfillsVersionRange({
305
+ acceptableVersions,
306
+ version: packageVersion,
307
+ canBeLatest: true,
308
+ });
309
+
310
+ if (isSupportedVersion) {
311
+ Sentry.setTag(`${packageName.toLowerCase()}-supported`, true);
312
+ return;
313
+ }
314
+
315
+ clack.log.warn(
316
+ `You have an unsupported version of ${packageName} installed:
317
+
318
+ ${packageId}@${packageVersion}`,
319
+ );
320
+
321
+ clack.note(
322
+ `Please upgrade to ${acceptableVersions} if you wish to use the Sentry Wizard.
323
+ Or setup using ${chalk.cyan(
324
+ 'https://docs.sentry.io/platforms/react-native/manual-setup/manual-setup/',
325
+ )}`,
326
+ );
327
+ const continueWithUnsupportedVersion = await abortIfCancelled(
328
+ clack.confirm({
329
+ message: 'Do you want to continue anyway?',
330
+ }),
331
+ );
332
+ Sentry.setTag(
333
+ `${packageName.toLowerCase()}-continue-with-unsupported-version`,
334
+ continueWithUnsupportedVersion,
335
+ );
336
+
337
+ if (!continueWithUnsupportedVersion) {
338
+ await abort(undefined, 0);
339
+ }
340
+ });
341
+ }
342
+
251
343
  export async function installPackage({
252
344
  packageName,
253
345
  alreadyInstalled,
@@ -306,80 +398,143 @@ export async function installPackage({
306
398
  }
307
399
 
308
400
  export async function addSentryCliConfig(
309
- authToken: string,
310
- setupConfig: CliSetupConfig = sourceMapsCliSetupConfig,
401
+ { authToken, org, project, url }: CliSetupConfigContent,
402
+ setupConfig: CliSetupConfig = rcCliSetupConfig,
311
403
  ): Promise<void> {
312
404
  return traceStep('add-sentry-cli-config', async () => {
313
- const configExists = fs.existsSync(
314
- path.join(process.cwd(), setupConfig.filename),
405
+ const configPath = path.join(process.cwd(), setupConfig.filename);
406
+ const configExists = fs.existsSync(configPath);
407
+
408
+ let configContents =
409
+ (configExists && fs.readFileSync(configPath, 'utf8')) || '';
410
+ configContents = addAuthTokenToSentryConfig(
411
+ configContents,
412
+ authToken,
413
+ setupConfig,
315
414
  );
316
- if (configExists) {
317
- const configContents = fs.readFileSync(
318
- path.join(process.cwd(), setupConfig.filename),
319
- 'utf8',
415
+ configContents = addOrgAndProjectToSentryConfig(
416
+ configContents,
417
+ org,
418
+ project,
419
+ setupConfig,
420
+ );
421
+ configContents = addUrlToSentryConfig(configContents, url, setupConfig);
422
+
423
+ try {
424
+ await fs.promises.writeFile(configPath, configContents, {
425
+ encoding: 'utf8',
426
+ flag: 'w',
427
+ });
428
+ clack.log.success(
429
+ `${configExists ? 'Saved' : 'Created'} ${chalk.cyan(
430
+ setupConfig.filename,
431
+ )}.`,
320
432
  );
433
+ } catch {
434
+ clack.log.warning(
435
+ `Failed to add auth token to ${chalk.cyan(
436
+ setupConfig.filename,
437
+ )}. Uploading ${
438
+ setupConfig.name
439
+ } during build will likely not work locally.`,
440
+ );
441
+ }
321
442
 
322
- if (setupConfig.likelyAlreadyHasAuthToken(configContents)) {
323
- clack.log.warn(
324
- `${chalk.bold(
325
- setupConfig.filename,
326
- )} already has auth token. Will not add one.`,
327
- );
328
- } else {
329
- try {
330
- await fs.promises.writeFile(
331
- path.join(process.cwd(), setupConfig.filename),
332
- `${configContents}\n${setupConfig.tokenContent(authToken)}\n`,
333
- { encoding: 'utf8', flag: 'w' },
334
- );
335
- clack.log.success(
336
- chalk.greenBright(
337
- `Added auth token to ${chalk.bold(
338
- setupConfig.filename,
339
- )} for you to test uploading ${setupConfig.name} locally.`,
340
- ),
341
- );
342
- } catch {
343
- clack.log.warning(
344
- `Failed to add auth token to ${chalk.bold(
345
- setupConfig.filename,
346
- )}. Uploading ${
347
- setupConfig.name
348
- } during build will likely not work locally.`,
349
- );
350
- }
351
- }
443
+ if (setupConfig.gitignore) {
444
+ await addCliConfigFileToGitIgnore(setupConfig.filename);
352
445
  } else {
353
- try {
354
- await fs.promises.writeFile(
355
- path.join(process.cwd(), setupConfig.filename),
356
- `${setupConfig.tokenContent(authToken)}\n`,
357
- { encoding: 'utf8', flag: 'w' },
358
- );
359
- clack.log.success(
360
- chalk.greenBright(
361
- `Created ${chalk.bold(
362
- setupConfig.filename,
363
- )} with auth token for you to test uploading ${
364
- setupConfig.name
365
- } locally.`,
366
- ),
367
- );
368
- } catch {
369
- clack.log.warning(
370
- `Failed to create ${chalk.bold(
371
- setupConfig.filename,
372
- )} with auth token. Uploading ${
373
- setupConfig.name
374
- } during build will likely not work locally.`,
375
- );
376
- }
446
+ clack.log.warn(
447
+ chalk.yellow('DO NOT commit auth token to your repository!'),
448
+ );
377
449
  }
378
-
379
- await addAuthTokenFileToGitIgnore(setupConfig.filename);
380
450
  });
381
451
  }
382
452
 
453
+ function addAuthTokenToSentryConfig(
454
+ configContents: string,
455
+ authToken: string | undefined,
456
+ setupConfig: CliSetupConfig,
457
+ ): string {
458
+ if (!authToken) {
459
+ return configContents;
460
+ }
461
+
462
+ if (setupConfig.likelyAlreadyHasAuthToken(configContents)) {
463
+ clack.log.warn(
464
+ `${chalk.cyan(
465
+ setupConfig.filename,
466
+ )} already has auth token. Will not add one.`,
467
+ );
468
+ return configContents;
469
+ }
470
+
471
+ const newContents = `${configContents}\n${setupConfig.tokenContent(
472
+ authToken,
473
+ )}\n`;
474
+ clack.log.success(
475
+ `Added auth token to ${chalk.cyan(
476
+ setupConfig.filename,
477
+ )} for you to test uploading ${setupConfig.name} locally.`,
478
+ );
479
+ return newContents;
480
+ }
481
+
482
+ function addOrgAndProjectToSentryConfig(
483
+ configContents: string,
484
+ org: string | undefined,
485
+ project: string | undefined,
486
+ setupConfig: CliSetupConfig,
487
+ ): string {
488
+ if (!org || !project) {
489
+ return configContents;
490
+ }
491
+
492
+ if (setupConfig.likelyAlreadyHasOrgAndProject(configContents)) {
493
+ clack.log.warn(
494
+ `${chalk.cyan(
495
+ setupConfig.filename,
496
+ )} already has org and project. Will not add them.`,
497
+ );
498
+ return configContents;
499
+ }
500
+
501
+ const newContents = `${configContents}\n${setupConfig.orgAndProjContent(
502
+ org,
503
+ project,
504
+ )}\n`;
505
+ clack.log.success(
506
+ `Added default org and project to ${chalk.cyan(
507
+ setupConfig.filename,
508
+ )} for you to test uploading ${setupConfig.name} locally.`,
509
+ );
510
+ return newContents;
511
+ }
512
+
513
+ function addUrlToSentryConfig(
514
+ configContents: string,
515
+ url: string | undefined,
516
+ setupConfig: CliSetupConfig,
517
+ ): string {
518
+ if (!url || !setupConfig.urlContent || !setupConfig.likelyAlreadyHasUrl) {
519
+ return configContents;
520
+ }
521
+
522
+ if (setupConfig.likelyAlreadyHasUrl(configContents)) {
523
+ clack.log.warn(
524
+ `${chalk.cyan(setupConfig.filename)} already has url. Will not add one.`,
525
+ );
526
+ return configContents;
527
+ }
528
+
529
+ const newContents = `${configContents}\n${setupConfig.urlContent(url)}\n`;
530
+ clack.log.success(
531
+ `Added default url to ${chalk.cyan(
532
+ setupConfig.filename,
533
+ )} for you to test uploading ${setupConfig.name} locally.`,
534
+ );
535
+ return newContents;
536
+ }
537
+
383
538
  export async function addDotEnvSentryBuildPluginFile(
384
539
  authToken: string,
385
540
  ): Promise<void> {
@@ -447,25 +602,23 @@ SENTRY_AUTH_TOKEN="${authToken}"
447
602
  }
448
603
  }
449
604
 
450
- await addAuthTokenFileToGitIgnore(SENTRY_DOT_ENV_FILE);
605
+ await addCliConfigFileToGitIgnore(SENTRY_DOT_ENV_FILE);
451
606
  }
452
607
 
453
- async function addAuthTokenFileToGitIgnore(filename: string): Promise<void> {
608
+ async function addCliConfigFileToGitIgnore(filename: string): Promise<void> {
454
609
  //TODO: Add a check to see if the file is already ignored in .gitignore
455
610
  try {
456
611
  await fs.promises.appendFile(
457
612
  path.join(process.cwd(), '.gitignore'),
458
- `\n# Sentry Auth Token\n${filename}\n`,
613
+ `\n# Sentry Config File\n${filename}\n`,
459
614
  { encoding: 'utf8' },
460
615
  );
461
616
  clack.log.success(
462
- chalk.greenBright(
463
- `Added ${chalk.bold(filename)} to ${chalk.bold('.gitignore')}.`,
464
- ),
617
+ `Added ${chalk.cyan(filename)} to ${chalk.cyan('.gitignore')}.`,
465
618
  );
466
619
  } catch {
467
620
  clack.log.error(
468
- `Failed adding ${chalk.bold(filename)} to ${chalk.bold(
621
+ `Failed adding ${chalk.cyan(filename)} to ${chalk.cyan(
469
622
  '.gitignore',
470
623
  )}. Please add it manually!`,
471
624
  );
@@ -585,7 +738,8 @@ export async function getOrAskForProjectData(
585
738
  | 'javascript-remix'
586
739
  | 'javascript-sveltekit'
587
740
  | 'apple-ios'
588
- | 'android',
741
+ | 'android'
742
+ | 'react-native',
589
743
  ): Promise<{
590
744
  sentryUrl: string;
591
745
  selfHosted: boolean;
@@ -713,7 +867,8 @@ async function askForWizardLogin(options: {
713
867
  | 'javascript-remix'
714
868
  | 'javascript-sveltekit'
715
869
  | 'apple-ios'
716
- | 'android';
870
+ | 'android'
871
+ | 'react-native';
717
872
  }): Promise<WizardProjectData> {
718
873
  Sentry.setTag('has-promo-code', !!options.promoCode);
719
874
 
@@ -0,0 +1,33 @@
1
+ import { satisfies, subset, valid, validRange } from 'semver';
2
+
3
+ export function fulfillsVersionRange({
4
+ version,
5
+ acceptableVersions,
6
+ canBeLatest,
7
+ }: {
8
+ version: string;
9
+ acceptableVersions: string;
10
+ canBeLatest: boolean;
11
+ }): boolean {
12
+ if (version === 'latest') {
13
+ return canBeLatest;
14
+ }
15
+
16
+ let cleanedUserVersion, isRange;
17
+
18
+ if (valid(version)) {
19
+ cleanedUserVersion = valid(version);
20
+ isRange = false;
21
+ } else if (validRange(version)) {
22
+ cleanedUserVersion = validRange(version);
23
+ isRange = true;
24
+ }
25
+
26
+ return (
27
+ // If the given version is a bogus format, this will still be undefined and we'll automatically reject it
28
+ !!cleanedUserVersion &&
29
+ (isRange
30
+ ? subset(cleanedUserVersion, acceptableVersions)
31
+ : satisfies(cleanedUserVersion, acceptableVersions))
32
+ );
33
+ }
@@ -13,7 +13,9 @@ export function createSentryCLIRC(
13
13
  const rcPath = path.join(directory, '.sentryclirc');
14
14
  fs.writeFileSync(rcPath, '[auth]\ntoken=' + params.auth_token);
15
15
 
16
- if (fs.existsSync('.gitignore')) {
16
+ if (!fs.existsSync('.gitignore')) {
17
+ fs.writeFileSync('.gitignore', '.sentryclirc');
18
+ } else {
17
19
  const gitIgnore = fs.readFileSync('.gitignore').toString();
18
20
  if (!gitIgnore.includes('.sentryclirc')) {
19
21
  fs.appendFileSync('.gitignore', '\n.sentryclirc');
@@ -5,6 +5,9 @@ export interface SentryProjectData {
5
5
  platform: string;
6
6
  organization: {
7
7
  slug: string;
8
+ links: {
9
+ organizationUrl: string;
10
+ };
8
11
  };
9
12
  keys: [{ dsn: { public: string } }];
10
13
  }