Class: BranchIOCLI::Helper::ConfigurationHelper

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

Overview

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

.all_domainsObject

Returns the value of attribute all_domains.



17
18
19
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 17

def all_domains
  @all_domains
end

.cartfile_pathObject

Returns the value of attribute cartfile_path.



19
20
21
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 19

def cartfile_path
  @cartfile_path
end

.keysObject

Returns the value of attribute keys.



16
17
18
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 16

def keys
  @keys
end

.podfile_pathObject

Returns the value of attribute podfile_path.



18
19
20
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 18

def podfile_path
  @podfile_path
end

.targetObject

Returns the value of attribute target.



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

def target
  @target
end

.uri_schemeObject

Returns the value of attribute uri_scheme.



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

def uri_scheme
  @uri_scheme
end

.xcodeprojObject

Returns the value of attribute xcodeproj.



15
16
17
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 15

def xcodeproj
  @xcodeproj
end

.xcodeproj_pathObject

Returns the value of attribute xcodeproj_path.



14
15
16
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 14

def xcodeproj_path
  @xcodeproj_path
end

Class Method Details

.add_carthage(options) ⇒ Object



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
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 347

def add_carthage(options)
  # TODO: Collapse this and Command::update_cartfile

  # 1. Generate Cartfile
  @cartfile_path = File.expand_path "../Cartfile", @xcodeproj_path
  File.open(@cartfile_path, "w") do |file|
    file.write "github \"BranchMetrics/ios-branch-deep-linking\"\n"
  end

  # 2. carthage update
  Dir.chdir(File.dirname(@cartfile_path)) do
    system "carthage update --platform ios"
  end

  # 3. Add Cartfile and Cartfile.resolved to commit (in case :commit param specified)
  BranchHelper.add_change cartfile_path
  BranchHelper.add_change "#{cartfile_path}.resolved"

  # 4. Add to target depependencies
  frameworks_group = @xcodeproj.frameworks_group
  branch_framework = frameworks_group.new_file "Carthage/Build/iOS/Branch.framework"
  target = BranchHelper.target_from_project @xcodeproj, options.target
  target.frameworks_build_phase.add_file_reference branch_framework

  # 5. Create a copy-frameworks build phase
  carthage_build_phase = target.new_shell_script_build_phase "carthage copy-frameworks"
  carthage_build_phase.shell_script = "/usr/local/bin/carthage copy-frameworks"

  carthage_build_phase.input_paths << "$(SRCROOT)/Carthage/Build/iOS/Branch.framework"
  carthage_build_phase.output_paths << "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Branch.framework"

  @xcodeproj.save

  # For now, add Carthage folder to SCM

  # 6. Add the Carthage folder to the commit (in case :commit param specified)
  current_pathname = Pathname.new File.expand_path "."
  carthage_folder_path = Pathname.new(File.expand_path("../Carthage", cartfile_path)).relative_path_from(current_pathname)
  cartfile_pathname = Pathname.new(@cartfile_path).relative_path_from current_pathname
  BranchHelper.add_change carthage_folder_path
  `git add #{cartfile_pathname} #{cartfile_pathname}.resolved #{carthage_folder_path}` if options.commit
end

.add_cocoapods(options) ⇒ Object



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 316

def add_cocoapods(options)
  @podfile_path = File.expand_path "../Podfile", @xcodeproj_path
  target = BranchHelper.target_from_project @xcodeproj, options.target

  install_command = "pod install"
  install_command += " --repo-update" unless options.no_pod_repo_update
  Dir.chdir(File.dirname(@podfile_path)) do
    system "pod init"
    BranchHelper.apply_patch(
      files: @podfile_path,
      regexp: /^(\s*)# Pods for #{target.name}$/,
      mode: :append,
      text: "\n\\1pod \"Branch\"",
      global: false
    )
    system install_command
  end

  BranchHelper.add_change @podfile_path
  BranchHelper.add_change "#{@podfile_path}.lock"

  # For now, add Pods folder to SCM.
  current_pathname = Pathname.new File.expand_path "."
  pods_folder_path = Pathname.new(File.expand_path("../Pods", podfile_path)).relative_path_from current_pathname
  workspace_path = Pathname.new(File.expand_path(@xcodeproj_path.sub(/.xcodeproj$/, ".xcworkspace"))).relative_path_from current_pathname
  podfile_pathname = Pathname.new(@podfile_path).relative_path_from current_pathname
  BranchHelper.add_change pods_folder_path
  BranchHelper.add_change workspace_path
  `git add #{podfile_pathname} #{podfile_pathname}.lock #{pods_folder_path} #{workspace_path}` if options.commit
end

.add_direct(options) ⇒ Object



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
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
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 392

