class BranchIOCLI::Configuration::Configuration

rubocop: disable Metrics/ClassLength

Attributes

current[RW]
apps[R]
cartfile_path[R]
keys[R]
options[R]
pod_repo_update[R]
podfile[R]
podfile_path[R]
quiet[R]
sdk[R]
sdk_integration_mode[R]
target[R]
workspace[R]
workspace_path[R]
xcodeproj[R]
xcodeproj_path[R]

Public Class Methods

absolute_path(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 32
def absolute_path(path)
  return path unless current
  current.absolute_path path
end
available_options() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 27
def available_options
  root = name.gsub(/^.*::(\w+)Configuration$/, '\1')
  BranchIOCLI::Configuration.const_get("#{root.capitalize}Options").available_options
end
defaults() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 16
def defaults
  available_options.inject({}) do |defs, o|
    default_value = o.env_value
    default_value = o.default_value if default_value.nil?

    next defs if default_value.nil?

    defs.merge(o.name => default_value)
  end
end
new(options) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 86
def initialize(options)
  @options = options
  @pod_repo_update = options.pod_repo_update if self.class.available_options.map(&:name).include?(:pod_repo_update)
  @sdk = "iphonesimulator" # to load Xcode build settings for commands without a --sdk option
  @confirm = options.confirm

  Configuration.current = self

  unless quiet
    say "\n"
    print_identification
    say ASCII_ART
    say "\n"
  end

  validate_options
  log
end
open_podfile(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 42
def open_podfile(path)
  return false unless current
  current.open_podfile absolute_path path
end
open_xcodeproj(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 47
def open_xcodeproj(path)
  return false unless current
  current.open_xcodeproj absolute_path path
end
relative_path(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 37
def relative_path(path)
  return path unless current
  current.relative_path path
end
root() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 52
def root
  return nil unless current
  current.root
end
uri_scheme_without_suffix(scheme) click to toggle source

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

myscheme
myscheme:
myscheme://
myscheme:///
etc.
# File lib/branch_io_cli/configuration/configuration.rb, line 64
def uri_scheme_without_suffix(scheme)
  return nil if scheme.blank?
  scheme.sub %r{:/*$}, ""
end
wrapper(hash, add_defaults = true) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 12
def wrapper(hash, add_defaults = true)
  OptionWrapper.new hash, available_options, add_defaults
end

Public Instance Methods

absolute_path(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 147
def absolute_path(path)
  return nil if path.nil?

  path = Pathname.new(path) unless path.kind_of? Pathname
  return path.to_s if path.absolute?

  (root + path).to_s
end
all_xcodeproj_paths() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 182
def all_xcodeproj_paths
  return @all_xcodeproj_paths if @all_xcodeproj_paths
  xcodeproj_paths = Dir[File.expand_path(File.join(".", "**/*.xcodeproj"))]
  @all_xcodeproj_paths = 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
  @all_xcodeproj_paths
end
app_delegate_objc_path() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 380
def app_delegate_objc_path
  app_delegate = target.source_build_phase.files.find { |f| f.file_ref.path =~ /AppDelegate\.m$/ }
  return nil if app_delegate.nil?

  app_delegate.file_ref.real_path.to_s
end
app_delegate_swift_path() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 371
def app_delegate_swift_path
  return nil unless swift_version && swift_version.to_f >= 3.0

  app_delegate = target.source_build_phase.files.find { |f| f.file_ref.path =~ /AppDelegate\.swift$/ }
  return nil if app_delegate.nil?

  app_delegate.file_ref.real_path.to_s
end
branch_imports() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 439
def branch_imports
  return @branch_imports if @branch_imports

  source_files = target.source_build_phase.files.map { |f| f.file_ref.real_path.to_s }
  source_files << bridging_header_path if bridging_header_path && File.exist?(bridging_header_path)
  @branch_imports = source_files.compact.map do |f|
    imports = branch_imports_from_file f
    next {} if imports.empty?
    { f => imports }
  end.inject({}, :merge)
  @branch_imports
end
branch_imports_from_file(path) click to toggle source

Detect anything that appears to be an attempt to import the Branch SDK, even if it might be wrong.

# File lib/branch_io_cli/configuration/configuration.rb, line 454
def branch_imports_from_file(path)
  imports = []
  File.readlines(path).each_with_index do |line, line_no|
    next unless line =~ /(include|import).*branch/i
    imports << "#{line_no}: #{line.chomp}"
  end
  imports
rescue StandardError
  # Quietly ignore anything that can't be opened for now.
  # TODO: Get these errors into report output.
  []
end
bridging_header_path(configuration = "Release") click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 420
def bridging_header_path(configuration = "Release")
  return @bridging_header_path if @bridging_header_path

  return nil unless target
  path = target.expanded_build_setting "SWIFT_OBJC_BRIDGING_HEADER", configuration
  return nil unless path

  @bridging_header_path = File.expand_path path, File.dirname(xcodeproj_path)
  @bridging_header_path
end
bridging_header_required?() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 364
def bridging_header_required?
  return false unless swift_version
  # If there is a Podfile and use_frameworks! is not present for this
  # target, we need a bridging header.
  podfile && !uses_frameworks?
end
confirm_with_user() click to toggle source

Prompt the user to confirm the configuration or edit.

# File lib/branch_io_cli/configuration/configuration.rb, line 480
def confirm_with_user
  confirmed = Helper::Util.confirm "Is this OK? ", true
  return if confirmed

  loop do
    Helper::Util.clear

    print_identification

    say "<%= color('The following options may be adjusted before continuing.', BOLD) %>"
    choice = choose do |menu|
      self.class.available_options.reject(&:skip_confirmation).each do |option|
        value = send option.confirm_symbol
        menu.choice "#{option.label}: #{option.display_value(value)}"
      end

      menu.choice "Accept and continue"
      menu.choice "Quit"
      menu.readline = true
      menu.prompt = "What would you like to do?"
    end

    Helper::Util.clear

    print_identification

    if (option = self.class.available_options.find { |o| choice =~ /^#{Regexp.quote(o.label)}/ })
      loop do
        break if prompt_for_option(option)
        say "Invalid value for option.\n\n"
      end
    elsif choice =~ /^Accept/
      log
      return
    else
      exit(0)
    end
  end
end
env() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 133
def env
  Environment
end
find_project() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 195
def find_project
  all_xcodeproj_paths.count == 1 ? all_xcodeproj_paths.first : nil
end
helper() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 125
def helper
  Helper::BranchHelper
end
ios_urischemes_from_api(apps = @apps) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 407
def ios_urischemes_from_api(apps = @apps)
  Set.new apps.map(&:ios_uri_scheme).compact
end
key_valid?(key, type) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 304
def key_valid?(key, type)
  return false if key.nil?
  return true if key.empty?
  unless key =~ /^key_#{type}_.+/
    say "#{key.inspect} is not a valid #{type} Branch key. It must begin with key_#{type}_."
    return false
  end

  # For now: When using --no-validate with the setup command, don't call the Branch API.
  return true unless self.class.available_options.map(&:name).include?(:validate) && validate

  begin
    # Retrieve info from the API
    app = BranchApp[key]
    @apps[key] = app
    true
  rescue StandardError => e
    say "Error fetching app for key #{key} from Branch API: #{e.message}"
    false
  end
end
log() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 109
      def log
        return if quiet

        say <<EOF
<%= color('Configuration:', [CYAN, BOLD, UNDERLINE]) %>

EOF
        # subclass implementation follows
      end
messages_view_controller_path() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 387
def messages_view_controller_path
  return nil unless target.symbol_type == :messages_extension

  all_paths = target.source_build_phase.files.map { |f| f.file_ref.real_path.to_s }
  swift_paths = all_paths.grep(/\.swift$/)

  # We're looking for the @interface declaration for a class that inherits from
  # MSMessagesAppViewController. The target probably doesn't have a headers
  # build phase. Include all .ms from the source build phase and any .h
  # with the same root.
  objc_paths = all_paths.grep(/\.m$/)
  objc_paths += objc_paths.map { |p| p.sub(/\.m$/, '.h') }.select { |f| File.exist? f }

  path = swift_paths.find { |f| /class.*:\s+MSMessagesAppViewController\s*{\n/m.match_file? f } ||
         objc_paths.find { |f| /@interface.*:\s+MSMessagesAppViewController/.match_file? f }

  # If we found a .h, patch the corresponding .m.
  path && path.sub(/\.h$/, '.m')
end
method_missing(method_sym, *arguments, &block) click to toggle source
Calls superclass method
# File lib/branch_io_cli/configuration/configuration.rb, line 467
def method_missing(method_sym, *arguments, &block)
  all_options = self.class.available_options.map(&:name)
  return super unless all_options.include?(method_sym)

  # Define an attr_reader for this method
  self.class.send :define_method, method_sym do
    instance_variable_get "@#{method_sym}"
  end

  send method_sym
end
modules_enabled?() click to toggle source

TODO: How many of these can vary by configuration?

# File lib/branch_io_cli/configuration/configuration.rb, line 413
def modules_enabled?
  return nil unless target
  setting = target.resolved_build_setting("CLANG_ENABLE_MODULES")["Release"]
  return nil unless setting
  setting == "YES"
end
open_podfile(path = podfile_path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 246
def open_podfile(path = podfile_path)
  @podfile = Pod::Podfile.from_file path
  @podfile_path = File.expand_path path
  @sdk_integration_mode = :cocoapods
  true
rescue Pod::PlainInformative => e
  say e.message
  false
end
open_xcodeproj(path = xcodeproj_path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 256
def open_xcodeproj(path = xcodeproj_path)
  @xcodeproj = Xcodeproj::Project.open path
  @xcodeproj_path = File.expand_path path
  true
rescue Xcodeproj::PlainInformative => e
  say e.message
  false
end
pod_install_required?() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 333
def pod_install_required?
  return false unless podfile_path && File.exist?(podfile_path)

  lockfile_path = "#{podfile_path}.lock"
  manifest_path = File.expand_path "../Pods/Manifest.lock", podfile_path

  return true unless File.exist?(lockfile_path) && File.exist?(manifest_path)

  lockfile = Pod::Lockfile.from_file Pathname.new lockfile_path
  manifest = Pod::Lockfile.from_file Pathname.new manifest_path

  # diff the contents of Podfile.lock and Pods/Manifest.lock
  # This is just what is done in the "[CP] Check Pods Manifest.lock" script build phase
  # in a project using CocoaPods.
  return true unless lockfile == manifest

  # compare checksum of Podfile with checksum in Podfile.lock
  # This is a good sanity check, but perhaps unnecessary. It means pod install
  # has not been run since the Podfile was modified, which is probably an oversight.
  return true unless lockfile.to_hash["PODFILE CHECKSUM"] == podfile.checksum

  false
end
print_identification() click to toggle source
prompt_for_option(option) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 520
def prompt_for_option(option)
  say "<%= color('#{option.label}', BOLD) %>\n\n"
  say "#{option.description}\n\n"
  value = send option.confirm_symbol
  say "<%= color('Type', BOLD) %>: #{option.ui_type}\n"
  say "<%= color('Current value', BOLD) %>: #{option.display_value(value)}"
  say "<%= color('Example', BOLD) %>: #{option.example}" if option.example
  say "\n"

  valid_values = option.valid_values

  if valid_values && !option.type.nil? && option.type != Array
    new_value = choose(*valid_values) do |menu|
      menu.readline = true
      menu.prompt = "Please choose from this list. "
    end

    # Valid because chosen from list
  elsif valid_values && option.type == Array
    # There seems to be a problem with using menu.gather, so we do this.
    valid_values.each do |v|
      say "#{v}\n"
    end

    new_value = ask "Please enter one or more of the above, separated by commas: " do |q|
      q.readline = true
      q.completion = valid_values
    end

    new_value = new_value.split(",") # comma-split with ask("...", Array) not working
  elsif option.type.nil?
    new_value = Helper::Util.confirm "#{option.label}? ", value
  else
    new_value = ask "Please enter a new value for #{option.label}: ", option.type
  end

  new_value = option.convert new_value

  return false unless option.valid?(new_value)
  instance_variable_set "@#{option.confirm_symbol}", new_value
  true
end
relative_path(path) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 156
def relative_path(path)
  return nil if path.nil?

  path = Pathname.new(path) unless path.kind_of? Pathname
  return path.to_s unless path.absolute?

  path.relative_path_from(root).to_s
end
root() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 137
def root
  return @root if @root
  if workspace
    @root = Pathname.new(workspace_path).dirname
  else
    @root = Pathname.new(xcodeproj_path).dirname
  end
  @root
end
swift_version() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 431
def swift_version
  return @swift_version if @swift_version

  return nil unless target
  @swift_version = target.resolved_build_setting("SWIFT_VERSION")["Release"]
  @swift_version
end
target_name() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 129
def target_name
  target.name.nil? ? nil : target.name
end
uses_frameworks?() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 357
def uses_frameworks?
  return nil unless podfile
  target_definition = podfile.target_definition_list.find { |t| t.name == target.name }
  return nil unless target_definition
  target_definition.uses_frameworks?
end
validate_buildfile_at_path(buildfile_path, filename) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 265
def validate_buildfile_at_path(buildfile_path, filename)
  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." and return false unless valid
  end

  if filename == "Podfile" && open_podfile(buildfile_path)
    true
  elsif filename == "Cartfile"
    @cartfile_path = buildfile_path
    @sdk_integration_mode = :carthage
    true
  else
    false
  end
end
validate_buildfile_path(buildfile_path, filename) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 229
def validate_buildfile_path(buildfile_path, filename)
  # Disable Podfile/Cartfile update if --no-add-sdk is present
  return unless sdk_integration_mode.nil?

  # No --podfile or --cartfile option
  if buildfile_path.nil?
    # 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
  end

  # Validate. Prompt if not valid.
  buildfile_path = ask "Please enter the path to your #{filename}: " while !buildfile_path || !validate_buildfile_at_path(buildfile_path, filename)

  @sdk_integration_mode = filename == "Podfile" ? :cocoapods : :carthage
end
validate_key(key, type, options = {}) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 326
def validate_key(key, type, options = {})
  return if options[:accept_nil] && key.nil?
  key = ask "Please enter your #{type} Branch key or use --#{type}-key [enter for none]: " until key_valid? key, type
  @keys[type] = key unless key.empty?
  instance_variable_set "@#{type}_key", key
end
validate_keys(optional: false) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 285
def validate_keys(optional: false)
  @keys = {}
  @apps = {}

  # 1. Check the options passed in. If nothing (nil) passed, continue.
  validate_key options.live_key, :live, accept_nil: true
  validate_key options.test_key, :test, accept_nil: true

  # 2. Did we find a valid key above?
  while !optional && @keys.empty?
    # 3. If not, prompt.
    say "A live key, a test key or both is required."
    validate_key nil, :live
    validate_key nil, :test
  end

  # 4. We have at least one valid key now, unless optional is truthy.
end
validate_options() click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 105
def validate_options
  # implemented in subclasses
end
validate_target(allow_extensions = true) click to toggle source
# File lib/branch_io_cli/configuration/configuration.rb, line 199
def validate_target(allow_extensions = true)
  return if @target

  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 = helper.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_xcodeproj_path() click to toggle source
  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.

# File lib/branch_io_cli/configuration/configuration.rb, line 168
def validate_xcodeproj_path
  if options.xcodeproj
    path = options.xcodeproj
  else
    path = find_project
  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
    return if open_xcodeproj path
  end
end