@sentry/wizard 3.10.0 → 3.12.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 (150) hide show
  1. package/CHANGELOG.md +54 -7
  2. package/dist/lib/Constants.d.ts +1 -0
  3. package/dist/lib/Constants.js +5 -0
  4. package/dist/lib/Constants.js.map +1 -1
  5. package/dist/lib/Steps/ChooseIntegration.js +8 -4
  6. package/dist/lib/Steps/ChooseIntegration.js.map +1 -1
  7. package/dist/lib/Steps/Integrations/Android.d.ts +9 -0
  8. package/dist/lib/Steps/Integrations/Android.js +86 -0
  9. package/dist/lib/Steps/Integrations/Android.js.map +1 -0
  10. package/dist/lib/Steps/Integrations/ReactNative.js +3 -3
  11. package/dist/lib/Steps/Integrations/ReactNative.js.map +1 -1
  12. package/dist/lib/Steps/PromptForParameters.js +36 -3
  13. package/dist/lib/Steps/PromptForParameters.js.map +1 -1
  14. package/dist/lib/Steps/SentryProjectSelector.js +1 -1
  15. package/dist/lib/Steps/SentryProjectSelector.js.map +1 -1
  16. package/dist/package.json +4 -3
  17. package/dist/src/android/android-wizard.d.ts +2 -0
  18. package/dist/src/android/android-wizard.js +225 -0
  19. package/dist/src/android/android-wizard.js.map +1 -0
  20. package/dist/src/android/code-tools.d.ts +47 -0
  21. package/dist/src/android/code-tools.js +173 -0
  22. package/dist/src/android/code-tools.js.map +1 -0
  23. package/dist/src/android/gradle.d.ts +62 -0
  24. package/dist/src/android/gradle.js +286 -0
  25. package/dist/src/android/gradle.js.map +1 -0
  26. package/dist/src/android/manifest.d.ts +57 -0
  27. package/dist/src/android/manifest.js +183 -0
  28. package/dist/src/android/manifest.js.map +1 -0
  29. package/dist/src/android/templates.d.ts +11 -0
  30. package/dist/src/android/templates.js +34 -0
  31. package/dist/src/android/templates.js.map +1 -0
  32. package/dist/src/apple/apple-wizard.js +123 -64
  33. package/dist/src/apple/apple-wizard.js.map +1 -1
  34. package/dist/src/apple/cocoapod.js +4 -3
  35. package/dist/src/apple/cocoapod.js.map +1 -1
  36. package/dist/src/apple/code-tools.d.ts +1 -1
  37. package/dist/src/apple/code-tools.js +43 -19
  38. package/dist/src/apple/code-tools.js.map +1 -1
  39. package/dist/src/apple/fastlane.d.ts +1 -1
  40. package/dist/src/apple/fastlane.js +12 -6
  41. package/dist/src/apple/fastlane.js.map +1 -1
  42. package/dist/src/apple/templates.d.ts +2 -2
  43. package/dist/src/apple/templates.js +4 -4
  44. package/dist/src/apple/templates.js.map +1 -1
  45. package/dist/src/apple/xcode-manager.d.ts +19 -3
  46. package/dist/src/apple/xcode-manager.js +126 -24
  47. package/dist/src/apple/xcode-manager.js.map +1 -1
  48. package/dist/src/nextjs/nextjs-wizard.js +49 -11
  49. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  50. package/dist/src/nextjs/templates.d.ts +2 -0
  51. package/dist/src/nextjs/templates.js +6 -2
  52. package/dist/src/nextjs/templates.js.map +1 -1
  53. package/dist/src/remix/remix-wizard.js +10 -20
  54. package/dist/src/remix/remix-wizard.js.map +1 -1
  55. package/dist/src/sourcemaps/sourcemaps-wizard.js +26 -13
  56. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  57. package/dist/src/sourcemaps/tools/nextjs.js +1 -1
  58. package/dist/src/sourcemaps/tools/nextjs.js.map +1 -1
  59. package/dist/src/sourcemaps/tools/sentry-cli.js +19 -16
  60. package/dist/src/sourcemaps/tools/sentry-cli.js.map +1 -1
  61. package/dist/src/sourcemaps/tools/vite.d.ts +2 -1
  62. package/dist/src/sourcemaps/tools/vite.js +123 -111
  63. package/dist/src/sourcemaps/tools/vite.js.map +1 -1
  64. package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
  65. package/dist/src/sourcemaps/tools/webpack.js +290 -25
  66. package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
  67. package/dist/src/sourcemaps/utils/detect-tool.d.ts +1 -1
  68. package/dist/src/sourcemaps/utils/detect-tool.js.map +1 -1
  69. package/dist/src/sveltekit/sdk-setup.js +5 -5
  70. package/dist/src/sveltekit/sdk-setup.js.map +1 -1
  71. package/dist/src/sveltekit/sveltekit-wizard.js +34 -44
  72. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  73. package/dist/src/telemetry.js +1 -0
  74. package/dist/src/telemetry.js.map +1 -1
  75. package/dist/src/utils/ast-utils.d.ts +9 -5
  76. package/dist/src/utils/ast-utils.js +26 -11
  77. package/dist/src/utils/ast-utils.js.map +1 -1
  78. package/dist/src/utils/clack-utils.d.ts +74 -28
  79. package/dist/src/utils/clack-utils.js +427 -264
  80. package/dist/src/utils/clack-utils.js.map +1 -1
  81. package/dist/src/utils/package-manager.d.ts +10 -0
  82. package/dist/{lib/Helper/PackageManager.js → src/utils/package-manager.js} +42 -74
  83. package/dist/src/utils/package-manager.js.map +1 -0
  84. package/dist/src/utils/release-registry.d.ts +1 -0
  85. package/dist/src/utils/release-registry.js +68 -0
  86. package/dist/src/utils/release-registry.js.map +1 -0
  87. package/dist/src/utils/sentrycli-utils.d.ts +4 -0
  88. package/dist/src/utils/sentrycli-utils.js +41 -0
  89. package/dist/src/utils/sentrycli-utils.js.map +1 -0
  90. package/dist/test/android/code-tools.test.d.ts +1 -0
  91. package/dist/test/android/code-tools.test.js +34 -0
  92. package/dist/test/android/code-tools.test.js.map +1 -0
  93. package/dist/test/sourcemaps/tools/vite.test.d.ts +1 -0
  94. package/dist/test/sourcemaps/tools/vite.test.js +132 -0
  95. package/dist/test/sourcemaps/tools/vite.test.js.map +1 -0
  96. package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
  97. package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
  98. package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
  99. package/dist/test/utils/ast-utils.test.js +42 -7
  100. package/dist/test/utils/ast-utils.test.js.map +1 -1
  101. package/dist/test/utils/clack-utils.test.d.ts +1 -0
  102. package/dist/test/utils/clack-utils.test.js +200 -0
  103. package/dist/test/utils/clack-utils.test.js.map +1 -0
  104. package/lib/Constants.ts +5 -0
  105. package/lib/Steps/ChooseIntegration.ts +7 -3
  106. package/lib/Steps/Integrations/Android.ts +23 -0
  107. package/lib/Steps/Integrations/ReactNative.ts +9 -3
  108. package/lib/Steps/PromptForParameters.ts +48 -3
  109. package/lib/Steps/SentryProjectSelector.ts +3 -1
  110. package/package.json +4 -3
  111. package/src/android/android-wizard.ts +204 -0
  112. package/src/android/code-tools.ts +170 -0
  113. package/src/android/gradle.ts +250 -0
  114. package/src/android/manifest.ts +180 -0
  115. package/src/android/templates.ts +88 -0
  116. package/src/apple/apple-wizard.ts +113 -35
  117. package/src/apple/cocoapod.ts +6 -3
  118. package/src/apple/code-tools.ts +46 -18
  119. package/src/apple/fastlane.ts +6 -12
  120. package/src/apple/templates.ts +2 -8
  121. package/src/apple/xcode-manager.ts +167 -25
  122. package/src/nextjs/nextjs-wizard.ts +72 -8
  123. package/src/nextjs/templates.ts +16 -2
  124. package/src/remix/remix-wizard.ts +10 -15
  125. package/src/sourcemaps/sourcemaps-wizard.ts +19 -5
  126. package/src/sourcemaps/tools/nextjs.ts +2 -2
  127. package/src/sourcemaps/tools/sentry-cli.ts +8 -7
  128. package/src/sourcemaps/tools/vite.ts +143 -79
  129. package/src/sourcemaps/tools/webpack.ts +369 -30
  130. package/src/sourcemaps/utils/detect-tool.ts +2 -1
  131. package/src/sveltekit/sdk-setup.ts +10 -6
  132. package/src/sveltekit/sveltekit-wizard.ts +5 -14
  133. package/src/telemetry.ts +2 -0
  134. package/src/utils/ast-utils.ts +29 -11
  135. package/src/utils/clack-utils.ts +485 -283
  136. package/src/utils/package-manager.ts +61 -0
  137. package/src/utils/release-registry.ts +19 -0
  138. package/src/utils/sentrycli-utils.ts +22 -0
  139. package/test/android/code-tools.test.ts +49 -0
  140. package/test/sourcemaps/tools/vite.test.ts +149 -0
  141. package/test/sourcemaps/tools/webpack.test.ts +303 -0
  142. package/test/utils/ast-utils.test.ts +28 -9
  143. package/test/utils/clack-utils.test.ts +142 -0
  144. package/dist/lib/Helper/PackageManager.d.ts +0 -22
  145. package/dist/lib/Helper/PackageManager.js.map +0 -1
  146. package/dist/src/utils/vendor/clack-custom-select.d.ts +0 -21
  147. package/dist/src/utils/vendor/clack-custom-select.js +0 -137
  148. package/dist/src/utils/vendor/clack-custom-select.js.map +0 -1
  149. package/lib/Helper/PackageManager.ts +0 -59
  150. package/src/utils/vendor/clack-custom-select.ts +0 -160
