Class: BranchIOCLI::Helper::ConfigurationHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/branch_io_cli/helper/configuration_helper.rb

Overview

Processes CLI options. Validates options. Prompts for input in a number of cases. rubocop: disable Metrics/ClassLength

Constant Summary collapse

/\.app\.link$|\.test-app\.link$/
SDK_OPTIONS =
{
  "Set this project up to use CocoaPods and add the Branch SDK." => :cocoapods,
  "Set this project up to use Carthage and add the Branch SDK." => :carthage,
  "Add Branch.framework directly to the project's dependencies." => :direct,
  "Skip adding the framework to the project." => :skip
}

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.add_sdkObject (readonly)

Returns the value of attribute add_sdk.



33
34
35
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 33

def add_sdk
  @add_sdk
end

.all_domainsObject (readonly)

Returns the value of attribute all_domains.



26
27
28
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 26

def all_domains
  @all_domains
end

.cartfile_pathObject (readonly)

Returns the value of attribute cartfile_path.



28
29
30
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 28

def cartfile_path
  @cartfile_path
end

.cleanObject (readonly)

Returns the value of attribute clean.



38
39
40
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 38

def clean
  @clean
end

.commitObject (readonly)

Returns the value of attribute commit.



36
37
38
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 36

def commit
  @commit
end

.configurationObject (readonly)

Returns the value of attribute configuration.



41
42
43
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 41

def configuration
  @configuration
end

.forceObject (readonly)

Returns the value of attribute force.



34
35
36
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 34

def force
  @force
end

.header_onlyObject (readonly)

Returns the value of attribute header_only.



39
40
41
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 39

def header_only
  @header_only
end

.keysObject (readonly)

Returns the value of attribute keys.



25
26
27
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 25

def keys
  @keys
end

.patch_sourceObject (readonly)

Returns the value of attribute patch_source.



35
36
37
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 35

def patch_source
  @patch_source
end

.pod_repo_updateObject (readonly)

Returns the value of attribute pod_repo_update.



31
32
33
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 31

def pod_repo_update
  @pod_repo_update
end

.podfile_pathObject (readonly)

Returns the value of attribute podfile_path.



27
28
29
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 27

def podfile_path
  @podfile_path
end

.report_pathObject (readonly)

Returns the value of attribute report_path.



42
43
44
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 42

def report_path
  @report_path
end

.schemeObject (readonly)

Returns the value of attribute scheme.



40
41
42
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 40

def scheme
  @scheme
end

.sdk_integration_modeObject (readonly)

Returns the value of attribute sdk_integration_mode.



37
38
39
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 37

def sdk_integration_mode
  @sdk_integration_mode
end

.targetObject (readonly)

Returns the value of attribute target.



29
30
31
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 29

def target
  @target
end

.uri_schemeObject (readonly)

Returns the value of attribute uri_scheme.



30
31
32
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 30

def uri_scheme
  @uri_scheme
end

.validateObject (readonly)

Returns the value of attribute validate.



32
33
34
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 32

def validate
  @validate
end

.workspaceObject (readonly)

Returns the value of attribute workspace.



24
25
26
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 24

def workspace
  @workspace
end

.workspace_pathObject (readonly)

Returns the value of attribute workspace_path.



23
24
25
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 23

def workspace_path
  @workspace_path
end

.xcodeprojObject (readonly)

Returns the value of attribute xcodeproj.



22
23
24
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 22

def xcodeproj
  @xcodeproj
end

.xcodeproj_pathObject (readonly)

Returns the value of attribute xcodeproj_path.



21
22
23
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 21

def xcodeproj_path
  @xcodeproj_path
end

Class Method Details

.all_domains_from_domains(domains) ⇒ Object



414
415
416
417
418
419
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 414

def all_domains_from_domains(domains)
  app_link_roots = app_link_roots_from_domains domains
  app_link_subdomains = app_link_subdomains_from_roots app_link_roots
  custom_domains = custom_domains_from_domains domains
  custom_domains + app_link_subdomains
end

.all_schemesObject



