class Fastlane::Runner

Attributes

current_lane[RW]

Symbol for the current lane

current_platform[RW]

Symbol for the current platform

lanes[RW]

@return [Hash] All the lanes available, first the platform, then the lane

Public Instance Methods

action_completed(action_name, status: nil, exception: nil) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 281
def action_completed(action_name, status: nil, exception: nil)
  #  https://github.com/fastlane/fastlane/issues/11913
  # if exception.nil? || exception.fastlane_should_report_metrics?
  #   action_completion_context = FastlaneCore::ActionCompletionContext.context_for_action_name(action_name, args: ARGV, status: status)
  #   FastlaneCore.session.action_completed(completion_context: action_completion_context)
  # end
end
add_lane(lane, override = false) click to toggle source

@param lane [Lane] A lane object

# File fastlane/lib/fastlane/runner.rb, line 311
def add_lane(lane, override = false)
  lanes[lane.platform] ||= {}

  if !override && lanes[lane.platform][lane.name]
    UI.user_error!("Lane '#{lane.name}' was defined multiple times!")
  end

  lanes[lane.platform][lane.name] = lane
end
after_all_blocks() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 370
def after_all_blocks
  @after_all ||= {}
end
after_each_blocks() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 362
def after_each_blocks
  @after_each ||= {}
end
available_lanes(filter_platform = nil) click to toggle source

@param filter_platform: Filter, to only show the lanes of a given platform @return an array of lanes (platform lane_name) to print them out to the user

# File fastlane/lib/fastlane/runner.rb, line 77
def available_lanes(filter_platform = nil)
  all = []
  lanes.each do |platform, platform_lanes|
    next if filter_platform && filter_platform.to_s != platform.to_s # skip actions that don't match

    platform_lanes.each do |lane_name, lane|
      all << [platform, lane_name].reject(&:nil?).join(' ') unless lane.is_private
    end
  end
  all
end
before_all_blocks() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 366
def before_all_blocks
  @before_all ||= {}
end
before_each_blocks() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 358
def before_each_blocks
  @before_each ||= {}
end
class_reference_from_action_alias(method_sym) click to toggle source

Pass a action alias symbol (e.g. :enable_automatic_code_signing) and this method will return a reference to the action class if it exists. In case the action with this alias can't be found this method will return nil.

# File fastlane/lib/fastlane/runner.rb, line 108
def class_reference_from_action_alias(method_sym)
  alias_found = find_alias(method_sym.to_s)
  return nil unless alias_found

  class_reference_from_action_name(alias_found.to_sym)
end
class_reference_from_action_name(method_sym) click to toggle source

Pass a action symbol (e.g. :deliver or :commit_version_bump) and this method will return a reference to the action class if it exists. In case the action with this name can't be found this method will return nil. This method is being called by `trigger_action_by_name` to see if a given action is available (either built-in or loaded from a plugin) and is also being called from the fastlane docs generator

# File fastlane/lib/fastlane/runner.rb, line 96
def class_reference_from_action_name(method_sym)
  method_str = method_sym.to_s.delete("?") # as a `?` could be at the end of the method name
  class_ref = Actions.action_class_ref(method_str)

  return class_ref if class_ref && class_ref.respond_to?(:run)
  nil
end
did_finish() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 354
def did_finish
  # to maintain compatibility with other sibling classes that have this API
end
error_blocks() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 374
def error_blocks
  @error_blocks ||= {}
end
execute(lane, platform = nil, parameters = nil) click to toggle source

This will take care of executing one lane. That's when the user triggers a lane from the CLI for example This method is not executed when switching a lane @param lane_name The name of the lane to execute @param platform The name of the platform to execute @param parameters [Hash] The parameters passed from the command line to the lane

# File fastlane/lib/fastlane/runner.rb, line 21
def execute(lane, platform = nil, parameters = nil)
  UI.crash!("No lane given") unless lane

  self.current_lane = lane.to_sym
  self.current_platform = (platform ? platform.to_sym : nil)

  lane_obj = lanes.fetch(current_platform, {}).fetch(current_lane, nil)

  UI.user_error!("Could not find lane '#{full_lane_name}'. Available lanes: #{available_lanes.join(', ')}") unless lane_obj
  UI.user_error!("You can't call the private lane '#{lane}' directly") if lane_obj.is_private

  ENV["FASTLANE_LANE_NAME"] = current_lane.to_s
  ENV["FASTLANE_PLATFORM_NAME"] = (current_platform ? current_platform.to_s : nil)

  Actions.lane_context[Actions::SharedValues::PLATFORM_NAME] = current_platform
  Actions.lane_context[Actions::SharedValues::LANE_NAME] = full_lane_name

  UI.success("Driving the lane '#{full_lane_name}' 🚀")

  return_val = nil

  path_to_use = FastlaneCore::FastlaneFolder.path || Dir.pwd
  parameters ||= {}
  begin
    Dir.chdir(path_to_use) do # the file is located in the fastlane folder
      execute_flow_block(before_all_blocks, current_platform, current_lane, parameters)
      execute_flow_block(before_each_blocks, current_platform, current_lane, parameters)

      return_val = lane_obj.call(parameters) # by default no parameters

      # after blocks are only called if no exception was raised before
      # Call the platform specific after block and then the general one
      execute_flow_block(after_each_blocks, current_platform, current_lane, parameters)
      execute_flow_block(after_all_blocks, current_platform, current_lane, parameters)
    end

    return return_val
  rescue => ex
    Dir.chdir(path_to_use) do
      # Provide error block exception without color code
      begin
        error_blocks[current_platform].call(current_lane, ex, parameters) if current_platform && error_blocks[current_platform]
        error_blocks[nil].call(current_lane, ex, parameters) if error_blocks[nil]
      rescue => error_block_exception
        UI.error("An error occurred while executing the `error` block:")
        UI.error(error_block_exception.to_s)
        raise ex # raise the original error message
      end
    end

    raise ex
  end
