@sentry/wizard 3.39.0 → 3.41.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 (221) hide show
  1. package/CHANGELOG.md +12 -1
  2. package/README.md +21 -21
  3. package/bin.ts +5 -0
  4. package/codecov.yml +15 -0
  5. package/dist/bin.js +4 -0
  6. package/dist/bin.js.map +1 -1
  7. package/dist/e2e-tests/jest.config.d.ts +1 -0
  8. package/dist/e2e-tests/jest.config.js +1 -0
  9. package/dist/e2e-tests/jest.config.js.map +1 -1
  10. package/dist/e2e-tests/tests/flutter.test.d.ts +1 -0
  11. package/dist/e2e-tests/tests/flutter.test.js +190 -0
  12. package/dist/e2e-tests/tests/flutter.test.js.map +1 -0
  13. package/dist/e2e-tests/utils/index.d.ts +11 -0
  14. package/dist/e2e-tests/utils/index.js +36 -1
  15. package/dist/e2e-tests/utils/index.js.map +1 -1
  16. package/dist/lib/Constants.d.ts +1 -0
  17. package/dist/lib/Constants.js +5 -0
  18. package/dist/lib/Constants.js.map +1 -1
  19. package/dist/package.json +3 -2
  20. package/dist/src/apple/apple-wizard.js +2 -3
  21. package/dist/src/apple/apple-wizard.js.map +1 -1
  22. package/dist/src/apple/code-tools.d.ts +10 -0
  23. package/dist/src/apple/code-tools.js +16 -12
  24. package/dist/src/apple/code-tools.js.map +1 -1
  25. package/dist/src/apple/fastlane.d.ts +23 -0
  26. package/dist/src/apple/fastlane.js +11 -7
  27. package/dist/src/apple/fastlane.js.map +1 -1
  28. package/dist/src/apple/templates.d.ts +1 -1
  29. package/dist/src/apple/templates.js +0 -2
  30. package/dist/src/apple/templates.js.map +1 -1
  31. package/dist/src/apple/xcode-manager.d.ts +13 -9
  32. package/dist/src/apple/xcode-manager.js +146 -61
  33. package/dist/src/apple/xcode-manager.js.map +1 -1
  34. package/dist/src/flutter/code-tools.d.ts +17 -0
  35. package/dist/src/flutter/code-tools.js +263 -0
  36. package/dist/src/flutter/code-tools.js.map +1 -0
  37. package/dist/src/flutter/flutter-wizard.d.ts +2 -0
  38. package/dist/src/flutter/flutter-wizard.js +171 -0
  39. package/dist/src/flutter/flutter-wizard.js.map +1 -0
  40. package/dist/src/flutter/templates.d.ts +9 -0
  41. package/dist/src/flutter/templates.js +40 -0
  42. package/dist/src/flutter/templates.js.map +1 -0
  43. package/dist/src/nextjs/nextjs-wizard.js +5 -3
  44. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  45. package/dist/src/nuxt/nuxt-wizard.js +6 -4
  46. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  47. package/dist/src/nuxt/sdk-setup.d.ts +1 -1
  48. package/dist/src/nuxt/sdk-setup.js +2 -1
  49. package/dist/src/nuxt/sdk-setup.js.map +1 -1
  50. package/dist/src/react-native/react-native-wizard.js +5 -3
  51. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  52. package/dist/src/remix/remix-wizard.js +5 -3
  53. package/dist/src/remix/remix-wizard.js.map +1 -1
  54. package/dist/src/run.d.ts +2 -1
  55. package/dist/src/run.js +40 -32
  56. package/dist/src/run.js.map +1 -1
  57. package/dist/src/sveltekit/sveltekit-wizard.js +5 -3
  58. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  59. package/dist/src/utils/clack-utils.d.ts +4 -2
  60. package/dist/src/utils/clack-utils.js +18 -12
  61. package/dist/src/utils/clack-utils.js.map +1 -1
  62. package/dist/src/utils/package-manager.d.ts +1 -0
  63. package/dist/src/utils/package-manager.js +5 -0
  64. package/dist/src/utils/package-manager.js.map +1 -1
  65. package/dist/src/utils/types.d.ts +9 -0
  66. package/dist/src/utils/types.js.map +1 -1
  67. package/dist/test/apple/cocoapod.test.d.ts +1 -0
  68. package/dist/test/apple/cocoapod.test.js +409 -0
  69. package/dist/test/apple/cocoapod.test.js.map +1 -0
  70. package/dist/test/apple/code-tools.test.d.ts +1 -0
  71. package/dist/test/apple/code-tools.test.js +673 -0
  72. package/dist/test/apple/code-tools.test.js.map +1 -0
  73. package/dist/test/apple/fastfile.test.d.ts +1 -0
  74. package/dist/test/apple/fastfile.test.js +431 -0
  75. package/dist/test/apple/fastfile.test.js.map +1 -0
  76. package/dist/test/apple/templates.test.d.ts +1 -0
  77. package/dist/test/apple/templates.test.js +73 -0
  78. package/dist/test/apple/templates.test.js.map +1 -0
  79. package/dist/test/apple/xcode-manager.test.d.ts +1 -0
  80. package/dist/test/apple/xcode-manager.test.js +834 -0
  81. package/dist/test/apple/xcode-manager.test.js.map +1 -0
  82. package/dist/test/flutter/code-tools.test.d.ts +1 -0
  83. package/dist/test/flutter/code-tools.test.js +84 -0
  84. package/dist/test/flutter/code-tools.test.js.map +1 -0
  85. package/dist/test/flutter/templates.test.d.ts +1 -0
  86. package/dist/test/flutter/templates.test.js +41 -0
  87. package/dist/test/flutter/templates.test.js.map +1 -0
  88. package/dist/test/utils/clack-utils.test.js +89 -0
  89. package/dist/test/utils/clack-utils.test.js.map +1 -1
  90. package/e2e-tests/README.md +5 -1
  91. package/e2e-tests/jest.config.ts +1 -0
  92. package/e2e-tests/test-applications/apple/damaged-missing-configuration-list/Project.xcodeproj/project.pbxproj +52 -0
  93. package/e2e-tests/test-applications/apple/damaged-missing-configuration-list/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +78 -0
  94. package/e2e-tests/test-applications/apple/no-targets/Project.xcodeproj/project.pbxproj +62 -0
  95. package/e2e-tests/test-applications/apple/no-targets/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +78 -0
  96. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project.xcodeproj/project.pbxproj +470 -0
  97. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project.xcodeproj/xcshareddata/xcschemes/Project1.xcscheme +78 -0
  98. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project1/ContentView.swift +7 -0
  99. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project1/Project1App.swift +10 -0
  100. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project2/ContentView.swift +7 -0
  101. package/e2e-tests/test-applications/apple/spm-swiftui-multi-targets/Project2/Project2App.swift +10 -0
  102. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Project.xcodeproj/project.pbxproj +382 -0
  103. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Project.xcodeproj/xcshareddata/xcschemes/Project.xcscheme +78 -0
  104. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Sources/ContentView.swift +7 -0
  105. package/e2e-tests/test-applications/apple/spm-swiftui-single-target/Sources/MainApp.swift +10 -0
  106. package/e2e-tests/test-applications/flutter-test-app/.metadata +45 -0
  107. package/e2e-tests/test-applications/flutter-test-app/README.md +16 -0
  108. package/e2e-tests/test-applications/flutter-test-app/analysis_options.yaml +28 -0
  109. package/e2e-tests/test-applications/flutter-test-app/android/app/build.gradle +44 -0
  110. package/e2e-tests/test-applications/flutter-test-app/android/app/src/debug/AndroidManifest.xml +7 -0
  111. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/AndroidManifest.xml +45 -0
  112. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/kotlin/com/example/flutter_magic/MainActivity.kt +5 -0
  113. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable/launch_background.xml +12 -0
  114. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
  115. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  116. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  117. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  118. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  119. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  120. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values/styles.xml +18 -0
  121. package/e2e-tests/test-applications/flutter-test-app/android/app/src/main/res/values-night/styles.xml +18 -0
  122. package/e2e-tests/test-applications/flutter-test-app/android/app/src/profile/AndroidManifest.xml +7 -0
  123. package/e2e-tests/test-applications/flutter-test-app/android/build.gradle +18 -0
  124. package/e2e-tests/test-applications/flutter-test-app/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  125. package/e2e-tests/test-applications/flutter-test-app/android/gradle.properties +3 -0
  126. package/e2e-tests/test-applications/flutter-test-app/android/settings.gradle +25 -0
  127. package/e2e-tests/test-applications/flutter-test-app/lib/main.dart +125 -0
  128. package/e2e-tests/test-applications/flutter-test-app/linux/CMakeLists.txt +145 -0
  129. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/CMakeLists.txt +88 -0
  130. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.cc +11 -0
  131. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugin_registrant.h +15 -0
  132. package/e2e-tests/test-applications/flutter-test-app/linux/flutter/generated_plugins.cmake +23 -0
  133. package/e2e-tests/test-applications/flutter-test-app/linux/main.cc +6 -0
  134. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.cc +124 -0
  135. package/e2e-tests/test-applications/flutter-test-app/linux/my_application.h +18 -0
  136. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Debug.xcconfig +2 -0
  137. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/Flutter-Release.xcconfig +2 -0
  138. package/e2e-tests/test-applications/flutter-test-app/macos/Flutter/GeneratedPluginRegistrant.swift +10 -0
  139. package/e2e-tests/test-applications/flutter-test-app/macos/Podfile +43 -0
  140. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/AppDelegate.swift +9 -0
  141. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  142. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png +0 -0
  143. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png +0 -0
  144. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png +0 -0
  145. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png +0 -0
  146. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png +0 -0
  147. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png +0 -0
  148. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png +0 -0
  149. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Base.lproj/MainMenu.xib +343 -0
  150. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/AppInfo.xcconfig +14 -0
  151. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Debug.xcconfig +2 -0
  152. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Release.xcconfig +2 -0
  153. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Configs/Warnings.xcconfig +13 -0
  154. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/DebugProfile.entitlements +12 -0
  155. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Info.plist +32 -0
  156. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/MainFlutterWindow.swift +15 -0
  157. package/e2e-tests/test-applications/flutter-test-app/macos/Runner/Release.entitlements +8 -0
  158. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.pbxproj +705 -0
  159. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  160. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +98 -0
  161. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/contents.xcworkspacedata +7 -0
  162. package/e2e-tests/test-applications/flutter-test-app/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  163. package/e2e-tests/test-applications/flutter-test-app/macos/RunnerTests/RunnerTests.swift +12 -0
  164. package/e2e-tests/test-applications/flutter-test-app/pubspec.lock +213 -0
  165. package/e2e-tests/test-applications/flutter-test-app/pubspec.yaml +89 -0
  166. package/e2e-tests/test-applications/flutter-test-app/test/widget_test.dart +30 -0
  167. package/e2e-tests/test-applications/flutter-test-app/web/favicon.png +0 -0
  168. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-192.png +0 -0
  169. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-512.png +0 -0
  170. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-192.png +0 -0
  171. package/e2e-tests/test-applications/flutter-test-app/web/icons/Icon-maskable-512.png +0 -0
  172. package/e2e-tests/test-applications/flutter-test-app/web/index.html +38 -0
  173. package/e2e-tests/test-applications/flutter-test-app/web/manifest.json +35 -0
  174. package/e2e-tests/test-applications/flutter-test-app/windows/CMakeLists.txt +108 -0
  175. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/CMakeLists.txt +109 -0
  176. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.cc +11 -0
  177. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugin_registrant.h +15 -0
  178. package/e2e-tests/test-applications/flutter-test-app/windows/flutter/generated_plugins.cmake +23 -0
  179. package/e2e-tests/test-applications/flutter-test-app/windows/runner/CMakeLists.txt +40 -0
  180. package/e2e-tests/test-applications/flutter-test-app/windows/runner/Runner.rc +121 -0
  181. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.cpp +71 -0
  182. package/e2e-tests/test-applications/flutter-test-app/windows/runner/flutter_window.h +33 -0
  183. package/e2e-tests/test-applications/flutter-test-app/windows/runner/main.cpp +43 -0
  184. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resource.h +16 -0
  185. package/e2e-tests/test-applications/flutter-test-app/windows/runner/resources/app_icon.ico +0 -0
  186. package/e2e-tests/test-applications/flutter-test-app/windows/runner/runner.exe.manifest +14 -0
  187. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.cpp +65 -0
  188. package/e2e-tests/test-applications/flutter-test-app/windows/runner/utils.h +19 -0
  189. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.cpp +288 -0
  190. package/e2e-tests/test-applications/flutter-test-app/windows/runner/win32_window.h +102 -0
  191. package/e2e-tests/tests/flutter.test.ts +127 -0
  192. package/e2e-tests/utils/index.ts +33 -0
  193. package/lib/Constants.ts +5 -0
  194. package/package.json +3 -2
  195. package/src/apple/apple-wizard.ts +2 -3
  196. package/src/apple/code-tools.ts +21 -6
  197. package/src/apple/fastlane.ts +18 -2
  198. package/src/apple/templates.ts +2 -2
  199. package/src/apple/xcode-manager.ts +186 -99
  200. package/src/flutter/code-tools.ts +284 -0
  201. package/src/flutter/flutter-wizard.ts +164 -0
  202. package/src/flutter/templates.ts +90 -0
  203. package/src/nextjs/nextjs-wizard.ts +5 -2
  204. package/src/nuxt/nuxt-wizard.ts +6 -3
  205. package/src/nuxt/sdk-setup.ts +2 -0
  206. package/src/react-native/react-native-wizard.ts +5 -2
  207. package/src/remix/remix-wizard.ts +5 -2
  208. package/src/run.ts +9 -0
  209. package/src/sveltekit/sveltekit-wizard.ts +5 -2
  210. package/src/utils/clack-utils.ts +16 -4
  211. package/src/utils/package-manager.ts +6 -0
  212. package/src/utils/types.ts +10 -0
  213. package/test/apple/cocoapod.test.ts +306 -0
  214. package/test/apple/code-tools.test.ts +1042 -0
  215. package/test/apple/fastfile.test.ts +550 -0
  216. package/test/apple/templates.test.ts +191 -0
  217. package/test/apple/xcode-manager.test.ts +1066 -0
  218. package/test/flutter/code-tools.test.ts +212 -0
  219. package/test/flutter/templates.test.ts +100 -0
  220. package/test/utils/clack-utils.test.ts +92 -0
  221. package/types/xcode.d.ts +526 -0