338
339
340
341
342
343
344
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 338

def all_schemes
  if @workspace_path
    @workspace.schemes.keys.reject { |scheme| scheme == "Pods" }
  else
    Xcodeproj::Project.schemes @xcodeproj_path
  end
end


374
375
376
377
378
379
380
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 374

def app_link_roots_from_domains(domains)
  return [] if domains.nil?

  domains.select { |d| d =~ APP_LINK_REGEXP }
         .map { |d| d.sub(APP_LINK_REGEXP, '').sub(/-alternate$/, '') }
         .uniq
end


387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 387

def app_link_subdomains(root)
  app_link_subdomain = root
  return [] if app_link_subdomain.nil?

  live_key = @keys[:live]
  test_key = @keys[:test]

  domains = []
  unless live_key.nil?
    domains += [
      "#{app_link_subdomain}.app.link",
      "#{app_link_subdomain}-alternate.app.link"
    ]
  end
  unless test_key.nil?
    domains += [
      "#{app_link_subdomain}.test-app.link",
      "#{app_link_subdomain}-alternate.test-app.link"
    ]
  end
  domains
end


410
411
412
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 410

def app_link_subdomains_from_roots(roots)
  roots.inject([]) { |domains, root| domains + app_link_subdomains(root) }
end

.custom_domains_from_domains(domains) ⇒ Object



382
383
384
385
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 382

def custom_domains_from_domains(domains)
  return [] if domains.nil?
  domains.reject { |d| d =~ APP_LINK_REGEXP }.uniq
end


116
117
118
119
120
121
122
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 116

def print_identification(command)
  say "\n<%= color(\"branch_io \#{command} v. \#{VERSION}\", BOLD) %>\n\n"
end


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 157

def print_report_configuration
  say "<%= color('Configuration:', BOLD) %>\n\n<%= color('Xcode workspace:', BOLD) %> \#{@workspace_path || '(none)'}\n<%= color('Xcode project:', BOLD) %> \#{@xcodeproj_path || '(none)'}\n<%= color('Scheme:', BOLD) %> \#{@scheme || '(none)'}\n<%= color('Target:', BOLD) %> \#{@target || '(none)'}\n<%= color('Configuration:', BOLD) %> \#{@configuration || '(none)'}\n<%= color('Podfile:', BOLD) %> \#{@podfile_path || '(none)'}\n<%= color('Cartfile:', BOLD) %> \#{@cartfile_path || '(none)'}\n<%= color('Clean:', BOLD) %> \#{@clean.inspect}\n<%= color('Report path:', BOLD) %> \#{@report_path}\n"
end


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 124

def print_setup_configuration
  say "<%= color('Configuration:', BOLD) %>\n\n<%= color('Xcode project:', BOLD) %> \#{@xcodeproj_path}\n<%= color('Target:', BOLD) %> \#{@target.name}\n<%= color('Live key:', BOLD) %> \#{@keys[:live] || '(none)'}\n<%= color('Test key:', BOLD) %> \#{@keys[:test] || '(none)'}\n<%= color('Domains:', BOLD) %> \#{@all_domains}\n<%= color('URI scheme:', BOLD) %> \#{@uri_scheme || '(none)'}\n<%= color('Podfile:', BOLD) %> \#{@podfile_path || '(none)'}\n<%= color('Cartfile:', BOLD) %> \#{@cartfile_path || '(none)'}\n<%= color('Pod repo update:', BOLD) %> \#{@pod_repo_update.inspect}\n<%= color('Validate:', BOLD) %> \#{@validate.inspect}\n<%= color('Force:', BOLD) %> \#{@force.inspect}\n<%= color('Add SDK:', BOLD) %> \#{@add_sdk.inspect}\n<%= color('Patch source:', BOLD) %> \#{@patch_source.inspect}\n<%= color('Commit:', BOLD) %> \#{@commit.inspect}\n<%= color('SDK integration mode:', BOLD) %> \#{@sdk_integration_mode || '(none)'}\n\n"
end


