<commit_before>module Fastlane
  class Runner
    # Symbol for the current lane
    attr_accessor :current_lane

    # Symbol for the current platform
    attr_accessor :current_platform

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

    def full_lane_name
      [current_platform, current_lane].reject(&:nil?).join(' ')
    end

    # 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
    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 = Fastlane::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 colour code
          error_ex = ex.exception(ex.message.gsub(/\033\[\d+m/, ''))

          error_blocks[current_platform].call(current_lane, error_ex, parameters) if error_blocks[current_platform] && current_platform
          error_blocks[nil].call(current_lane, error_ex, parameters) if error_blocks[nil]
        end

        raise ex
      end
    end

    # @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
    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

    # This is being called from `method_missing` from the Fastfile
    # It's also used when an action is called from another action
    def trigger_action_by_name(method_sym, custom_dir, *arguments)
      method_str = method_sym.to_s
      method_str.delete!('?') # as a `?` could be at the end of the method name

      # First, check if there is a predefined method in the actions folder
      class_ref = Actions.action_class_ref(method_str)

      # It's important to *not* have this code inside the rescue block
      # otherwise all NameErrors will be caught and the error message is
      # confusing
      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)
        else
          UI.user_error!("Action '#{method_sym}' of class '#{class_name}' was found, but has no `run` method.")
        end
      else
        # Action was not found
        # Is there a lane under this name?
        begin
          return self.try_switch_to_lane(method_sym, arguments)
        rescue LaneNotAvailableError
          # 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_str)
            # 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 or lane '#{method_sym}'. Check out the README for more details: https://github.com/fastlane/fastlane/tree/master/fastlane")
          end
        end
      end
    end

    #
    # All the methods that are usually called on execution
    #

    class LaneNotAvailableError < StandardError
    end

    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.success "Cruising over to lane '#{pretty.join(' ')}' 🚖"

        # Actually switch lane now
        self.current_lane = new_lane
        collector.did_launch_action(:lane_switch)
        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.success "Cruising back to lane '#{original_full}' 🚘".green
        return result
      else
        raise LaneNotAvailableError.new, "Lane not found"
      end
    end

    def execute_action(method_sym, class_ref, arguments, custom_dir: nil)
      custom_dir ||= '..'<commit_msg>[fastlane] Switching lane shouldn't be green (#5914)<commit_after>module Fastlane
  class Runner
    # Symbol for the current lane
    attr_accessor :current_lane

    # Symbol for the current platform
    attr_accessor :current_platform

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

    def full_lane_name
      [current_platform, current_lane].reject(&:nil?).join(' ')
    end

    # 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
    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 = Fastlane::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 colour code
          error_ex = ex.exception(ex.message.gsub(/\033\[\d+m/, ''))

          error_blocks[current_platform].call(current_lane, error_ex, parameters) if error_blocks[current_platform] && current_platform
          error_blocks[nil].call(current_lane, error_ex, parameters) if error_blocks[nil]
        end

        raise ex
      end
    end

    # @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
    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

    # This is being called from `method_missing` from the Fastfile
    # It's also used when an action is called from another action
    def trigger_action_by_name(method_sym, custom_dir, *arguments)
      method_str = method_sym.to_s
      method_str.delete!('?') # as a `?` could be at the end of the method name

      # First, check if there is a predefined method in the actions folder
      class_ref = Actions.action_class_ref(method_str)

      # It's important to *not* have this code inside the rescue block
      # otherwise all NameErrors will be caught and the error message is
      # confusing
      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)
        else
          UI.user_error!("Action '#{method_sym}' of class '#{class_name}' was found, but has no `run` method.")
        end
      else
        # Action was not found
        # Is there a lane under this name?
        begin
          return self.try_switch_to_lane(method_sym, arguments)
        rescue LaneNotAvailableError
          # 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_str)
            # 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 or lane '#{method_sym}'. Check out the README for more details: https://github.com/fastlane/fastlane/tree/master/fastlane")
          end
        end
      end
    end

    #
    # All the methods that are usually called on execution
    #

    class LaneNotAvailableError < StandardError
    end

    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
        collector.did_launch_action(:lane_switch)
        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

    def execute_action(method_sym, class_ref, arguments, custom_dir: nil)
      custom_dir ||= '..'
{'fraction_non_alphanumeric': 0.08228228228228228, 'fraction_numerical': 0.000667334000667334, 'mean_word_length': 3.043712898003238, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2339426', 'n_tokens_mistral': 4258, 'n_tokens_neox': 4135, 'n_words': 1368}