end
execute_action(method_sym, class_ref, arguments, custom_dir: nil, from_action: false) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 218
def execute_action(method_sym, class_ref, arguments, custom_dir: nil, from_action: false)
  if from_action == true
    custom_dir = "." # We preserve the directory from where the previous action was called from
  elsif custom_dir.nil?
    custom_dir ||= "." if Helper.test?
    custom_dir ||= ".."
  end

  verify_supported_os(method_sym, class_ref)

  begin
    Dir.chdir(custom_dir) do # go up from the fastlane folder, to the project folder
      # Removing step_name before its parsed into configurations
      args = arguments.kind_of?(Array) && arguments.first.kind_of?(Hash) ? arguments.first : {}
      step_name = args.delete(:step_name)

      # arguments is an array by default, containing an hash with the actual parameters
      # Since we usually just need the passed hash, we'll just use the first object if there is only one
      if arguments.count == 0
        configurations = ConfigurationHelper.parse(class_ref, {}) # no parameters => empty hash
      elsif arguments.count == 1 && arguments.first.kind_of?(Hash)
        configurations = ConfigurationHelper.parse(class_ref, arguments.first) # Correct configuration passed
      elsif !class_ref.available_options
        # This action does not use the new action format
        # Just passing the arguments to this method
        configurations = arguments
      else
        UI.user_error!("You have to call the integration like `#{method_sym}(key: \"value\")`. Run `fastlane action #{method_sym}` for all available keys. Please check out the current documentation on GitHub.")
      end

      # If another action is calling this action, we shouldn't show it in the summary
      # A nil value for action_name will hide it from the summary
      unless from_action
        action_name = step_name
        action_name ||= class_ref.method(:step_text).arity == 1 ? class_ref.step_text(configurations) : class_ref.step_text
      end

      Actions.execute_action(action_name) do
        if Fastlane::Actions.is_deprecated?(class_ref)
          puts("==========================================".deprecated)
          puts("This action (#{method_sym}) is deprecated".deprecated)
          puts(class_ref.deprecated_notes.to_s.remove_markdown.deprecated) if class_ref.deprecated_notes
          puts("==========================================\n".deprecated)
        end
        class_ref.runner = self # needed to call another action from an action
        return class_ref.run(configurations)
      end
    end
  rescue Interrupt => e
    raise e # reraise the interruption to avoid logging this as a crash
  rescue FastlaneCore::Interface::FastlaneCommonException => e # these are exceptions that we don't count as crashes
    raise e
  rescue FastlaneCore::Interface::FastlaneError => e # user_error!
    action_completed(method_sym.to_s, status: FastlaneCore::ActionCompletionStatus::USER_ERROR, exception: e)
    raise e
  rescue Exception => e # rubocop:disable Lint/RescueException
    # high chance this is actually FastlaneCore::Interface::FastlaneCrash, but can be anything else
    # Catches all exceptions, since some plugins might use system exits to get out
    action_completed(method_sym.to_s, status: FastlaneCore::ActionCompletionStatus::FAILED, exception: e)
    raise e
  end
end
execute_flow_block(block, current_platform, lane, parameters) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 289
def execute_flow_block(block, current_platform, lane, parameters)
  # Call the platform specific block and default back to the general one
  block[current_platform].call(lane, parameters) if block[current_platform] && current_platform
  block[nil].call(lane, parameters) if block[nil]
end
find_alias(action_name) click to toggle source

lookup if an alias exists

# File fastlane/lib/fastlane/runner.rb, line 116
def find_alias(action_name)
  Actions.alias_actions.each do |key, v|
    next unless Actions.alias_actions[key]
    next unless Actions.alias_actions[key].include?(action_name)
    return key
  end
  nil
end
full_lane_name() click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 12
def full_lane_name
  [current_platform, current_lane].reject(&:nil?).join(' ')
end
set_after_all(platform, block) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 336
def set_after_all(platform, block)
  unless after_all_blocks[platform].nil?
    UI.error("You defined multiple `after_all` blocks in your `Fastfile`. The last one being set will be used.")
  end
  after_all_blocks[platform] = block
end
set_after_each(platform, block) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 325
def set_after_each(platform, block)
  after_each_blocks[platform] = block
