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.



819
820
821
822
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 819

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

Instance Method Details

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



928
929
930
931
932
933
934
935
936
937
938
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 928

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



836
837
838
839
840
841
842
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 836

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



846
847
848
849
850
851
852
853
854
855
856
857
858
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 846

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



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

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



975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 975

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



862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 862

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



1011
1012
1013
1014
1015
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1011

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



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

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)


947
948
949
950
951
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 947

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



1018
1019
1020
1021
1022
# File 'lib/dependabot/npm_and_yarn/file_updater/yarn_lockfile_updater.rb', line 1018

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

  nil
end