branch_io_cli 0.9.0 → 0.9.1

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/patches/ContinueUserActivity.m +4 -0
  3. data/lib/assets/patches/ContinueUserActivity.swift +4 -0
  4. data/lib/assets/patches/ContinueUserActivityNew.m +6 -0
  5. data/lib/assets/patches/ContinueUserActivityNew.swift +5 -0
  6. data/lib/assets/patches/DidFinishLaunching.m +4 -0
  7. data/lib/assets/patches/DidFinishLaunching.swift +5 -0
  8. data/lib/assets/patches/DidFinishLaunchingNew.m +9 -0
  9. data/lib/assets/patches/DidFinishLaunchingNew.swift +9 -0
  10. data/lib/assets/patches/DidFinishLaunchingNewTest.m +13 -0
  11. data/lib/assets/patches/DidFinishLaunchingNewTest.swift +13 -0
  12. data/lib/assets/patches/DidFinishLaunchingTest.m +8 -0
  13. data/lib/assets/patches/DidFinishLaunchingTest.swift +9 -0
  14. data/lib/assets/patches/OpenUrl.m +4 -0
  15. data/lib/assets/patches/OpenUrl.swift +4 -0
  16. data/lib/assets/patches/OpenUrlNew.m +6 -0
  17. data/lib/assets/patches/OpenUrlNew.swift +5 -0
  18. data/lib/assets/patches/OpenUrlSourceApplication.m +4 -0
  19. data/lib/assets/patches/OpenUrlSourceApplication.swift +4 -0
  20. data/lib/assets/patches/cartfile.yml +3 -0
  21. data/lib/assets/patches/continue_user_activity_new_objc.yml +2 -0
  22. data/lib/assets/patches/continue_user_activity_new_swift.yml +2 -0
  23. data/lib/assets/patches/continue_user_activity_objc.yml +2 -0
  24. data/lib/assets/patches/continue_user_activity_swift.yml +2 -0
  25. data/lib/assets/patches/did_finish_launching_new_objc.yml +2 -0
  26. data/lib/assets/patches/did_finish_launching_new_swift.yml +2 -0
  27. data/lib/assets/patches/did_finish_launching_new_test_objc.yml +2 -0
  28. data/lib/assets/patches/did_finish_launching_new_test_swift.yml +2 -0
  29. data/lib/assets/patches/did_finish_launching_objc.yml +2 -0
  30. data/lib/assets/patches/did_finish_launching_swift.yml +2 -0
  31. data/lib/assets/patches/did_finish_launching_test_objc.yml +2 -0
  32. data/lib/assets/patches/did_finish_launching_test_swift.yml +2 -0
  33. data/lib/assets/patches/objc_import.yml +3 -0
  34. data/lib/assets/patches/open_url_new_objc.yml +2 -0
  35. data/lib/assets/patches/open_url_new_swift.yml +2 -0
  36. data/lib/assets/patches/open_url_objc.yml +2 -0
  37. data/lib/assets/patches/open_url_source_application_objc.yml +2 -0
  38. data/lib/assets/patches/open_url_source_application_swift.yml +2 -0
  39. data/lib/assets/patches/open_url_swift.yml +2 -0
  40. data/lib/assets/patches/swift_import.yml +3 -0
  41. data/lib/branch_io_cli.rb +2 -1
  42. data/lib/branch_io_cli/cli.rb +3 -3
  43. data/lib/branch_io_cli/command.rb +4 -0
  44. data/lib/branch_io_cli/{commands → command}/command.rb +4 -3
  45. data/lib/branch_io_cli/{commands → command}/report_command.rb +57 -54
  46. data/lib/branch_io_cli/{commands → command}/setup_command.rb +29 -26
  47. data/lib/branch_io_cli/{commands → command}/validate_command.rb +4 -2
  48. data/lib/branch_io_cli/configuration.rb +4 -0
  49. data/lib/branch_io_cli/configuration/configuration.rb +211 -0
  50. data/lib/branch_io_cli/configuration/report_configuration.rb +164 -0
  51. data/lib/branch_io_cli/configuration/setup_configuration.rb +215 -0
  52. data/lib/branch_io_cli/configuration/validate_configuration.rb +26 -0
  53. data/lib/branch_io_cli/core_ext/io.rb +2 -2
  54. data/lib/branch_io_cli/helper.rb +1 -1
  55. data/lib/branch_io_cli/helper/branch_helper.rb +0 -16
  56. data/lib/branch_io_cli/helper/ios_helper.rb +87 -468
  57. data/lib/branch_io_cli/helper/methods.rb +1 -1
  58. data/lib/branch_io_cli/helper/patch_helper.rb +267 -0
  59. data/lib/branch_io_cli/version.rb +1 -1
  60. metadata +55 -11
  61. data/lib/branch_io_cli/commands.rb +0 -4
  62. data/lib/branch_io_cli/helper/configuration_helper.rb +0 -529
@@ -15,7 +15,7 @@ module BranchIOCLI
15
15
  # :command: [String] A shell command to execute
16
16
  # :output: [IO] An optional IO object to receive stdout and stderr from the command
17
17
  def sh(command, output = STDOUT)