147
148
149
150
151
152
153
154
155
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 147

def print_validation_configuration
  say "<%= color('Configuration:', BOLD) %>\n\n<%= color('Xcode project:', BOLD) %> \#{@xcodeproj_path}\n<%= color('Target:', BOLD) %> \#{@target.name}\n<%= color('Domains:', BOLD) %> \#{@all_domains || '(none)'}\n"
end

.uri_scheme_without_suffix(scheme) ⇒ Object

Removes any trailing :// from the argument and returns a copy



422
423
424
425
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 422

def uri_scheme_without_suffix(scheme)
  return nil if scheme.nil?
  scheme.sub %r{://$}, ""
end

.validate_all_domains(options, required = true) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 190

def validate_all_domains(options, required = true)
  app_link_roots = app_link_roots_from_domains options.domains

  unless options.app_link_subdomain.nil? || app_link_roots.include?(options.app_link_subdomain)
    app_link_roots << options.app_link_subdomain
  end

  # app_link_roots now contains options.app_link_subdomain, if supplied, and the roots of any
  # .app.link or .test-app.link domains provided via options.domains.

  app_link_subdomains = app_link_subdomains_from_roots app_link_roots

  custom_domains = custom_domains_from_domains options.domains

  @all_domains = (app_link_subdomains + custom_domains).uniq

  while required && @all_domains.empty?
    domains = ask "Please enter domains as a comma-separated list: ", ->(str) { str.split "," }

    @all_domains = all_domains_from_domains domains
  end
end

.validate_buildfile_path(buildfile_path, filename) ⇒ Object



427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 427

def validate_buildfile_path(buildfile_path, filename)
  # Disable Podfile/Cartfile update if --no-add-sdk is present
  return unless @sdk_integration_mode.nil?

  # Was --podfile/--cartfile used?
  if buildfile_path
    # Yes: Validate. Prompt if not valid.
    loop do
      valid = buildfile_path =~ %r{/?#{filename}$}
      say "#{filename} path must end in /#{filename}." unless valid

      if valid
        valid = File.exist? buildfile_path
        say "#{buildfile_path} not found." unless valid
      end

      if valid
        if filename == "Podfile"
          @podfile_path = buildfile_path
        else
          @cartfile_path = buildfile_path
        end
        return
      end

      buildfile_path = ask "Please enter the path to your #{filename}: "
    end
  end

  # No: Check for Podfile/Cartfile next to workspace or project
  buildfile_path = File.expand_path "../#{filename}", (@workspace_path || @xcodeproj_path)
  return unless File.exist? buildfile_path

  # Exists: Use it (valid if found)
  if filename == "Podfile"
    @podfile_path = buildfile_path
  else
    @cartfile_path = buildfile_path
  end

  @sdk_integration_mode = filename == "Podfile" ? :cocoapods : :carthage
end

.validate_keys_from_setup_options(options) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 173

def validate_keys_from_setup_options(options)
  live_key = options.live_key
  test_key = options.test_key
  @keys = {}
  @keys[:live] = live_key unless live_key.nil?
  @keys[:test] = test_key unless test_key.nil?

  while @keys.empty?
    say "A live key, a test key or both is required."
    live_key = ask "Please enter your live Branch key or use --live_key [enter for none]: "
    test_key = ask "Please enter your test Branch key or use --test_key [enter for none]: "

    @keys[:live] = live_key unless live_key == ""
    @keys[:test] = test_key unless test_key == ""
  end
end

.validate_report_options(options) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 90

def validate_report_options(options)
  print_identification "report"

  @clean = options.clean
  @header_only = options.header_only
  @scheme = options.scheme
  @target = options.target
  @configuration = options.configuration
  @report_path = options.out || "./report.txt"

  validate_xcodeproj_and_workspace options
  validate_scheme options

  # If neither --podfile nor --cartfile is present, arbitrarily look for a Podfile
  # first.

  # If --cartfile is present, don't look for a Podfile. Just validate that
  # Cartfile.
  validate_buildfile_path(options.podfile, "Podfile") if options.cartfile.nil?

  # If --podfile is present or a Podfile was found, don't look for a Cartfile.
  validate_buildfile_path(options.cartfile, "Cartfile") if @sdk_integration_mode.nil?

  print_report_configuration
end

.validate_scheme(options) ⇒ Object

rubocop: enable Metrics/PerceivedComplexity



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 320

def validate_scheme(options)
  schemes = all_schemes
  if options.scheme && schemes.include?(options.scheme)
    @scheme = options.scheme
  elsif schemes.count == 1
    @scheme = schemes.first
  elsif !schemes.empty?
    say "Please specify one of the following for the --scheme argument:"
    schemes.each do |scheme|
      say " #{scheme}"
    end
    exit 1
  else
    say "No scheme defined in project."
    exit(-1)
  end
end

.validate_sdk_addition(options) ⇒ Object



470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 470

def validate_sdk_addition(options)
  return if !options.add_sdk || @sdk_integration_mode

  # If no CocoaPods or Carthage, check to see if the framework is linked.
  target = BranchHelper.target_from_project @xcodeproj, options.target
  return if target.frameworks_build_phase.files.map(&:file_ref).map(&:path).any? { |p| p =~ /Branch.framework$/ }

  # --podfile, --cartfile not specified. No Podfile found. No Cartfile found. No Branch.framework in project.
  # Prompt the user:
  selected = choose do |menu|
    menu.header = "No Podfile or Cartfile specified or found. Here are your options"

    SDK_OPTIONS.each_key { |k| menu.choice k }

    menu.prompt = "What would you like to do?"
  end

  @sdk_integration_mode = SDK_OPTIONS[selected]

  case @sdk_integration_mode
  when :cocoapods
    @podfile_path = File.expand_path "../Podfile", @xcodeproj_path
  when :carthage
    @cartfile_path = File.expand_path "../Cartfile", @xcodeproj_path
  end
end

.validate_setup_options(options) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 44

def validate_setup_options(options)
  print_identification "setup"

  @pod_repo_update = options.pod_repo_update
  @validate = options.validate
  @patch_source = options.patch_source
  @add_sdk = options.add_sdk
  @force = options.force
  @commit = options.commit

  say "--force is ignored when --no-validate is used." if !options.validate && options.force
  if options.cartfile && options.podfile
    say "--cartfile and --podfile are mutually exclusive. Please specify the file to patch."
    exit 1
  end

  validate_xcodeproj_path options
  validate_target options
  validate_keys_from_setup_options options
  validate_all_domains options, !@target.extension_target_type?
  validate_uri_scheme options

  # If neither --podfile nor --cartfile is present, arbitrarily look for a Podfile
  # first.

  # If --cartfile is present, don't look for a Podfile. Just validate that
  # Cartfile.
  validate_buildfile_path options.podfile, "Podfile" if options.cartfile.nil? && options.add_sdk

  # If --podfile is present or a Podfile was found, don't look for a Cartfile.
  validate_buildfile_path options.cartfile, "Cartfile" if @sdk_integration_mode.nil? && options.add_sdk

  validate_sdk_addition options

  print_setup_configuration
end

.validate_target(options, allow_extensions = true) ⇒ Object



346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 346

def validate_target(options, allow_extensions = true)
  non_test_targets = @xcodeproj.targets.reject(&:test_target_type?)
  raise "No non-test target found in project" if non_test_targets.empty?

  valid_targets = non_test_targets.reject { |t| !allow_extensions && t.extension_target_type? }

  begin
    target = BranchHelper.target_from_project @xcodeproj, options.target

    # If a test target was explicitly specified.
    raise "Cannot use test targets" if target.test_target_type?

    # If an extension target was explicitly specified for validation.
    raise "Extension targets not allowed for this command" if !allow_extensions && target.extension_target_type?

    @target = target
  rescue StandardError => e
    say e.message

    choice = choose do |menu|
      valid_targets.each { |t| menu.choice t.name }
      menu.prompt = "Which target do you wish to use? "
    end

    @target = @xcodeproj.targets.find { |t| t.name = choice }
  end
end

.validate_uri_scheme(options) ⇒ Object



213
214
215
216
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 213

def validate_uri_scheme(options)
  # No validation at the moment. Just strips off any trailing ://
  @uri_scheme = uri_scheme_without_suffix options.uri_scheme
end

.validate_validation_options(options) ⇒ Object



81
82
83
84
85
86
87
88
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 81

def validate_validation_options(options)
  print_identification "validate"

  validate_xcodeproj_path options
  validate_target options, false

  print_validation_configuration
end

.validate_xcodeproj_and_workspace(options) ⇒ Object

rubocop: disable Metrics/PerceivedComplexity



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 254

def validate_xcodeproj_and_workspace(options)
  # 1. What was passed in?
  begin
    if options.workspace
      path = options.workspace
      @workspace = Xcodeproj::Workspace.new_from_xcworkspace options.workspace
      @workspace_path = options.workspace
    end
    if options.xcodeproj
      path = options.xcodeproj
      @xcodeproj = Xcodeproj::Project.open options.xcodeproj
      @xcodeproj_path = options.xcodeproj
    end
    return if @workspace || @xcodeproj
  rescue StandardError => e
    say e.message
  end

  # Try to find first a workspace, then a project
  all_workspace_paths = Dir[File.expand_path(File.join(".", "**/*.xcworkspace"))]
                        .reject { |w| w =~ %r{/project.pbxproj$} }
                        .select do |p|
    valid = true
    Pathname.new(p).each_filename do |f|
      valid = false && break if f == "Carthage" || f == "Pods"
    end
    valid
  end

  if all_workspace_paths.count == 1
    path = all_workspace_paths.first
  elsif all_workspace_paths.count == 0
    all_xcodeproj_paths = Dir[File.expand_path(File.join(".", "**/*.xcodeproj"))]
    xcodeproj_paths = all_xcodeproj_paths.select do |p|
      valid = true
      Pathname.new(p).each_filename do |f|
        valid = false && break if f == "Carthage" || f == "Pods"
      end
      valid
    end

    path = xcodeproj_paths.first if xcodeproj_paths.count == 1
  end
  # If more than one workspace. Don't try to find a project. Just prompt.

  loop do
    path = ask "Please enter a path to your Xcode project or workspace: " if path.nil?
    begin
      if path =~ /\.xcworkspace$/
        @workspace = Xcodeproj::Workspace.new_from_xcworkspace path
        @workspace_path = path
        return
      elsif path =~ /\.xcodeproj$/
        @xcodeproj = Xcodeproj::Project.open path
        @xcodeproj_path = path
        return
      else
        say "Path must end with .xcworkspace or .xcodeproj"
      end
    rescue StandardError => e
      say e.message
    end
  end
end

.validate_xcodeproj_path(options) ⇒ Object

  1. Look for options.xcodeproj.

  2. If not specified, look for projects under . (excluding anything in Pods or Carthage folder).

  3. If none or more than one found, prompt the user.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 221

def validate_xcodeproj_path(options)
  if options.xcodeproj
    path = options.xcodeproj
  else
    all_xcodeproj_paths = Dir[File.expand_path(File.join(".", "**/*.xcodeproj"))]
    # find an xcodeproj (ignoring the Pods and Carthage folders)
    # TODO: Improve this filter
    xcodeproj_paths = all_xcodeproj_paths.select do |p|
      valid = true
      Pathname.new(p).each_filename do |f|
        valid = false && break if f == "Carthage" || f == "Pods"
      end
      valid
    end

    path = xcodeproj_paths.first if xcodeproj_paths.count == 1
  end

  loop do
    path = ask "Please enter the path to your Xcode project or use --xcodeproj: " if path.nil?
    # TODO: Allow the user to choose if xcodeproj_paths.count > 0
    begin
      @xcodeproj = Xcodeproj::Project.open path
      @xcodeproj_path = path
      return
    rescue StandardError => e
      say e.message
      path = nil
    end
  end
end