@@ -7,9 +7,10 @@
7
7
  import clack from '@clack/prompts';
8
8
  import * as fs from 'fs';
9
9
  import * as path from 'path';
10
- import * as xcManager from './xcode-manager';
10
+ import { XcodeProject } from './xcode-manager';
11
11
  import * as codeTools from './code-tools';
12
12
  import * as bash from '../utils/bash';
13
+ import * as SentryUtils from '../utils/sentrycli-utils';
13
14
  import { SentryProjectData, WizardOptions } from '../utils/types';
14
15
  import * as Sentry from '@sentry/node';
15
16
  import { traceStep, withTelemetry } from '../telemetry';
@@ -20,13 +21,12 @@ const xcode = require('xcode');
20
21
  /* eslint-enable @typescript-eslint/no-unused-vars */
21
22
 
22
23
  import {
23
- askForProjectSelection,
24
- askForSelfHosted,
25
- askForWizardLogin,
26
24
  askToInstallSentryCLI,
27
25
  printWelcome,
28
26
  abort,
29
27
  askForItemSelection,
28
+ confirmContinueEvenThoughNoGitRepo,
29
+ getOrAskForProjectData,
30
30
  } from '../utils/clack-utils';
31
31
 
32
32
  export async function runAppleWizard(options: WizardOptions): Promise<void> {
@@ -47,6 +47,8 @@ async function runAppleWizardWithTelementry(
47
47
  promoCode: options.promoCode,
48
48
  });