18
- status = output.report_command command
18
+ status = output.log_command command
19
19
  raise CommandError, %{Error executing "#{command}": #{status}.} unless status.success?
20
20
  end
21
21
  end
@@ -0,0 +1,267 @@
1
+ require "pattern_patch"
2
+
3
+ module BranchIOCLI
4
+ module Helper
5
+ class PatchHelper
6
+ class << self
7
+ def load_patch(name)
8
+ path = File.expand_path(File.join('..', '..', '..', 'assets', 'patches', "#{name}.yml"), __FILE__)
9
+ PatternPatch::Patch.from_yaml path
10
+ end
11
+
12
+ def config
13
+ Configuration::Configuration.current
14
+ end
15
+
16
+ def helper
17
+ BranchHelper
18
+ end
19
+
20
+ def add_change(change)
21
+ helper.add_change change
22
+ end
23
+
24
+ def patch_bridging_header
25
+ unless config.bridging_header_path
26
+ say "Modules not available and bridging header not found. Cannot import Branch."
27
+ say "Please add use_frameworks! to your Podfile and/or enable modules in your project or use --no-patch-source."
28
+ exit(-1)
29
+ end
30
+
31
+ begin
32
+ bridging_header = File.read config.bridging_header_path
33
+ return false if bridging_header =~ %r{^\s+#import\s+<Branch/Branch.h>|^\s+@import\s+Branch\s*;}
34
+ rescue RuntimeError => e
35
+ say e.message
36
+ say "Cannot read #{config.bridging_header_path}."
37
+ say "Please correct this setting or use --no-patch-source."
38
+ exit(-1)
39
+ end
40
+
41
+ say "Patching #{config.bridging_header_path}"
42
+
43
+ load_patch(:objc_import).apply config.bridging_header_path
44
+ helper.add_change config.bridging_header_path
45
+ end
46
+
47
+ def patch_app_delegate_swift(project)
48
+ return false unless config.swift_version
49
+
50
+ app_delegate_swift = project.files.find { |f| f.path =~ /AppDelegate.swift$/ }
51
+ return false if app_delegate_swift.nil?
52
+
53
+ app_delegate_swift_path = app_delegate_swift.real_path.to_s
54
+
55
+ app_delegate = File.read app_delegate_swift_path
56
+
57
+ # Can't check for the import here, since there may be a bridging header.
58
+ return false if app_delegate =~ /Branch\.initSession/
59
+
60
+ unless config.bridging_header_required?
61
+ load_patch(:swift_import).apply app_delegate_swift_path
62
+ end
63
+
64
+ say "Patching #{app_delegate_swift_path}"
65
+
66
+ patch_did_finish_launching_method_swift app_delegate_swift_path
67
+ patch_continue_user_activity_method_swift app_delegate_swift_path
68
+ patch_open_url_method_swift app_delegate_swift_path
69
+
70
+ add_change app_delegate_swift_path
71
+ true
72
+ end
73
+
74
+ def patch_app_delegate_objc(project)
75
+ app_delegate_objc = project.files.find { |f| f.path =~ /AppDelegate.m$/ }
76
+ return false if app_delegate_objc.nil?
77
+
78
+ app_delegate_objc_path = app_delegate_objc.real_path.to_s
79
+
80
+ app_delegate = File.read app_delegate_objc_path
81
+ return false if app_delegate =~ %r{^\s+#import\s+<Branch/Branch.h>|^\s+@import\s+Branch\s*;}
82
+
83
+ say "Patching #{app_delegate_objc_path}"
84
+
85
+ load_patch(:objc_import).apply app_delegate_objc_path
86
+
87
+ patch_did_finish_launching_method_objc app_delegate_objc_path
88
+ patch_continue_user_activity_method_objc app_delegate_objc_path
89
+ patch_open_url_method_objc app_delegate_objc_path
90
+
91
+ add_change app_delegate_objc_path
92
+ true
93
+ end
94
+
95
+ def patch_did_finish_launching_method_swift(app_delegate_swift_path)
96
+ app_delegate_swift = File.read app_delegate_swift_path
97
+
98
+ patch_name = "did_finish_launching_"
99
+ if app_delegate_swift =~ /didFinishLaunching[^\n]+?\{/m
100
+ # method already present
101
+ patch_name += "test_" unless config.keys.count <= 1 || has_multiple_info_plists?
102
+ patch_name += "swift"
103
+ patch = load_patch patch_name
104
+ patch.regexp = /didFinishLaunchingWithOptions.*?\{[^\n]*\n/m
105
+ else
106
+ # method not present. add entire method
107
+ patch_name += "new_"
108
+ patch_name += "test_" unless config.keys.count <= 1 || has_multiple_info_plists?
109
+ patch_name += "swift"
110
+ patch = load_patch patch_name
111
+ patch.regexp = /var\s+window\s?:\s?UIWindow\?.*?\n/m
112
+ end
113
+ patch.apply app_delegate_swift_path
114
+ end
115
+
116
+ def patch_did_finish_launching_method_objc(app_delegate_objc_path)
117
+ app_delegate_objc = File.read app_delegate_objc_path
118
+
119
+ patch_name = "did_finish_launching_"
120
+ if app_delegate_objc =~ /didFinishLaunchingWithOptions/m
121
+ # method exists. patch it.
122
+ patch_name += "test_" unless config.keys.count <= 1 || has_multiple_info_plists?
123
+ patch_name += "objc"
124
+ patch = load_patch patch_name
125
+ patch.regexp = /didFinishLaunchingWithOptions.*?\{[^\n]*\n/m
126
+ else
127
+ # method does not exist. add it.
128
+ patch_name += "new_"
129
+ patch_name += "test_" unless config.keys.count <= 1 || has_multiple_info_plists?
130
+ patch_name += "objc"
131
+ patch = load_patch patch_name
132
+ patch.regexp = /^@implementation.*?\n/m
133
+ end
134
+ patch.apply app_delegate_objc_path
135
+ end
136
+
137
+ def patch_open_url_method_swift(app_delegate_swift_path)
138
+ app_delegate_swift = File.read app_delegate_swift_path
139
+ patch_name = "open_url_"
140
+ if app_delegate_swift =~ /application.*open\s+url.*options/
141
+ # Has application:openURL:options:
142
+ patch_name += "swift"
143
+ patch = load_patch patch_name
144
+ patch.regexp = /application.*open\s+url.*options:.*?\{.*?\n/m
145
+ elsif app_delegate_swift =~ /application.*open\s+url.*sourceApplication/
146
+ # Has application:openURL:sourceApplication:annotation:
147
+ # TODO: This method is deprecated.
148
+ patch_name += "source_application_swift"
149
+ patch = load_patch patch_name
150
+ patch.regexp = /application.*open\s+url.*sourceApplication:.*?\{.*?\n/m
151
+ else
152
+ # Has neither
153
+ patch_name += "new_swift"
154
+ patch = load_patch patch_name
155
+ patch.regexp = /\n\s*\}[^{}]*\Z/m
156
+ end
157
+ patch.apply app_delegate_swift_path
158
+ end
159
+
160
+ def patch_continue_user_activity_method_swift(app_delegate_swift_path)
161
+ app_delegate = File.read app_delegate_swift_path
162
+ patch_name = "continue_user_activity_"
163
+ if app_delegate =~ /application:.*continue userActivity:.*restorationHandler:/
164
+ # Add something to the top of the method
165
+ patch_name += "swift"
166
+ patch = load_patch patch_name
167
+ patch.regexp = /application:.*continue userActivity:.*restorationHandler:.*?\{.*?\n/m
168
+ else
169
+ # Add the application:continueUserActivity:restorationHandler method if it does not exist
170
+ patch_name += "new_swift"
171
+ patch = load_patch patch_name
172
+ patch.regexp = /\n\s*\}[^{}]*\Z/m
173
+ end
174
+ patch.apply app_delegate_swift_path
175
+ end
176
+
177
+ def patch_open_url_method_objc(app_delegate_objc_path)
178
+ app_delegate_objc = File.read app_delegate_objc_path
179
+ patch_name = "open_url_"
180
+ if app_delegate_objc =~ /application:.*openURL:.*options/
181
+ # Has application:openURL:options:
182
+ patch_name += "objc"
183
+ patch = load_patch patch_name
184
+ patch.regexp = /application:.*openURL:.*options:.*?\{.*?\n/m
185
+ elsif app_delegate_objc =~ /application:.*openURL:.*sourceApplication/
186
+ # Has application:openURL:sourceApplication:annotation:
187
+ patch_name += "source_application_objc"
188
+ patch = load_patch patch_name
189
+ patch.regexp = /application:.*openURL:.*sourceApplication:.*?\{.*?\n/m
190
+ else
191
+ # Has neither
192
+ patch_name += "new_objc"
193
+ patch = load_patch patch_name
194
+ patch.regexp = /\n\s*@end[^@]*\Z/m
195
+ end
196
+ patch.apply app_delegate_objc_path
197
+ end
198
+
199
+ def patch_continue_user_activity_method_objc(app_delegate_objc_path)
200
+ app_delegate = File.read app_delegate_objc_path
201
+ patch_name = "continue_user_activity_"
202
+ if app_delegate =~ /application:.*continueUserActivity:.*restorationHandler:/
203
+ patch_name += "objc"
204
+ patch = load_patch patch_name
205
+ patch.regexp = /application:.*continueUserActivity:.*restorationHandler:.*?\{.*?\n/m
206
+ else
207
+ # Add the application:continueUserActivity:restorationHandler method if it does not exist
208
+ patch_name += "new_objc"
209
+ patch = load_patch patch_name
210
+ patch.regexp = /\n\s*@end[^@]*\Z/m
211
+ end
212
+ patch.apply app_delegate_objc_path
213
+ end
214
+
215
+ def patch_podfile(podfile_path)
216
+ podfile = File.read podfile_path
217
+
218
+ # Podfile already contains the Branch pod
219
+ # TODO: Allow for adding to multiple targets in the Podfile
220
+ return false if podfile =~ /pod\s+('Branch'|"Branch")/
221
+
222
+ say "Adding pod \"Branch\" to #{podfile_path}"
223
+
224
+ if podfile =~ /target\s+(["'])#{config.target.name}\1\s+do.*?\n/m
225
+ # if there is a target block for this target:
226
+ patch = PatternPatch::Patch.new(
227
+ regexp: /\n(\s*)target\s+(["'])#{config.target.name}\2\s+do.*?\n/m,
228
+ text: "\\1 pod \"Branch\"\n",
229
+ mode: :append
230
+ )
231
+ else
232
+ # add to the abstract_target for this target
233
+ patch = PatternPatch::Patch.new(
234
+ regexp: /^(\s*)target\s+["']#{config.target.name}/,
235
+ text: "\\1pod \"Branch\"\n",
236
+ mode: :prepend
237
+ )
238
+ end
239
+ patch.apply podfile_path
240
+
241
+ true
242
+ end
243
+
244
+ def patch_cartfile(cartfile_path)
245
+ cartfile = File.read cartfile_path
246
+
247
+ # Cartfile already contains the Branch framework
248
+ return false if cartfile =~ /git.+Branch/
249
+
250
+ say "Adding \"Branch\" to #{cartfile_path}"
251
+
252
+ load_patch(:cartfile).apply cartfile_path
253
+
254
+ true
255
+ end
256
+
257
+ def patch_source(xcodeproj)
258
+ # Patch the bridging header any time Swift imports are not available,
259
+ # to make Branch available throughout the app, whether the AppDelegate
260
+ # is in Swift or Objective-C.
261
+ patch_bridging_header if config.bridging_header_required?
262
+ patch_app_delegate_swift(xcodeproj) || patch_app_delegate_objc(xcodeproj)
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
@@ -1,3 +1,3 @@
1
1
  module BranchIOCLI
2
- VERSION = "0.9.0"
2
+ VERSION = "0.9.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: branch_io_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Branch
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-11-03 00:00:00.000000000 Z
12
+ date: 2017-11-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: CFPropertyList
@@ -59,14 +59,14 @@ dependencies:
59
59
  requirements:
60
60
  - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: '0'
62
+ version: 0.3.0
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '0'
69
+ version: 0.3.0
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: plist
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -250,21 +250,65 @@ files:
250
250
  - bin/branch_io
251
251
  - lib/assets/completions/completion.bash
252
252
  - lib/assets/completions/completion.zsh
253
+ - lib/assets/patches/ContinueUserActivity.m
254
+ - lib/assets/patches/ContinueUserActivity.swift
255
+ - lib/assets/patches/ContinueUserActivityNew.m
256
+ - lib/assets/patches/ContinueUserActivityNew.swift
257
+ - lib/assets/patches/DidFinishLaunching.m
258
+ - lib/assets/patches/DidFinishLaunching.swift
259
+ - lib/assets/patches/DidFinishLaunchingNew.m
260
+ - lib/assets/patches/DidFinishLaunchingNew.swift
261
+ - lib/assets/patches/DidFinishLaunchingNewTest.m
262
+ - lib/assets/patches/DidFinishLaunchingNewTest.swift
263
+ - lib/assets/patches/DidFinishLaunchingTest.m
264
+ - lib/assets/patches/DidFinishLaunchingTest.swift
265
+ - lib/assets/patches/OpenUrl.m
266
+ - lib/assets/patches/OpenUrl.swift
267
+ - lib/assets/patches/OpenUrlNew.m
268
+ - lib/assets/patches/OpenUrlNew.swift
269
+ - lib/assets/patches/OpenUrlSourceApplication.m
270
+ - lib/assets/patches/OpenUrlSourceApplication.swift
271
+ - lib/assets/patches/cartfile.yml
272
+ - lib/assets/patches/continue_user_activity_new_objc.yml
273
+ - lib/assets/patches/continue_user_activity_new_swift.yml
274
+ - lib/assets/patches/continue_user_activity_objc.yml
275
+ - lib/assets/patches/continue_user_activity_swift.yml
276
+ - lib/assets/patches/did_finish_launching_new_objc.yml
277
+ - lib/assets/patches/did_finish_launching_new_swift.yml
278
+ - lib/assets/patches/did_finish_launching_new_test_objc.yml
279
+ - lib/assets/patches/did_finish_launching_new_test_swift.yml
280
+ - lib/assets/patches/did_finish_launching_objc.yml
281
+ - lib/assets/patches/did_finish_launching_swift.yml
282
+ - lib/assets/patches/did_finish_launching_test_objc.yml
283
+ - lib/assets/patches/did_finish_launching_test_swift.yml
284
+ - lib/assets/patches/objc_import.yml
285
+ - lib/assets/patches/open_url_new_objc.yml
286
+ - lib/assets/patches/open_url_new_swift.yml
287
+ - lib/assets/patches/open_url_objc.yml
288
+ - lib/assets/patches/open_url_source_application_objc.yml
289
+ - lib/assets/patches/open_url_source_application_swift.yml
290
+ - lib/assets/patches/open_url_swift.yml
291
+ - lib/assets/patches/swift_import.yml
253
292
  - lib/branch_io_cli.rb
254
293
  - lib/branch_io_cli/cli.rb
255
- - lib/branch_io_cli/commands.rb
256
- - lib/branch_io_cli/commands/command.rb
257
- - lib/branch_io_cli/commands/report_command.rb
258
- - lib/branch_io_cli/commands/setup_command.rb
259
- - lib/branch_io_cli/commands/validate_command.rb
294
+ - lib/branch_io_cli/command.rb
295
+ - lib/branch_io_cli/command/command.rb
296
+ - lib/branch_io_cli/command/report_command.rb
297
+ - lib/branch_io_cli/command/setup_command.rb
298
+ - lib/branch_io_cli/command/validate_command.rb
299
+ - lib/branch_io_cli/configuration.rb
300
+ - lib/branch_io_cli/configuration/configuration.rb
301
+ - lib/branch_io_cli/configuration/report_configuration.rb
302
+ - lib/branch_io_cli/configuration/setup_configuration.rb
303
+ - lib/branch_io_cli/configuration/validate_configuration.rb
260
304
  - lib/branch_io_cli/core_ext.rb
261
305
  - lib/branch_io_cli/core_ext/io.rb
262
306
  - lib/branch_io_cli/helper.rb
263
307
  - lib/branch_io_cli/helper/android_helper.rb
264
308
  - lib/branch_io_cli/helper/branch_helper.rb
265
- - lib/branch_io_cli/helper/configuration_helper.rb
266
309
  - lib/branch_io_cli/helper/ios_helper.rb
267
310
  - lib/branch_io_cli/helper/methods.rb
311
+ - lib/branch_io_cli/helper/patch_helper.rb
268
312
  - lib/branch_io_cli/version.rb
269
313
  homepage: http://github.com/BranchMetrics/branch_io_cli
270
314
  licenses:
@@ -286,7 +330,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
286
330
  version: '0'
287
331
  requirements: []
288
332
  rubyforge_project:
289
- rubygems_version: 2.7.0
333
+ rubygems_version: 2.7.1
290
334
  signing_key:
291
335
  specification_version: 4
292
336
  summary: Branch.io command-line interface for mobile app integration
@@ -1,4 +0,0 @@
1
- require "branch_io_cli/commands/command"
2
- require "branch_io_cli/commands/report_command"
3
- require "branch_io_cli/commands/setup_command"
4
- require "branch_io_cli/commands/validate_command"
@@ -1,529 +0,0 @@
1
- require "pathname"
2
- require "xcodeproj"
3
-
4
- module BranchIOCLI
5
- module Helper
6
- # Processes CLI options.
7
- # Validates options.
8
- # Prompts for input in a number of cases.
9
- # rubocop: disable Metrics/ClassLength
10
- class ConfigurationHelper
11
- APP_LINK_REGEXP = /\.app\.link$|\.test-app\.link$/
12
- SDK_OPTIONS =
13
- {
14
- "Set this project up to use CocoaPods and add the Branch SDK." => :cocoapods,
15
- "Set this project up to use Carthage and add the Branch SDK." => :carthage,
16
- "Add Branch.framework directly to the project's dependencies." => :direct,
17
- "Skip adding the framework to the project." => :skip
18
- }
19
-
20
- class << self
21
- attr_reader :xcodeproj_path
22
- attr_reader :xcodeproj
23
- attr_reader :workspace_path
24
- attr_reader :workspace
25
- attr_reader :keys
26
- attr_reader :all_domains
27
- attr_reader :podfile_path
28
- attr_reader :cartfile_path
29
- attr_reader :carthage_command
30
- attr_reader :target
31
- attr_reader :uri_scheme
32
- attr_reader :pod_repo_update
33
- attr_reader :validate
34
- attr_reader :add_sdk
35
- attr_reader :force
36
- attr_reader :patch_source
37
- attr_reader :commit
38
- attr_reader :sdk_integration_mode
39
- attr_reader :clean
40
- attr_reader :header_only
41
- attr_reader :scheme
42
- attr_reader :configuration
43
- attr_reader :report_path
44
- attr_reader :sdk
45
-
46
- def validate_setup_options(options)
47
- print_identification "setup"
48
-
49
- @pod_repo_update = options.pod_repo_update
50
- @validate = options.validate
51
- @patch_source = options.patch_source
52
- @add_sdk = options.add_sdk
53
- @force = options.force
54
- @commit = options.commit
55
-
56
- say "--force is ignored when --no-validate is used." if !options.validate && options.force
57
- if options.cartfile && options.podfile
58
- say "--cartfile and --podfile are mutually exclusive. Please specify the file to patch."
59
- exit 1
60
- end
61
-
62
- validate_xcodeproj_path options
63
- validate_target options
64
- validate_keys_from_setup_options options
65
- validate_all_domains options, !@target.extension_target_type?
66
- validate_uri_scheme options
67
-
68
- # If neither --podfile nor --cartfile is present, arbitrarily look for a Podfile
69
- # first.
70
-
71
- # If --cartfile is present, don't look for a Podfile. Just validate that
72
- # Cartfile.
73
- validate_buildfile_path options.podfile, "Podfile" if options.cartfile.nil? && options.add_sdk
74
-
75
- # If --podfile is present or a Podfile was found, don't look for a Cartfile.
76
- validate_buildfile_path options.cartfile, "Cartfile" if @sdk_integration_mode.nil? && options.add_sdk
77
- @carthage_command = options.carthage_command if @sdk_integration_mode == :carthage
78
-
79
- validate_sdk_addition options
80
-
81
- BranchHelper.verify_git if @commit
82
-
83
- print_setup_configuration
84
- end
85
-
86
- def validate_validation_options(options)
87
- print_identification "validate"
88
-
89
- validate_xcodeproj_path options
90
- validate_target options, false
91
-
92
- print_validation_configuration
93
- end
94
-
95
- def validate_report_options(options)
96
- print_identification "report"
97
-
98
- @clean = options.clean
99
- @header_only = options.header_only
100
- @scheme = options.scheme
101
- @target = options.target
102
- @configuration = options.configuration
103
- @report_path = options.out
104
- @sdk = options.sdk
105
- @pod_repo_update = options.pod_repo_update
106
-
107
- validate_xcodeproj_and_workspace options
108
- validate_target options
109
- validate_scheme options
110
-
111
- # If neither --podfile nor --cartfile is present, arbitrarily look for a Podfile
112
- # first.
113
-
114
- # If --cartfile is present, don't look for a Podfile. Just validate that
115
- # Cartfile.
116
- validate_buildfile_path(options.podfile, "Podfile") if options.cartfile.nil?
117
-
118
- # If --podfile is present or a Podfile was found, don't look for a Cartfile.
119
- validate_buildfile_path(options.cartfile, "Cartfile") if @sdk_integration_mode.nil?
120
-
121
- print_report_configuration
122
- end
123
-
124
- def print_identification(command)
125
- say <<EOF
126
-
127
- <%= color("branch_io #{command} v. #{VERSION}", BOLD) %>
128
-
129
- EOF
130
- end
131
-
132
- def print_setup_configuration
133
- say <<EOF
134
- <%= color('Configuration:', [CYAN, BOLD, UNDERLINE]) %>
135
-
136
- <%= color('Xcode project:', BOLD) %> #{@xcodeproj_path}
137
- <%= color('Target:', BOLD) %> #{@target.name}
138
- <%= color('Live key:', BOLD) %> #{@keys[:live] || '(none)'}
139
- <%= color('Test key:', BOLD) %> #{@keys[:test] || '(none)'}
140
- <%= color('Domains:', BOLD) %> #{@all_domains}
141
- <%= color('URI scheme:', BOLD) %> #{@uri_scheme || '(none)'}
142
- <%= color('Podfile:', BOLD) %> #{@podfile_path || '(none)'}
143
- <%= color('Cartfile:', BOLD) %> #{@cartfile_path || '(none)'}
144
- <%= color('Carthage command:', BOLD) %> #{@carthage_command || '(none)'}
145
- <%= color('Pod repo update:', BOLD) %> #{@pod_repo_update.inspect}
146
- <%= color('Validate:', BOLD) %> #{@validate.inspect}
147
- <%= color('Force:', BOLD) %> #{@force.inspect}
148
- <%= color('Add SDK:', BOLD) %> #{@add_sdk.inspect}
149
- <%= color('Patch source:', BOLD) %> #{@patch_source.inspect}
150
- <%= color('Commit:', BOLD) %> #{@commit.inspect}
151
- <%= color('SDK integration mode:', BOLD) %> #{@sdk_integration_mode || '(none)'}
152
-
153
- EOF
154
- end
155
-
156
- def print_validation_configuration
157
- say <<EOF
158
- <%= color('Configuration:', [CYAN, BOLD, UNDERLINE]) %>
159
-
160
- <%= color('Xcode project:', BOLD) %> #{@xcodeproj_path}
161
- <%= color('Target:', BOLD) %> #{@target.name}
162
- <%= color('Domains:', BOLD) %> #{@all_domains || '(none)'}
163
- EOF
164
- end
165
-
166
- def print_report_configuration
167
- say <<EOF
168
- <%= color('Configuration:', [CYAN, BOLD, UNDERLINE]) %>
169
-
170
- <%= color('Xcode workspace:', BOLD) %> #{@workspace_path || '(none)'}
171
- <%= color('Xcode project:', BOLD) %> #{@xcodeproj_path || '(none)'}
172
- <%= color('Scheme:', BOLD) %> #{@scheme || '(none)'}
173
- <%= color('Target:', BOLD) %> #{@target || '(none)'}
174
- <%= color('Configuration:', BOLD) %> #{@configuration}
175
- <%= color('SDK:', BOLD) %> #{@sdk}
176
- <%= color('Podfile:', BOLD) %> #{@podfile_path || '(none)'}
177
- <%= color('Cartfile:', BOLD) %> #{@cartfile_path || '(none)'}
178
- <%= color('Pod repo update:', BOLD) %> #{@pod_repo_update.inspect}
179
- <%= color('Clean:', BOLD) %> #{@clean.inspect}
180
- <%= color('Report path:', BOLD) %> #{@report_path}
181
- EOF
182
- end
183
-
184
- def validate_keys_from_setup_options(options)
185
- live_key = options.live_key
186
- test_key = options.test_key
187
- @keys = {}
188
- @keys[:live] = live_key unless live_key.nil?
189
- @keys[:test] = test_key unless test_key.nil?
190
-
191
- while @keys.empty?
192
- say "A live key, a test key or both is required."
193
- live_key = ask "Please enter your live Branch key or use --live_key [enter for none]: "
194
- test_key = ask "Please enter your test Branch key or use --test_key [enter for none]: "
195
-
196
- @keys[:live] = live_key unless live_key == ""
197
- @keys[:test] = test_key unless test_key == ""
198
- end
199
- end
200
-
201
- def validate_all_domains(options, required = true)
202
- app_link_roots = app_link_roots_from_domains options.domains
203
-
204
- unless options.app_link_subdomain.nil? || app_link_roots.include?(options.app_link_subdomain)
205
- app_link_roots << options.app_link_subdomain
206
- end
207
-
208
- # app_link_roots now contains options.app_link_subdomain, if supplied, and the roots of any
209
- # .app.link or .test-app.link domains provided via options.domains.
210
-
211
- app_link_subdomains = app_link_subdomains_from_roots app_link_roots
212
-
213
- custom_domains = custom_domains_from_domains options.domains
214
-
215
- @all_domains = (app_link_subdomains + custom_domains).uniq
216
-
217
- while required && @all_domains.empty?
218
- domains = ask "Please enter domains as a comma-separated list: ", ->(str) { str.split "," }
219
-
220
- @all_domains = all_domains_from_domains domains
221
- end
222
- end
223
-
224
- def validate_uri_scheme(options)
225
- # No validation at the moment. Just strips off any trailing ://
226
- @uri_scheme = uri_scheme_without_suffix options.uri_scheme
227
- end
228
-
229
- # 1. Look for options.xcodeproj.
230
- # 2. If not specified, look for projects under . (excluding anything in Pods or Carthage folder).
231
- # 3. If none or more than one found, prompt the user.
232
- def validate_xcodeproj_path(options)
233
- if options.xcodeproj
234
- path = options.xcodeproj
235
- else
236
- all_xcodeproj_paths = Dir[File.expand_path(File.join(".", "**/*.xcodeproj"))]
237
- # find an xcodeproj (ignoring the Pods and Carthage folders)
238
- # TODO: Improve this filter
239
- xcodeproj_paths = all_xcodeproj_paths.select do |p|
240
- valid = true
241
- Pathname.new(p).each_filename do |f|
242
- valid = false && break if f == "Carthage" || f == "Pods"
243
- end
244
- valid
245
- end
246
-
247
- path = xcodeproj_paths.first if xcodeproj_paths.count == 1
248
- end
249
-
250
- loop do
251
- path = ask "Please enter the path to your Xcode project or use --xcodeproj: " if path.nil?
252
- # TODO: Allow the user to choose if xcodeproj_paths.count > 0
253
- begin
254
- @xcodeproj = Xcodeproj::Project.open path
255
- @xcodeproj_path = path
256
- return
257
- rescue StandardError => e
258
- say e.message
259
- path = nil
260
- end
261
- end
262
- end
263
-
264
- # rubocop: disable Metrics/PerceivedComplexity
265
- def validate_xcodeproj_and_workspace(options)
266
- # 1. What was passed in?
267
- begin
268
- if options.workspace
269
- path = options.workspace
270
- @workspace = Xcodeproj::Workspace.new_from_xcworkspace options.workspace
271
- @workspace_path = options.workspace
272
- end
273
- if options.xcodeproj
274
- path = options.xcodeproj
275
- @xcodeproj = Xcodeproj::Project.open options.xcodeproj
276
- @xcodeproj_path = options.xcodeproj
277
- else
278
- # Pass --workspace and --xcodeproj to override this inference.
279
- if @workspace && @workspace.file_references.count > 0 && @workspace.file_references.first.path =~ /\.xcodeproj$/
280
- @xcodeproj_path = File.expand_path "../#{@workspace.file_references.first.path}", @workspace_path
281
- @xcodeproj = Xcodeproj::Project.open @xcodeproj_path
282
- end
283
- end
284
- return if @workspace || @xcodeproj
285
- rescue StandardError => e
286
- say e.message
287
- end
288
-
289
- # Try to find first a workspace, then a project
290
- all_workspace_paths = Dir[File.expand_path(File.join(".", "**/*.xcworkspace"))]
291
- .reject { |w| w =~ %r{/project.xcworkspace$} }
292
- .select do |p|
293
- valid = true
294
- Pathname.new(p).each_filename do |f|
295
- valid = false && break if f == "Carthage" || f == "Pods"
296
- end
297
- valid
298
- end
299
-
300
- if all_workspace_paths.count == 1
301
- path = all_workspace_paths.first
302
- elsif all_workspace_paths.count == 0
303
- all_xcodeproj_paths = Dir[File.expand_path(File.join(".", "**/*.xcodeproj"))]
304
- xcodeproj_paths = all_xcodeproj_paths.select do |p|
305
- valid = true
306
- Pathname.new(p).each_filename do |f|
307
- valid = false && break if f == "Carthage" || f == "Pods"
308
- end
309
- valid
310
- end
311
-
312
- path = xcodeproj_paths.first if xcodeproj_paths.count == 1
313
- end
314
- # If more than one workspace. Don't try to find a project. Just prompt.
315
-
316
- loop do
317
- path = ask "Please enter a path to your Xcode project or workspace: " if path.nil?
318
- begin
319
- if path =~ /\.xcworkspace$/
320
- @workspace = Xcodeproj::Workspace.new_from_xcworkspace path
321
- @workspace_path = path
322
-
323
- # Pass --workspace and --xcodeproj to override this inference.
324
- if @workspace.file_references.count > 0 && @workspace.file_references.first.path =~ /\.xcodeproj$/
325
- @xcodeproj_path = File.expand_path "../#{@workspace.file_references.first.path}", @workspace_path
326
- @xcodeproj = Xcodeproj::Project.open @xcodeproj_path
327
- end
328
-
329
- return
330
- elsif path =~ /\.xcodeproj$/
331
- @xcodeproj = Xcodeproj::Project.open path
332
- @xcodeproj_path = path
333
- return
334
- else
335
- say "Path must end with .xcworkspace or .xcodeproj"
336
- end
337
- rescue StandardError => e
338
- say e.message
339
- end
340
- end
341
- end
342
- # rubocop: enable Metrics/PerceivedComplexity
343
-
344
- def validate_scheme(options)
345
- schemes = all_schemes
346
- # TODO: Prompt if --scheme specified but not found.
347
- if options.scheme && schemes.include?(options.scheme)
348
- @scheme = options.scheme
349
- elsif schemes.count == 1
350
- @scheme = schemes.first
351
- elsif !schemes.empty?
352
- # By default, take a scheme with the same name as the target name.
353
- return if (@scheme = schemes.find { |s| s == @target.name })
354
-
355
- say "Please specify one of the following for the --scheme argument:"
356
- schemes.each do |scheme|
357
- say " #{scheme}"
358
- end
359
- exit 1
360
- else
361
- say "No scheme defined in project."
362
- exit(-1)
363
- end
364
- end
365
-
366
- def all_schemes
367
- if @workspace_path
368
- @workspace.schemes.keys.reject { |scheme| scheme == "Pods" }
369
- else
370
- Xcodeproj::Project.schemes @xcodeproj_path
371
- end
372
- end
373
-
374
- def validate_target(options, allow_extensions = true)
375
- non_test_targets = @xcodeproj.targets.reject(&:test_target_type?)
376
- raise "No non-test target found in project" if non_test_targets.empty?
377
-
378
- valid_targets = non_test_targets.reject { |t| !allow_extensions && t.extension_target_type? }
379
-
380
- begin
381
- target = BranchHelper.target_from_project @xcodeproj, options.target
382
-
383
- # If a test target was explicitly specified.
384
- raise "Cannot use test targets" if target.test_target_type?
385
-
386
- # If an extension target was explicitly specified for validation.
387
- raise "Extension targets not allowed for this command" if !allow_extensions && target.extension_target_type?
388
-
389
- @target = target
390
- rescue StandardError => e
391
- say e.message
392
-
393
- choice = choose do |menu|
394
- valid_targets.each { |t| menu.choice t.name }
395
- menu.prompt = "Which target do you wish to use? "
396
- end
397
-
398
- @target = @xcodeproj.targets.find { |t| t.name = choice }
399
- end
400
- end
401
-
402
- def app_link_roots_from_domains(domains)
403
- return [] if domains.nil?
404
-
405
- domains.select { |d| d =~ APP_LINK_REGEXP }
406
- .map { |d| d.sub(APP_LINK_REGEXP, '').sub(/-alternate$/, '') }
407
- .uniq
408
- end
409
-
410
- def custom_domains_from_domains(domains)
411
- return [] if domains.nil?
412
- domains.reject { |d| d =~ APP_LINK_REGEXP }.uniq
413
- end
414
-
415
- def app_link_subdomains(root)
416
- app_link_subdomain = root
417
- return [] if app_link_subdomain.nil?
418
-
419
- live_key = @keys[:live]
420
- test_key = @keys[:test]
421
-
422
- domains = []
423
- unless live_key.nil?
424
- domains += [
425
- "#{app_link_subdomain}.app.link",
426
- "#{app_link_subdomain}-alternate.app.link"
427
- ]
428
- end
429
- unless test_key.nil?
430
- domains += [
431
- "#{app_link_subdomain}.test-app.link",
432
- "#{app_link_subdomain}-alternate.test-app.link"
433
- ]
434
- end
435
- domains
436
- end
437
-
438
- def app_link_subdomains_from_roots(roots)
439
- roots.inject([]) { |domains, root| domains + app_link_subdomains(root) }
440
- end
441
-
442
- def all_domains_from_domains(domains)
443
- app_link_roots = app_link_roots_from_domains domains
444
- app_link_subdomains = app_link_subdomains_from_roots app_link_roots
445
- custom_domains = custom_domains_from_domains domains
446
- custom_domains + app_link_subdomains
447
- end
448
-
449
- # Removes any trailing :// from the argument and returns a copy
450
- def uri_scheme_without_suffix(scheme)
451
- return nil if scheme.nil?
452
- scheme.sub %r{://$}, ""
453
- end
454
-
455
- def validate_buildfile_path(buildfile_path, filename)
456
- # Disable Podfile/Cartfile update if --no-add-sdk is present
457
- return unless @sdk_integration_mode.nil?
458
-
459
- # Was --podfile/--cartfile used?
460
- if buildfile_path
461
- # Yes: Validate. Prompt if not valid.
462
- loop do
463
- valid = buildfile_path =~ %r{/?#{filename}$}
464
- say "#{filename} path must end in /#{filename}." unless valid
465
-
466
- if valid
467
- valid = File.exist? buildfile_path
468
- say "#{buildfile_path} not found." unless valid
469
- end
470
-
471
- if valid
472
- if filename == "Podfile"
473
- @podfile_path = buildfile_path
474
- else
475
- @cartfile_path = buildfile_path
476
- end
477
- return
478
- end
479
-
480
- buildfile_path = ask "Please enter the path to your #{filename}: "
481
- end
482
- end
483
-
484
- # No: Check for Podfile/Cartfile next to workspace or project
485
- buildfile_path = File.expand_path "../#{filename}", (@workspace_path || @xcodeproj_path)
486
- return unless File.exist? buildfile_path
487
-
488
- # Exists: Use it (valid if found)
489
- if filename == "Podfile"
490
- @podfile_path = buildfile_path
491
- else
492
- @cartfile_path = buildfile_path
493
- end
494
-
495
- @sdk_integration_mode = filename == "Podfile" ? :cocoapods : :carthage
496
- end
497
-
498
- def validate_sdk_addition(options)
499
- return if !options.add_sdk || @sdk_integration_mode
500
-
501
- # If no CocoaPods or Carthage, check to see if the framework is linked.
502
- target = BranchHelper.target_from_project @xcodeproj, options.target
503
- return if target.frameworks_build_phase.files.map(&:file_ref).map(&:path).any? { |p| p =~ /Branch.framework$/ }
504
-
505
- # --podfile, --cartfile not specified. No Podfile found. No Cartfile found. No Branch.framework in project.
506
- # Prompt the user:
507
- selected = choose do |menu|
508
- menu.header = "No Podfile or Cartfile specified or found. Here are your options"
509
-
510
- SDK_OPTIONS.each_key { |k| menu.choice k }
511
-
512
- menu.prompt = "What would you like to do?"
513
- end
514
-
515
- @sdk_integration_mode = SDK_OPTIONS[selected]
516
-
517
- case @sdk_integration_mode
518
- when :cocoapods
519
- @podfile_path = File.expand_path "../Podfile", @xcodeproj_path
520
- when :carthage
521
- @cartfile_path = File.expand_path "../Cartfile", @xcodeproj_path
522
- @carthage_command = options.carthage_command
523
- end
524
- end
525
- end
526
- end
527
- # rubocop: enable Metrics/ClassLength
528
- end
529
- end