end
set_before_all(platform, block) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 329
def set_before_all(platform, block)
  unless before_all_blocks[platform].nil?
    UI.error("You defined multiple `before_all` blocks in your `Fastfile`. The last one being set will be used.")
  end
  before_all_blocks[platform] = block
end
set_before_each(platform, block) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 321
def set_before_each(platform, block)
  before_each_blocks[platform] = block
end
set_error(platform, block) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 343
def set_error(platform, block)
  unless error_blocks[platform].nil?
    UI.error("You defined multiple `error` blocks in your `Fastfile`. The last one being set will be used.")
  end
  error_blocks[platform] = block
end
trigger_action_by_name(method_sym, custom_dir, from_action, *arguments) click to toggle source

This is being called from `method_missing` from the Fastfile It's also used when an action is called from another action @param from_action Indicates if this action is being trigged by another action.

If so, it won't show up in summary.
# File fastlane/lib/fastlane/runner.rb, line 129
def trigger_action_by_name(method_sym, custom_dir, from_action, *arguments)
  # First, check if there is a predefined method in the actions folder
  class_ref = class_reference_from_action_name(method_sym)
  unless class_ref
    class_ref = class_reference_from_action_alias(method_sym)
    # notify action that it has been used by alias
    if class_ref.respond_to?(:alias_used)
      orig_action = method_sym.to_s
      arguments = [{}] if arguments.empty?
      class_ref.alias_used(orig_action, arguments.first)
    end
  end

  # It's important to *not* have this code inside the rescue block
  # otherwise all NameErrors will be caught and the error message is
  # confusing
  begin
    return self.try_switch_to_lane(method_sym, arguments)
  rescue LaneNotAvailableError
    # We don't actually handle this here yet
    # We just try to use a user configured lane first
    # and only if there is none, we're gonna check for the
    # built-in actions
  end

  if class_ref
    if class_ref.respond_to?(:run)
      # Action is available, now execute it
      return self.execute_action(method_sym, class_ref, arguments, custom_dir: custom_dir, from_action: from_action)
    else
      UI.user_error!("Action '#{method_sym}' of class '#{class_name}' was found, but has no `run` method.")
    end
  end

  # No lane, no action, let's at least show the correct error message
  if Fastlane.plugin_manager.plugin_is_added_as_dependency?(PluginManager.plugin_prefix + method_sym.to_s)
    # That's a plugin, but for some reason we can't find it
    UI.user_error!("Plugin '#{method_sym}' was not properly loaded, make sure to follow the plugin docs for troubleshooting: #{PluginManager::TROUBLESHOOTING_URL}")
  elsif Fastlane::Actions.formerly_bundled_actions.include?(method_sym.to_s)
    # This was a formerly bundled action which is now a plugin.
    UI.verbose(caller.join("\n"))
    UI.user_error!("The action '#{method_sym}' is no longer bundled with fastlane. You can install it using `fastlane add_plugin #{method_sym}`")
  else
    # So there is no plugin under that name, so just show the error message generated by the lane switch
    UI.verbose(caller.join("\n"))
    UI.user_error!("Could not find action, lane or variable '#{method_sym}'. Check out the documentation for more details: https://docs.fastlane.tools/actions")
  end
end
try_switch_to_lane(new_lane, parameters) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 185
def try_switch_to_lane(new_lane, parameters)
  block = lanes.fetch(current_platform, {}).fetch(new_lane, nil)
  block ||= lanes.fetch(nil, {}).fetch(new_lane, nil) # fallback to general lane for multiple platforms
  if block
    original_full = full_lane_name
    original_lane = current_lane

    UI.user_error!("Parameters for a lane must always be a hash") unless (parameters.first || {}).kind_of?(Hash)

    execute_flow_block(before_each_blocks, current_platform, new_lane, parameters)

    pretty = [new_lane]
    pretty = [current_platform, new_lane] if current_platform
    Actions.execute_action("Switch to #{pretty.join(' ')} lane") {} # log the action
    UI.message("Cruising over to lane '#{pretty.join(' ')}' 🚖")

    # Actually switch lane now
    self.current_lane = new_lane

    result = block.call(parameters.first || {}) # to always pass a hash
    self.current_lane = original_lane

    # after blocks are only called if no exception was raised before
    # Call the platform specific after block and then the general one
    execute_flow_block(after_each_blocks, current_platform, new_lane, parameters)

    UI.message("Cruising back to lane '#{original_full}' 🚘")
    return result
  else
    raise LaneNotAvailableError.new, "Lane not found"
  end
end
verify_supported_os(name, class_ref) click to toggle source
# File fastlane/lib/fastlane/runner.rb, line 295
def verify_supported_os(name, class_ref)
  if class_ref.respond_to?(:is_supported?)
    # This value is filled in based on the executed platform block. Might be nil when lane is in root of Fastfile
    platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
    if platform
      unless class_ref.is_supported?(platform)
        UI.important("Action '#{name}' isn't known to support operating system '#{platform}'.")
      end
    end
  end
end