49
49
 
50
+ await confirmContinueEvenThoughNoGitRepo();
51
+
50
52
  const hasCli = bash.hasSentryCLI();
51
53
  Sentry.setTag('has-cli', hasCli);
52
54
  if (!hasCli) {
@@ -64,7 +66,7 @@ async function runAppleWizardWithTelementry(
64
66
  }
65
67
 
66
68
  const projectDir = process.cwd();
67
- const xcodeProjFiles = findFilesWithExtension(projectDir, '.xcodeproj');
69
+ const xcodeProjFiles = searchXcodeProject(projectDir);
68
70
 
69
71
  if (!xcodeProjFiles || xcodeProjFiles.length === 0) {
70
72
  clack.log.error(
@@ -92,42 +94,79 @@ async function runAppleWizardWithTelementry(
92
94
  }
93
95
 
94
96
  const pbxproj = path.join(projectDir, xcodeProjFile, 'project.pbxproj');
97
+
95
98
  if (!fs.existsSync(pbxproj)) {
96
- clack.log.error(`No pbxproj found at ${pbxproj}`);
99
+ clack.log.error(`No pbxproj found at ${xcodeProjFile}`);
100
+ await abort();
101
+ return;
102
+ }
103
+
104
+ const { project, apiKey } = await getSentryProjectAndApiKey(options);
105
+
106
+ const xcProject = new XcodeProject(pbxproj);
107
+
108
+ const availableTargets = xcProject.getAllTargets();
109
+
110
+ if (availableTargets.length == 0) {
111
+ clack.log.error(`No suttable target found in ${xcodeProjFile}`);
97
112
  await abort();
98
113
  return;
99
114
  }
100
115
 
101
- const { project, apiKey } = await getSentryProjectAndApiKey(
102
- options.promoCode,
103
- options.url,
116
+ const target =
117
+ availableTargets.length == 1
118
+ ? availableTargets[0]
119
+ : (
120
+ await traceStep('Choose target', () =>
121
+ askForItemSelection(
122
+ availableTargets,
123
+ 'Which target do you want to add Sentry to?',
124
+ ),
125
+ )
126
+ ).value;
127
+
128
+ SentryUtils.createSentryCLIRC(projectDir, { auth_token: apiKey.token });
129
+ clack.log.info(
130
+ 'We created a ".sentryclirc" file in your project directory in order to provide an auth token for Sentry CLI.\nIt was also added to your ".gitignore" file.\nAt your CI enviroment, you can set the SENTRY_AUTH_TOKEN environment variable instead. See https://docs.sentry.io/cli/configuration/#auth-token for more information.',
104
131
  );
105
132
 
106
- const hasCocoa = cocoapod.usesCocoaPod(projectDir);
133
+ let hasCocoa = cocoapod.usesCocoaPod(projectDir);
107
134
 
108
135
  if (hasCocoa) {
109
- const podAdded = await traceStep('Add CocoaPods reference', () =>
110
- cocoapod.addCocoaPods(projectDir),
111
- );
112
- if (!podAdded) {
113
- clack.log.warn(
114
- "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",
136
+ const pm = (
137
+ await traceStep('Choose a package manager', () =>
138
+ askForItemSelection(
139
+ ['Swift Package Manager', 'CocoaPods'],
140
+ 'Which package manager would you like to use to add Sentry?',
141
+ ),
142
+ )
143
+ ).value;
144
+
145
+ hasCocoa = pm === 'CocoaPods';
146
+ if (hasCocoa) {
147
+ const podAdded = await traceStep('Add CocoaPods reference', () =>
148
+ cocoapod.addCocoaPods(projectDir),
115
149
  );
150
+ if (!podAdded) {
151
+ clack.log.warn(
152
+ "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",
153
+ );
154
+ }
116
155
  }
117
156
  }
118
157
 
119
158
  traceStep('Update Xcode project', () => {
120
- xcManager.updateXcodeProject(pbxproj, project, apiKey, !hasCocoa, true);
159
+ xcProject.updateXcodeProject(project, target, apiKey, !hasCocoa, true);
121
160
  });
122
161
 
123
162
  Sentry.setTag('package-manager', hasCocoa ? 'cocoapods' : 'SPM');
124
- const projSource = path.join(
125
- projectDir,
126
- xcodeProjFile.replace('.xcodeproj', ''),
127
- );
128
163
  const codeAdded = traceStep('Add code snippet', () => {
164
+ const files = xcProject.filesForTarget(target);
165
+ if (files === undefined || files.length == 0) return false;
166
+
129
167
  return codeTools.addCodeSnippetToProject(
130
- projSource,
168
+ projectDir,
169
+ files,
131
170
  project.keys[0].dsn.public,
132
171
  );
133
172
  });
@@ -144,35 +183,74 @@ async function runAppleWizardWithTelementry(
144
183
  'Found a Fastfile in your project. Do you want to configure a lane to upload debug symbols to Sentry?',
145
184
  });
146
185
  if (addLane) {
147
- await traceStep('Configure fastlane', () =>
186
+ const added = await traceStep('Configure fastlane', () =>
148
187
  fastlane.addSentryToFastlane(
149
188
  projectDir,
150
189
  project.organization.slug,
151
190
  project.slug,
152
- apiKey.token,
153
191
  ),
154
192
  );
193
+ if (added) {
194
+ clack.log.step(
195
+ '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.',
196
+ );
197
+ } else {
198
+ clack.log.warn(
199
+ 'Could not edit your fastlane file to upload debug symbols to Sentry. Please follow the instructions at https://docs.sentry.io/platforms/apple/guides/ios/dsym/#fastlane',
200
+ );
201
+ }
155
202
  }
156
203
  }
157
204
 
158
- clack.log.success('Sentry was successfully added to your project!');
205
+ clack.log.success(
206
+ 'Sentry was successfully added to your project! Run your project to send your first event to Sentry. Go to Sentry.io to see whether everything is working fine.',
207
+ );
159
208
  }
160
209
 
161
210
  //Prompt for Sentry project and API key
162
211
  async function getSentryProjectAndApiKey(
163
- promoCode?: string,
164
- url?: string,
212
+ options: WizardOptions,
165
213
  ): Promise<{ project: SentryProjectData; apiKey: { token: string } }> {
166
- const { url: sentryUrl } = await askForSelfHosted(url);
214
+ const { selectedProject, authToken } = await getOrAskForProjectData(options);
215
+ return { project: selectedProject, apiKey: { token: authToken } };
216
+ }
167
217
 
168
- const { projects, apiKeys } = await askForWizardLogin({
169
- promoCode: promoCode,
170
- url: sentryUrl,
171
- platform: 'apple-ios',
172
- });
218
+ function searchXcodeProject(at: string): string[] {
219
+ const projs = findFilesWithExtension(at, '.xcodeproj');
220
+ if (projs.length > 0) {
221
+ return projs;
222
+ }
223
+
224
+ const workspace = findFilesWithExtension(at, '.xcworkspace');
225
+ if (workspace.length == 0) {
226
+ return [];
227
+ }
228
+
229
+ const xsworkspacedata = path.join(
230
+ at,
231
+ workspace[0],
232
+ 'contents.xcworkspacedata',
233
+ );
234
+ if (!fs.existsSync(xsworkspacedata)) {
235
+ return [];
236
+ }
237
+ const groupRegex = /location *= *"group:([^"]+)"/gim;
238
+ const content = fs.readFileSync(xsworkspacedata, 'utf8');
239
+ let matches = groupRegex.exec(content);
173
240
 
174
- const selectedProject = await askForProjectSelection(projects);
175
- return { project: selectedProject, apiKey: apiKeys };
241
+ while (matches) {
242
+ const group = matches[1];
243
+ const groupPath = path.join(at, group);
244
+ if (
245
+ !group.endsWith('Pods.xcodeproj') &&
246
+ group.endsWith('.xcodeproj') &&
247
+ fs.existsSync(groupPath)
248
+ ) {
249
+ projs.push(group);
250
+ }
251
+ matches = groupRegex.exec(content);
252
+ }
253
+ return projs;
176
254
  }
177
255
 
178
256
  //find files with the given extension
@@ -42,14 +42,17 @@ export async function addCocoaPods(projPath: string): Promise<boolean> {
42
42
 
43
43
  const loginSpinner = clack.spinner();
44
44
 
45
+ clack.log.step('Sentry pod added to the project podFile.');
45
46
  loginSpinner.start("Running 'pod install'. This may take a few minutes...");
46
47
 
47
48
  try {
48
49
  await bash.execute('pod install --silent');
49
- loginSpinner.stop('Sentry pod added to the project.');
50
+ loginSpinner.stop('Running "pod install"');
50
51
  } catch (e) {
51
- clack.log.error("'pod install' failed. You will need to run it manually.");
52
- loginSpinner.stop();
52
+ loginSpinner.stop('Running "pod install"');
53
+ clack.log.error(
54
+ 'Failed to run "pod install". You can run it manually for more details.',
55
+ );
53
56
  Sentry.captureException('Sentry pod install failed.');
54
57
  }
55
58
 
@@ -2,12 +2,14 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as templates from './templates';
4
4
  import * as Sentry from '@sentry/node';
5
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
6
+ import clack from '@clack/prompts';
5
7
 
6
8
  const swiftAppLaunchRegex =
7
- /(func\s+application\s*\(_\sapplication:[^,]+,\s*didFinishLaunchingWithOptions[^,]+:[^)]+\)\s+->\s+Bool\s+{)|(init\s*\([^)]*\)\s*{)/im;
9
+ /(func\s+application\s*\(_\sapplication:[^,]+,\s*didFinishLaunchingWithOptions[^,]+:[^)]+\)\s+->\s+Bool\s+{)|func\s+applicationDidFinishLaunching\(_\s+aNotification:\s+Notification\)\s+{/im;
8
10
  const objcAppLaunchRegex =
9
11
  /-\s*\(BOOL\)\s*application:\s*\(UIApplication\s*\*\)\s*application\s+didFinishLaunchingWithOptions:\s*\(NSDictionary\s*\*\)\s*launchOptions\s*{/im;
10
- const swiftUIRegex = /struct[^:]+:\s*App\s*{/im;
12
+ const swiftUIRegex = /@main\s+struct[^:]+:\s*App\s*{/im;
11
13
 
12
14
  function isAppDelegateFile(filePath: string): boolean {
13
15
  const appLaunchRegex = filePath.toLowerCase().endsWith('.swift')
@@ -18,34 +20,41 @@ function isAppDelegateFile(filePath: string): boolean {
18
20
  return appLaunchRegex.test(fileContent) || swiftUIRegex.test(fileContent);
19
21
  }
20
22
 
21
- function findAppDidFinishLaunchingWithOptions(dir: string): string | null {
22
- const files = fs.readdirSync(dir);
23
+ function findAppDidFinishLaunchingWithOptions(
24
+ dir: string,
25
+ files: string[] | undefined = undefined,
26
+ ): string | null {
27
+ if (!files) {
28
+ files = fs.readdirSync(dir);
29
+ files = files.map((f) => path.join(dir, f));
30
+ }
31
+
23
32
  //iterate over subdirectories later,
24
33
  //the appdelegate usually is in the top level
25
34
  const dirs: string[] = [];
26
35
 
27
- for (const file of files) {
28
- const filePath = path.join(dir, file);
36
+ for (const filePath of files) {
29
37
  if (
30
- file.endsWith('.swift') ||
31
- file.endsWith('.m') ||
32
- file.endsWith('.mm')
38
+ filePath.endsWith('.swift') ||
39
+ filePath.endsWith('.m') ||
40
+ filePath.endsWith('.mm')
33
41
  ) {
34
- if (isAppDelegateFile(filePath)) {
42
+ if (fs.existsSync(filePath) && isAppDelegateFile(filePath)) {
35
43
  return filePath;
36
44
  }
37
45
  } else if (
38
- !file.startsWith('.') &&
39
- !file.endsWith('.xcodeproj') &&
40
- !file.endsWith('.xcassets') &&
46
+ !filePath.startsWith('.') &&
47
+ !filePath.endsWith('.xcodeproj') &&
48
+ !filePath.endsWith('.xcassets') &&
49
+ fs.existsSync(filePath) &&
41
50
  fs.lstatSync(filePath).isDirectory()
42
51
  ) {
43
- dirs.push(file);
52
+ dirs.push(filePath);
44
53
  }
45
54
  }
46
55
 
47
56
  for (const dr of dirs) {
48
- const result = findAppDidFinishLaunchingWithOptions(path.join(dir, dr));
57
+ const result = findAppDidFinishLaunchingWithOptions(dr);
49
58
  if (result) return result;
50
59
  }
51
60
  return null;
@@ -53,9 +62,10 @@ function findAppDidFinishLaunchingWithOptions(dir: string): string | null {
53
62
 
54
63
  export function addCodeSnippetToProject(
55
64
  projPath: string,
65
+ files: string[],
56
66
  dsn: string,
57
67
  ): boolean {
58
- const appDelegate = findAppDidFinishLaunchingWithOptions(projPath);
68
+ const appDelegate = findAppDidFinishLaunchingWithOptions(projPath, files);
59
69
  if (!appDelegate) {
60
70
  return false;
61
71
  }
@@ -77,6 +87,9 @@ export function addCodeSnippetToProject(
77
87
 
78
88
  if (fileContent.includes(checkForSentryInit)) {
79
89
  //already initialized
90
+ clack.log.info(
91
+ 'Sentry is already initialized in your AppDelegate. Skipping adding the code snippet.',
92
+ );
80
93
  return true;
81
94
  }
82
95
 
@@ -92,13 +105,28 @@ export function addCodeSnippetToProject(
92
105
  }
93
106
 
94
107
  const insertIndex = match.index + match[0].length;
95
- const newFileContent =
96
- (fileContent.indexOf(importStatement) >= 0 ? '' : importStatement) +
108
+ let newFileContent =
97
109
  fileContent.slice(0, insertIndex) +
98
110
  '\n' +
99
111
  codeSnippet +
100
112
  fileContent.slice(insertIndex);
113
+
114
+ if (newFileContent.indexOf(importStatement) < 0) {
115
+ const firstImport = /^[ \t]*import +\w+.*$/m.exec(newFileContent);
116
+ if (firstImport) {
117
+ const importIndex = firstImport.index + firstImport[0].length;
118
+ newFileContent =
119
+ newFileContent.slice(0, importIndex) +
120
+ '\n' +
121
+ importStatement +
122
+ newFileContent.slice(importIndex);
123
+ } else {
124
+ newFileContent = importStatement + newFileContent;
125
+ }
126
+ }
127
+
101
128
  fs.writeFileSync(appDelegate, newFileContent, 'utf8');
102
129
 
130
+ clack.log.step('Added Sentry initialization code snippet to ' + appDelegate);
103
131
  return true;
104
132
  }
@@ -2,6 +2,8 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as templates from './templates';
4
4
  import { askForItemSelection } from '../utils/clack-utils';
5
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
6
+ import clack from '@clack/prompts';
5
7
 
6
8
  export function fastFile(projectPath: string): string | null {
7
9
  const fastlanePath = path.join(projectPath, 'fastlane', 'Fastfile');
@@ -75,7 +77,6 @@ function addSentryToLane(
75
77
  lane: { index: number; length: number; name: string },
76
78
  org: string,
77
79
  project: string,
78
- token: string,
79
80
  ): string {
80
81
  const laneContent = content.slice(lane.index, lane.index + lane.length);
81
82
  const sentryCLIMatch = /sentry_cli\s*\([^)]+\)/gim.exec(laneContent);
@@ -83,7 +84,7 @@ function addSentryToLane(
83
84
  // Sentry already added to lane. Update it.
84
85
  return (
85
86
  content.slice(0, sentryCLIMatch.index + lane.index) +
86
- templates.getFastlaneSnippet(org, project, token).trim() +
87
+ templates.getFastlaneSnippet(org, project).trim() +
87
88
  content.slice(
88
89
  sentryCLIMatch.index + sentryCLIMatch[0].length + lane.index,
89
90
  )
@@ -94,7 +95,7 @@ function addSentryToLane(
94
95
  return (
95
96
  content.slice(0, lane.index + lane.length) +
96
97
  '\n' +
97
- templates.getFastlaneSnippet(org, project, token) +
98
+ templates.getFastlaneSnippet(org, project) +
98
99
  '\n' +
99
100
  content.slice(lane.index + lane.length)
100
101
  );
@@ -104,7 +105,6 @@ export async function addSentryToFastlane(
104
105
  projectPath: string,
105
106
  org: string,
106
107
  project: string,
107
- token: string,
108
108
  ): Promise<boolean> {
109
109
  const fastFilePath = fastFile(projectPath);
110
110
  if (!fastFilePath) {
@@ -125,18 +125,13 @@ export async function addSentryToFastlane(
125
125
  lanes?.forEach((l) => (l.index += platform.index));
126
126
 
127
127
  if (!lanes || lanes.length === 0) {
128
+ clack.log.warn('No suitable lanes in your Fastfile.');
128
129
  return false;
129
130
  }
130
131
 
131
132
  let newFileContent: string | undefined;
132
133
  if (lanes.length === 1) {
133
- newFileContent = addSentryToLane(
134
- fileContent,
135
- lanes[0],
136
- org,
137
- project,
138
- token,
139
- );
134
+ newFileContent = addSentryToLane(fileContent, lanes[0], org, project);
140
135
  } else {
141
136
  const laneNames = lanes.map((l) => l.name);
142
137
  const selectedLane = await askForItemSelection(
@@ -151,7 +146,6 @@ export async function addSentryToFastlane(
151
146
  lanes[selectedLane.index],
152
147
  org,
153
148
  project,
154
- token,
155
149
  );
156
150
  }
157
151
 
@@ -1,11 +1,10 @@
1
1
  export function getRunScriptTemplate(
2
2
  orgSlug: string,
3
3
  projectSlug: string,
4
- apiKey: string,
5
4
  uploadSource = true,
6
5
  ): string {
7
6
  // eslint-disable-next-line no-useless-escape
8
- return `# This script is responsable to upload debug symbols and source context for Sentry.\\nif which sentry-cli >/dev/null; then\\nexport SENTRY_ORG=${orgSlug}\\nexport SENTRY_PROJECT=${projectSlug}\\nexport SENTRY_AUTH_TOKEN=${apiKey}\\nERROR=$(sentry-cli debug-files upload ${
7
+ return `# This script is responsable to upload debug symbols and source context for Sentry.\\nif which sentry-cli >/dev/null; then\\nexport SENTRY_ORG=${orgSlug}\\nexport SENTRY_PROJECT=${projectSlug}\\nERROR=$(sentry-cli debug-files upload ${
9
8
  uploadSource ? '--include-sources ' : ''
10
9
  }"$DWARF_DSYM_FOLDER_PATH" 2>&1 >/dev/null)\\nif [ ! $? -eq 0 ]; then\\necho "warning: sentry-cli - $ERROR"\\nfi\\nelse\\necho "warning: sentry-cli not installed, download from https://github.com/getsentry/sentry-cli/releases"\\nfi\\n`;
11
10
  }
@@ -41,13 +40,8 @@ export function getObjcSnippet(dsn: string): string {
41
40
  [SentrySDK captureMessage:@"This app uses Sentry!"];\n`;
42
41
  }
43
42
 
44
- export function getFastlaneSnippet(
45
- org: string,
46
- project: string,
47
- token: string,
48
- ): string {
43
+ export function getFastlaneSnippet(org: string, project: string): string {
49
44
  return ` sentry_cli(
50
- auth_token: '${token}',
51
45
  org_slug: '${org}',
52
46
  project_slug: '${project}',
53
47
  include_sources: true