def add_direct(options)
  # TODO: Put these intermediates in a temp directory until Branch.framework is unzipped
  # (and validated?). For now dumped in the current directory.
  File.unlink "Branch.framework.zip" if File.exist? "Branch.framework.zip"
  remove_directory "Branch.framework"

  say "Finding current framework release..."

  # Find the latest release from GitHub.
  releases = JSON.parse fetch "https://api.github.com/repos/BranchMetrics/ios-branch-deep-linking/releases"
  current_release = releases.first
  # Get the download URL for the framework.
  framework_asset = current_release["assets"][0]
  framework_url = framework_asset["browser_download_url"]

  say "Downloading Branch.framework v. #{current_release['tag_name']} (#{framework_asset['size']} bytes zipped)..."

  # Download the framework zip
  File.open("Branch.framework.zip", "w") do |file|
    file.write fetch framework_url
  end

  say "Unzipping Branch.framework..."

  # Unzip
  Zip::File.open "Branch.framework.zip" do |zip_file|
    # Start with just the framework and add dSYM, etc., later
    zip_file.glob "Carthage/Build/iOS/Branch.framework/**/*" do |entry|
      filename = entry.name.sub %r{^Carthage/Build/iOS/}, ""
      ensure_directory File.dirname filename
      entry.extract filename
    end
  end

  # Remove intermediate zip file
  File.unlink "Branch.framework.zip"

  # Now the current framework is in ./Branch.framework

  say "Adding to #{@xcodeproj_path}..."

  # Add as a dependency in the Frameworks group
  frameworks_group = @xcodeproj.frameworks_group
  framework = frameworks_group.new_file "Branch.framework"
  target = BranchHelper.target_from_project @xcodeproj, options.target
  target.frameworks_build_phase.add_file_reference framework, true

  # Make sure this is in the FRAMEWORK_SEARCH_PATHS
  @xcodeproj.build_configurations.each do |config|
    setting = config.build_settings["FRAMEWORK_SEARCH_PATHS"] || []
    setting = [setting] if setting.kind_of? String
    next if setting.any? { |p| p == "$(SRCROOT)" }

    setting << "$(SRCROOT)"
    config.build_settings["FRAMEWORK_SEARCH_PATHS"] = setting
  end

  @xcodeproj.save

  BranchHelper.add_change File.expand_path "Branch.framework"
  `git add Branch.framework` if options.commit

  say "Done. ✅"
end

.all_domains_from_domains(domains) ⇒ Object



233
234
235
236
237
238
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 233

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


193
194
195
196
197
198
199
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 193

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


206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 206

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


229
230
231
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 229

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

.custom_domains_from_domains(domains) ⇒ Object



201
202
203
204
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 201

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

.ensure_directory(path) ⇒ Object



468
469
470
471
472
473
474
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 468

def ensure_directory(path)
  return if path == "/" || path == "."
  parent = File.dirname path
  ensure_directory parent
  return if Dir.exist? path
  Dir.mkdir path
end

.fetch(url) ⇒ Object



457
458
459
460
461
462
463
464
465
466
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 457

def fetch(url)
  response = Net::HTTP.get_response URI(url)

  case response
  when Net::HTTPRedirection
    fetch response['location']
  else
    response.body
  end
end


50
51
52
53
54
55
56
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 50

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


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 58

def print_setup_configuration
  say "\n<%= 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\n"
end


75
76
77
78
79
80
81
82
83
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 75

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}\n"
end

.remove_directory(path) ⇒ Object



476
477
478
479
480
481
482
483
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 476

def remove_directory(path)
  return unless File.exist? path

  Dir["#{path}/*"].each do |file|
    remove_directory(file) and next if File.directory?(file)
    File.unlink file
  end
end

.uri_scheme_without_suffix(scheme) ⇒ Object

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



241
242
243
244
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 241

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

.validate_all_domains(options, required = true) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 102

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(options, filename) ⇒ Object



246
247
248
249
250
251
252
253
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
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 246

def validate_buildfile_path(options, filename)
  # Disable Podfile/Cartfile update if --no_add_sdk is present
  return if options.no_add_sdk

  buildfile_path = filename == "Podfile" ? options.podfile : options.cartfile

  # 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 @xcodeproj_path
  buildfile_path = File.expand_path "../#{filename}", @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
end

.validate_keys_from_setup_options(options) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 85

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_sdk_addition(options) ⇒ Object



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
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 289

def validate_sdk_addition(options)
  return if options.no_add_sdk || @podfile_path || @cartfile_path

  # 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 =~ %r{/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

  option = SDK_OPTIONS[selected]

  case option
  when :skip
    return
  else
    send "add_#{option}", options
  end
end

.validate_setup_options(options) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 23

def validate_setup_options(options)
  print_identification "setup"

  say "--force is ignored when --no_validate is used." if options.no_validate && options.force

  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
  validate_buildfile_path options, "Podfile"
  validate_buildfile_path options, "Cartfile"

  print_setup_configuration

  validate_sdk_addition options
end

.validate_target(options, allow_extensions = true) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 165

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



125
126
127
128
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 125

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



41
42
43
44
45
46
47
48
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 41

def validate_validation_options(options)
  print_identification "validate"

  validate_xcodeproj_path options
  validate_target options, false

  print_validation_configuration
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.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/branch_io_cli/helper/configuration_helper.rb', line 133

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