package/src/run.ts CHANGED
@@ -5,6 +5,7 @@ import { runReactNativeWizard } from './react-native/react-native-wizard';
5
5
 
6
6
  import { run as legacyRun } from '../lib/Setup';
7
7
  import type { PreselectedProject, WizardOptions } from './utils/types';
8
+ import { runFlutterWizard } from './flutter/flutter-wizard';
8
9
  import { runAndroidWizard } from './android/android-wizard';
9
10
  import { runAppleWizard } from './apple/apple-wizard';
10
11
  import { runNextjsWizard } from './nextjs/nextjs-wizard';
@@ -18,6 +19,7 @@ import type { PackageDotJson } from './utils/package-json';
18
19
 
19
20
  type WizardIntegration =
20
21
  | 'reactNative'
22
+ | 'flutter'
21
23
  | 'ios'
22
24
  | 'android'
23
25
  | 'cordova'
@@ -54,6 +56,7 @@ type Args = {
54
56
  org?: string;
55
57
  project?: string;
56
58
  saas?: boolean;
59
+ forceInstall?: boolean;
57
60
  };
58
61
 
59
62
  function preSelectedProjectArgsToObject(
@@ -100,6 +103,7 @@ export async function run(argv: Args) {
100
103
  message: 'What do you want to set up?',
101
104
  options: [
102
105
  { value: 'reactNative', label: 'React Native' },
106
+ { value: 'flutter', label: 'Flutter' },
103
107
  { value: 'ios', label: 'iOS' },
104
108
  { value: 'android', label: 'Android' },
105
109
  { value: 'cordova', label: 'Cordova' },
@@ -129,6 +133,7 @@ export async function run(argv: Args) {
129
133
  projectSlug: finalArgs.project,
130
134
  saas: finalArgs.saas,
131
135
  preSelectedProject: preSelectedProjectArgsToObject(finalArgs),
136
+ forceInstall: finalArgs.forceInstall,
132
137
  };
133
138
 
134
139
  switch (integration) {
@@ -139,6 +144,10 @@ export async function run(argv: Args) {
139
144
  });
140
145
  break;
141
146
 
147
+ case 'flutter':
148
+ await runFlutterWizard(wizardOptions);
149
+ break;
150
+
142
151
  case 'ios':
143
152
  await runAppleWizard(wizardOptions);
144
153
  break;
@@ -41,10 +41,12 @@ export async function runSvelteKitWizard(
41
41
  export async function runSvelteKitWizardWithTelemetry(
42
42
  options: WizardOptions,
43
43
  ): Promise<void> {
44
+ const { promoCode, telemetryEnabled, forceInstall } = options;
45
+
44
46
  printWelcome({
45
47
  wizardName: 'Sentry SvelteKit Wizard',
46
- promoCode: options.promoCode,
47
- telemetryEnabled: options.telemetryEnabled,
48
+ promoCode,
49
+ telemetryEnabled,
48
50
  });
49
51
 
50
52
  await confirmContinueIfNoOrDirtyGitRepo();
@@ -98,6 +100,7 @@ export async function runSvelteKitWizardWithTelemetry(
98
100
  packageName: '@sentry/sveltekit@^8',
99
101
  packageNameDisplayLabel: '@sentry/sveltekit',
100
102
  alreadyInstalled: sdkAlreadyInstalled,
103
+ forceInstall,
101
104
  });
102
105
 
103
106
  await addDotEnvSentryBuildPluginFile(authToken);
@@ -207,6 +207,8 @@ export async function confirmContinueIfNoOrDirtyGitRepo(): Promise<void> {
207
207
  if (!continueWithoutGit) {
208
208
  await abort(undefined, 0);
209
209
  }
210
+ // return early to avoid checking for uncommitted files
211
+ return;
210
212
  }
211
213
 
212
214
  const uncommittedOrUntrackedFiles = getUncommittedOrUntrackedFiles();
@@ -247,7 +249,10 @@ export function isInGitRepo() {
247
249
  export function getUncommittedOrUntrackedFiles(): string[] {
248
250
  try {
249
251
  const gitStatus = childProcess
250
- .execSync('git status --porcelain=v1')
252
+ .execSync('git status --porcelain=v1', {
253
+ // we only care about stdout
254
+ stdio: ['ignore', 'pipe', 'ignore'],
255
+ })
251
256
  .toString();
252
257
 
253
258
  const files = gitStatus
@@ -356,6 +361,7 @@ export async function installPackage({
356
361
  askBeforeUpdating = true,
357
362
  packageNameDisplayLabel,
358
363
  packageManager,
364
+ forceInstall = false,
359
365
  }: {
360
366
  /** The string that is passed to the package manager CLI as identifier to install (e.g. `@sentry/nextjs`, or `@sentry/nextjs@^8`) */
361
367
  packageName: string;
@@ -364,6 +370,8 @@ export async function installPackage({
364
370
  /** Overrides what is shown in the installation logs in place of the `packageName` option. Useful if the `packageName` is ugly (e.g. `@sentry/nextjs@^8`) */
365
371
  packageNameDisplayLabel?: string;
366
372
  packageManager?: PackageManager;
373
+ /** Add force install flag to command to skip install precondition fails */
374
+ forceInstall?: boolean;
367
375
  }): Promise<{ packageManager?: PackageManager }> {
368
376
  return traceStep('install-package', async () => {
369
377
  if (alreadyInstalled && askBeforeUpdating) {
@@ -393,7 +401,9 @@ export async function installPackage({
393
401
  try {
394
402
  await new Promise<void>((resolve, reject) => {
395
403
  childProcess.exec(
396
- `${pkgManager.installCommand} ${packageName} ${pkgManager.flags}`,
404
+ `${pkgManager.installCommand} ${packageName} ${pkgManager.flags} ${
405
+ forceInstall ? pkgManager.forceInstallFlag : ''
406
+ }`,
397
407
  (err, stdout, stderr) => {
398
408
  if (err) {
399
409
  // Write a log file so we can better troubleshoot issues
@@ -881,7 +891,8 @@ export async function getOrAskForProjectData(
881
891
  | 'javascript-sveltekit'
882
892
  | 'apple-ios'
883
893
  | 'android'
884
- | 'react-native',
894
+ | 'react-native'
895
+ | 'flutter',
885
896
  ): Promise<{
886
897
  sentryUrl: string;
887
898
  selfHosted: boolean;
@@ -1043,7 +1054,8 @@ async function askForWizardLogin(options: {
1043
1054
  | 'javascript-sveltekit'
1044
1055
  | 'apple-ios'
1045
1056
  | 'android'
1046
- | 'react-native';
1057
+ | 'react-native'
1058
+ | 'flutter';
1047
1059
  orgSlug?: string;
1048
1060
  projectSlug?: string;
1049
1061
  }): Promise<WizardProjectData> {
@@ -14,6 +14,7 @@ export interface PackageManager {
14
14
  /* The command that the package manager uses to run a script from package.json */
15
15
  runScriptCommand: string;
16
16
  flags: string;
17
+ forceInstallFlag: string;
17
18
  detect: () => boolean;
18
19
  addOverride: (pkgName: string, pkgVersion: string) => Promise<void>;
19
20
  }
@@ -25,6 +26,7 @@ export const BUN: PackageManager = {
25
26
  buildCommand: 'bun run build',
26
27
  runScriptCommand: 'bun run',
27
28
  flags: '',
29
+ forceInstallFlag: '--force',
28
30
  detect: () =>
29
31
  ['bun.lockb', 'bun.lock'].some((lockFile) =>
30
32
  fs.existsSync(path.join(process.cwd(), lockFile)),
@@ -49,6 +51,7 @@ export const YARN_V1: PackageManager = {
49
51
  buildCommand: 'yarn build',
50
52
  runScriptCommand: 'yarn',
51
53
  flags: '--ignore-workspace-root-check',
54
+ forceInstallFlag: '--force',
52
55
  detect: () => {
53
56
  try {
54
57
  return fs
@@ -80,6 +83,7 @@ export const YARN_V2: PackageManager = {
80
83
  buildCommand: 'yarn build',
81
84
  runScriptCommand: 'yarn',
82
85
  flags: '',
86
+ forceInstallFlag: '--force',
83
87
  detect: () => {
84
88
  try {
85
89
  return fs
@@ -110,6 +114,7 @@ export const PNPM: PackageManager = {
110
114
  buildCommand: 'pnpm build',
111
115
  runScriptCommand: 'pnpm',
112
116
  flags: '--ignore-workspace-root-check',
117
+ forceInstallFlag: '--force',
113
118
  detect: () => fs.existsSync(path.join(process.cwd(), 'pnpm-lock.yaml')),
114
119
  addOverride: async (pkgName, pkgVersion): Promise<void> => {
115
120
  const packageDotJson = await getPackageDotJson();
@@ -135,6 +140,7 @@ export const NPM: PackageManager = {
135
140
  buildCommand: 'npm run build',
136
141
  runScriptCommand: 'npm run',
137
142
  flags: '',
143
+ forceInstallFlag: '--force',
138
144
  detect: () => fs.existsSync(path.join(process.cwd(), 'package-lock.json')),
139
145
  addOverride: async (pkgName, pkgVersion): Promise<void> => {
140
146
  const packageDotJson = await getPackageDotJson();
@@ -57,6 +57,16 @@ export type WizardOptions = {
57
57
  * If this is set, the wizard will skip the login and project selection step.
58
58
  */
59
59
  preSelectedProject?: PreselectedProject;
60
+
61
+ /**
62
+ * Force-install the SDK package to continue with the installation in case
63
+ * any package manager checks are failing (e.g. peer dependency versions).
64
+ *
65
+ * Use with caution and only if you know what you're doing.
66
+ *
67
+ * Does not apply to all wizard flows (currently NPM only)
68
+ */
69
+ forceInstall?: boolean;
60
70
  };
61
71
 
62
72
  export interface Feature {
@@ -0,0 +1,306 @@
1
+ import * as Sentry from '@sentry/node';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ import {
6
+ addCocoaPods,
7
+ podInstall,
8
+ usesCocoaPod,
9
+ } from '../../src/apple/cocoapod';
10
+ import * as bash from '../../src/utils/bash';
11
+ // @ts-ignore - clack is ESM and TS complains about that. It works though
12
+ import * as clack from '@clack/prompts';
13
+
14
+ jest.mock('../../src/utils/bash');
15
+ jest.spyOn(Sentry, 'setTag').mockImplementation();
16
+ jest.spyOn(Sentry, 'captureException').mockImplementation();
17
+
18
+ const clackSpinnerMock = {
19
+ start: jest.fn(),
20
+ stop: jest.fn(),
21
+ message: jest.fn(),
22
+ };
23
+
24
+ describe('cocoapod', () => {
25
+ beforeEach(() => {
26
+ jest.spyOn(clack, 'spinner').mockReturnValue(clackSpinnerMock);
27
+ jest.spyOn(clack.log, 'error').mockImplementation();
28
+ });
29
+
30
+ afterEach(() => {
31
+ jest.clearAllMocks();
32
+ });
33
+
34
+ describe('usesCocoaPod', () => {
35
+ describe('Podfile exists', () => {
36
+ it('should return true', () => {
37
+ // -- Arrange --
38
+ const projPath = path.join(os.tmpdir(), 'test-project-with-podfile');
39
+ const tempDir = fs.mkdtempSync(projPath);
40
+
41
+ const podfile = path.join(tempDir, 'Podfile');
42
+ fs.writeFileSync(podfile, '');
43
+
44
+ // -- Act --
45
+ const result = usesCocoaPod(tempDir);
46
+
47
+ // -- Assert --
48
+ expect(result).toBeTruthy();
49
+ });
50
+ });
51
+
52
+ describe('Podfile does not exist', () => {
53
+ it('should return false', () => {
54
+ // -- Arrange --
55
+ const projPath = path.join(os.tmpdir(), 'test-project-without-podfile');
56
+ const tempDir = fs.mkdtempSync(projPath);
57
+
58
+ // -- Act --
59
+ const result = usesCocoaPod(tempDir);
60
+
61
+ // -- Assert --
62
+ expect(result).toBeFalsy();
63
+ });
64
+ });
65
+ });
66
+
67
+ describe('addCocoaPods', () => {
68
+ describe('Podfile does not exist', () => {
69
+ it('should throw an error', async () => {
70
+ // -- Arrange --
71
+ const projPath = path.join(os.tmpdir(), 'test-project-without-podfile');
72
+ const tempDir = fs.mkdtempSync(projPath);
73
+
74
+ // -- Act & Assert --
75
+ await expect(addCocoaPods(tempDir)).rejects.toThrow(
76
+ 'ENOENT: no such file or directory, open',
77
+ );
78
+ });
79
+ });
80
+
81
+ describe('Podfile exists', () => {
82
+ describe('Podfile includes Sentry pod', () => {
83
+ const variations = [
84
+ {
85
+ case: 'simple',
86
+ content: 'pod "Sentry"',
87
+ },
88
+ {
89
+ case: 'with-swiftui',
90
+ content: 'pod "SentrySwiftUI"',
91
+ },
92
+ {
93
+ case: 'leading-space',
94
+ content: ' pod "Sentry"',
95
+ },
96
+ {
97
+ case: 'leading-space-and-swiftui',
98
+ content: ' pod "SentrySwiftUI"',
99
+ },
100
+ {
101
+ case: 'trailing-space',
102
+ content: 'pod "Sentry" ',
103
+ },
104
+ {
105
+ case: 'trailing-space-and-swiftui',
106
+ content: 'pod "SentrySwiftUI" ',
107
+ },
108
+ {
109
+ case: 'single-quotes',
110
+ content: "pod 'Sentry'",
111
+ },
112
+ {
113
+ case: 'double-quotes',
114
+ content: 'pod "Sentry"',
115
+ },
116
+ ];
117
+ for (const variation of variations) {
118
+ it(`should not change the Podfile for ${variation.case}`, async () => {
119
+ // -- Arrange --
120
+ const projPath = fs.mkdtempSync(path.join(os.tmpdir(), 'project'));
121
+ fs.mkdirSync(projPath, {
122
+ recursive: true,
123
+ });
124
+
125
+ const podfile = path.join(projPath, 'Podfile');
126
+ fs.writeFileSync(podfile, variation.content, 'utf8');
127
+
128
+ // -- Act --
129
+ const result = await addCocoaPods(projPath);
130
+
131
+ // -- Assert --
132
+ expect(result).toBeTruthy();
133
+ expect(fs.readFileSync(podfile, 'utf8')).toBe(variation.content);
134
+ });
135
+ }
136
+ });
137
+
138
+ describe('Podfile includes no other pods', () => {
139
+ describe('Podfile does not include use_frameworks!', () => {
140
+ it('should not change the Podfile', async () => {
141
+ // -- Arrange --
142
+ const projPath = fs.mkdtempSync(path.join(os.tmpdir(), 'project'));
143
+ fs.mkdirSync(projPath, {
144
+ recursive: true,
145
+ });
146
+
147
+ const podfile = path.join(projPath, 'Podfile');
148
+ fs.writeFileSync(podfile, '', 'utf8');
149
+
150
+ // -- Act --
151
+ const result = await addCocoaPods(projPath);
152
+
153
+ // -- Assert --
154
+ expect(result).toBeFalsy();
155
+ expect(fs.readFileSync(podfile, 'utf8')).toBe('');
156
+ });
157
+ });
158
+
159
+ describe('Podfile includes use_frameworks!', () => {
160
+ it('should change the Podfile', async () => {
161
+ // -- Arrange --
162
+ const projPath = fs.mkdtempSync(path.join(os.tmpdir(), 'project'));
163
+ fs.mkdirSync(projPath, {
164
+ recursive: true,
165
+ });
166
+
167
+ const podfile = path.join(projPath, 'Podfile');
168
+ fs.writeFileSync(podfile, `use_frameworks!`, 'utf8');
169
+
170
+ // -- Act --
171
+ const result = await addCocoaPods(projPath);
172
+
173
+ // -- Assert --
174
+ expect(result).toBeTruthy();
175
+ expect(fs.readFileSync(podfile, 'utf8')).toBe(
176
+ `use_frameworks!\npod 'Sentry'\n`,
177
+ );
178
+ });
179
+ });
180
+ });
181
+
182
+ describe('Podfile includes other pods', () => {
183
+ it('should append Sentry pod after last pod', async () => {
184
+ // -- Arrange --
185
+ const projPath = fs.mkdtempSync(path.join(os.tmpdir(), 'project'));
186
+ fs.mkdirSync(projPath, {
187
+ recursive: true,
188
+ });
189
+
190
+ const podfile = path.join(projPath, 'Podfile');
191
+ fs.writeFileSync(podfile, 'pod "OtherPod"', 'utf8');
192
+
193
+ // -- Act --
194
+ const result = await addCocoaPods(projPath);
195
+
196
+ // -- Assert --
197
+ expect(result).toBeTruthy();
198
+ expect(fs.readFileSync(podfile, 'utf8')).toBe(
199
+ `pod "OtherPod"\npod 'Sentry'\n`,
200
+ );
201
+ });
202
+ });
203
+ });
204
+ });
205
+
206
+ describe('podInstall', () => {
207
+ let workDir: string;
208
+
209
+ beforeEach(() => {
210
+ workDir = path.join(os.tmpdir(), 'test-project');
211
+ });
212
+
213
+ describe('any bash scripts fail', () => {
214
+ beforeEach(() => {
215
+ jest.spyOn(bash, 'execute').mockRejectedValue(new Error('test error'));
216
+ });
217
+
218
+ it('should not throw an error', async () => {
219
+ // -- Act & Assert --
220
+ await expect(podInstall(workDir)).resolves.not.toThrow();
221
+ });
222
+
223
+ it('should set tag', async () => {
224
+ // -- Act --
225
+ await podInstall(workDir);
226
+
227
+ // -- Assert --
228
+ expect(Sentry.setTag).toHaveBeenCalledWith('pods-installed', false);
229
+ });
230
+
231
+ it('should capture exception', async () => {
232
+ // -- Act --
233
+ await podInstall(workDir);
234
+
235
+ // -- Assert --
236
+ expect(Sentry.captureException).toHaveBeenCalledWith(
237
+ 'Sentry pod install failed.',
238
+ );
239
+ });
240
+
241
+ it('should start and stop spinner', async () => {
242
+ // -- Act --
243
+ await podInstall(workDir);
244
+
245
+ // -- Assert --
246
+ expect(clackSpinnerMock.start).toHaveBeenCalledWith(
247
+ "Running 'pod install'. This may take a few minutes...",
248
+ );
249
+ expect(clackSpinnerMock.stop).toHaveBeenCalledWith(
250
+ 'Failed to install pods.',
251
+ );
252
+ });
253
+ });
254
+
255
+ describe('all bash scripts work', () => {
256
+ beforeEach(() => {
257
+ jest.spyOn(bash, 'execute').mockResolvedValue('');
258
+ });
259
+
260
+ it('should call pod update and install', async () => {
261
+ // -- Act --
262
+ await podInstall(workDir);
263
+
264
+ // -- Assert --
265
+ expect(bash.execute).toHaveBeenCalledWith(
266
+ `cd ${workDir} && pod repo update`,
267
+ );
268
+ expect(bash.execute).toHaveBeenCalledWith(
269
+ `cd ${workDir} && pod install --silent`,
270
+ );
271
+ });
272
+
273
+ it('should set tag', async () => {
274
+ // -- Act --
275
+ await podInstall(workDir);
276
+
277
+ // -- Assert --
278
+ expect(Sentry.setTag).toHaveBeenCalledWith('pods-installed', true);
279
+ });
280
+
281
+ it('should start and stop spinner', async () => {
282
+ // -- Act --
283
+ await podInstall(workDir);
284
+
285
+ // -- Assert --
286
+ expect(clackSpinnerMock.start).toHaveBeenCalledWith(
287
+ "Running 'pod install'. This may take a few minutes...",
288
+ );
289
+ expect(clackSpinnerMock.stop).toHaveBeenCalledWith('Pods installed.');
290
+ });
291
+ });
292
+
293
+ describe('dir not given', () => {
294
+ it('should use current directory', async () => {
295
+ // -- Act --
296
+ await podInstall();
297
+
298
+ // -- Assert --
299
+ expect(bash.execute).toHaveBeenCalledWith(`cd . && pod repo update`);
300
+ expect(bash.execute).toHaveBeenCalledWith(
301
+ `cd . && pod install --silent`,
302
+ );
303
+ });
304
+ });
305
+ });
306
+ });