Class: Dependabot::NpmAndYarn::YarnErrorHandler

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb

Instance Method Summary collapse

Constructor Details

#initialize(dependencies:, dependency_files:) ⇒ YarnErrorHandler

Returns a new instance of YarnErrorHandler.



905
906
907
908
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 905

def initialize(dependencies:, dependency_files:)
  @dependencies = dependencies
  @dependency_files = dependency_files
end

Instance Method Details

#create_error(handler, message, error, params) ⇒ Object



1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1014

def create_error(handler, message, error, params)
  handler.call(
    message,
    error,
    {
      dependencies: dependencies,
      dependency_files: dependency_files,
      **params
    }
  )
end

#find_usage_error(error_message) ⇒ Object



922
923
924
925
926
927
928
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 922

def find_usage_error(error_message)
  start_index = error_message.rindex(YARN_USAGE_ERROR_TEXT)
  return nil unless start_index

  error_details = error_message[start_index..-1]
  error_details&.strip
end

#handle_error(error, params) ⇒ Object



932
933
934
935
936
937
938
939
940
941
942
943
944
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 932

def handle_error(error, params)
  error_message = error.message

  # Extract the usage error message from the raw error message
  usage_error_message = find_usage_error(error_message) || ""

  # Check if the error message contains any group patterns and raise the corresponding error class
  handle_group_patterns(error, usage_error_message, params)

  # Check if defined yarn error codes contained in the error message
  # and raise the corresponding error class
  handle_yarn_error(error, params)
end

#handle_group_patterns(error, usage_error_message, params) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity



984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 984

def handle_group_patterns(error, usage_error_message, params) # rubocop:disable Metrics/PerceivedComplexity
  error_message = error.message.gsub(/\e\[\d+(;\d+)*m/, "")
  VALIDATION_GROUP_PATTERNS.each do |group|
    patterns = group[:patterns]
    matchfn = group[:matchfn]
    handler = group[:handler]
    in_usage = group[:in_usage] || false

    next unless (patterns || matchfn) && handler

    message = usage_error_message.empty? ? error_message : usage_error_message
    if in_usage && pattern_in_message(patterns, usage_error_message)
      raise create_error(handler, message, error, params)
    elsif !in_usage && pattern_in_message(patterns, error_message)
      raise create_error(handler, error_message, error, params)
    end

    raise create_error(handler, message, error, params) if matchfn&.call(usage_error_message, error_message)
  end
end

#handle_package_not_found(error_message, yarn_lock) ⇒ Object

rubocop:disable Metrics/PerceivedComplexity



1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1061

def handle_package_not_found(error_message, yarn_lock) # rubocop:disable Metrics/PerceivedComplexity
  # There are 2 different package not found error messages
  package_not_found = error_message.include?(PACKAGE_NOT_FOUND)
  package_not_found2 = error_message.match?(PACKAGE_NOT_FOUND2)

  # If non of the patterns are found, return an empty hash
  return {} unless package_not_found || package_not_found2

  sanitized_name = T.let(nil, T.nilable(String))

  if package_not_found
    package_name =
      error_message
      .match(PACKAGE_NOT_FOUND_PACKAGE_NAME_REGEX)
      &.named_captures
      &.[](PACKAGE_NOT_FOUND_PACKAGE_NAME_CAPTURE)
      &.split(PACKAGE_NOT_FOUND_PACKAGE_NAME_CAPTURE_SPLIT_REGEX)
      &.first
  end

  if package_not_found2
    package_name =
      error_message
      .match(PACKAGE_NOT_FOUND2_PACKAGE_NAME_REGEX)
      &.named_captures
      &.[](PACKAGE_NOT_FOUND2_PACKAGE_NAME_CAPTURE)
  end

  raise_resolvability_error(error_message, yarn_lock) unless package_name
  sanitized_name = sanitize_package_name(package_name) if package_name
  error_message = error_message.gsub(package_name, sanitized_name) if package_name && sanitized_name
  { sanitized_name: sanitized_name, sanitized_message: error_message }
end

#handle_yarn_error(error, params) ⇒ Object



948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 948

def handle_yarn_error(error, params)
  ## Clean error message from ANSI escape codes
  error_message = error.message.gsub(/\e\[\d+(;\d+)*m/, "")
  matches = error_message.scan(YARN_CODE_REGEX)
  return if matches.empty?

  # Go through each match backwards in the error message and raise the corresponding error class
  matches.reverse_each do |match|
    code = match[0]
    next unless code

    yarn_error = YARN_ERROR_CODES[code]
    next unless yarn_error.is_a?(Hash)

    message = yarn_error[:message]
    handler = yarn_error[:handler]
    next unless handler

    modified_error_message = if message
                               "[#{code}]: #{message}, Detail: #{error_message}"
                             else
                               "[#{code}]: #{error_message}"
                             end

    raise  create_error(handler, modified_error_message, error, params)
  end
end

#package_missing(error_message) ⇒ Object



1097
1098
1099
1100
1101
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1097

def package_missing(error_message)
  names = dependencies.map(&:name)
  package_missing = names.any? { |name| error_message.include?("find package \"#{name}") }
  !!error_message.match(PACKAGE_MISSING_REGEX) || package_missing
end

#pattern_in_message(patterns, message) ⇒ Object



1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1046

def pattern_in_message(patterns, message)
  patterns.each do |pattern|
    if pattern.is_a?(String)
      return true if message.include?(pattern)
    elsif pattern.is_a?(Regexp)
      return true if message.gsub(/\e\[[\d;]*[A-Za-z]/, "").match?(pattern)
    end
  end
  false
end

#raise_resolvability_error(error_message, yarn_lock) ⇒ Object

Raises:

  • (Dependabot::DependencyFileNotResolvable)


1033
1034
1035
1036
1037
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1033

def raise_resolvability_error(error_message, yarn_lock)
  dependency_names = dependencies.map(&:name).join(", ")
  msg = "Error whilst updating #{dependency_names} in #{yarn_lock.path}:\n#{error_message}"
  raise Dependabot::DependencyFileNotResolvable, msg
end

#sanitize_package_name(package_name) ⇒ Object



1104
1105
1106
1107
1108
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1104

def sanitize_package_name(package_name)
  return package_name.gsub("%2f", "/").gsub("%2F", "/") if package_name

  nil
end