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.



850
851
852
853
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 850

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

Instance Method Details

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



959
960
961
962
963
964
965
966
967
968
969
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 959

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



867
868
869
870
871
872
873
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 867

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



877
878
879
880
881
882
883
884
885
886
887
888
889
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 877

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



929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 929

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



1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1006

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



893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 893

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



1042
1043
1044
1045
1046
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1042

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



991
992
993
994
995
996
997
998
999
1000
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 991

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)


978
979
980
981
982
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 978

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



1049
1050
1051
1052
1053
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1049

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

  nil
end