Class: Dependabot::NpmAndYarn::UpdateChecker::VersionResolver

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

Constant Summary collapse

TIGHTLY_COUPLED_MONOREPOS =
T.let(
  {
    "vue" => %w(vue vue-template-compiler)
  }.freeze,
  T::Hash[String, T::Array[String]]
)
MAX_TRUST_DOWNGRADE_FALLBACK_ATTEMPTS =

Maximum number of older versions to check when the latest version triggers a pnpm trust downgrade. Prevents excessive subprocess spawning for packages with many published versions.

5
YARN_PEER_DEP_ERROR_REGEX =

Error message returned by ‘yarn add` (for Yarn classic): “ > @reach/router@1.2.1” has incorrect peer dependency “react@15.x || 16.x || 16.4.0-alpha.0911da3” “workspace-aggregator-<random-string> > test > react-dom@15.6.2” has incorrect peer dependency “react@^15.6.2” “ > react-burger-menu@1.9.9” has unmet peer dependency “react@>=0.14.0 <16.0.0”

/
  \s>\s(?<requiring_dep>[^>"]+)"\s
  has\s(incorrect|unmet)\speer\sdependency\s
  "(?<required_dep>[^"]+)"
/x
YARN_BERRY_PEER_DEP_ERROR_REGEX =

Error message returned by ‘yarn add` (for Yarn berry): YN0060: │ eve-roster@workspace:. provides jest (p8d618) \ with version 29.3.0, which doesn’t satisfy \ what ts-jest requestsn

/
  YN0060:.+\sprovides\s(?<required_dep>.+?)\s\((?<info_hash>\w+)\).+what\s(?<requiring_dep>.+?)\srequests
/x
YARN_BERRY_V4_PEER_DEP_ERROR_REGEX =

Error message returned by ‘yarn add` (for Yarn berry v4): YN0060: │ react is listed by your project with version 15.2.0, \ which doesn’t satisfy what react-dom (p89012) requests (^16.0.0).

/
  YN0060:.+\s(?<required_dep>.+?)\sis\s.+what\s(?<requiring_dep>.+?)\s\((?<info_hash>\w+)\)\srequests
/x
PNPM_PEER_DEP_ERROR_REGEX =

Error message returned by ‘pnpm update`: └─┬ react-dom 15.7.0

└── ✕ unmet peer react@^15.7.0: found 16.3.1
/
  ┬\s(?<requiring_dep>[^\n]+)\n
  [^\n]*✕\sunmet\speer\s(?<required_dep>[^:]+):
/mx
NPM6_PEER_DEP_ERROR_REGEX =

Error message returned by ‘npm install` (for NPM 6): react-dom@15.2.0 requires a peer of react@^15.2.0 \ but none is installed. You must install peer dependencies yourself.

/
  (?<requiring_dep>[^\s]+)\s
  requires\sa\speer\sof\s
  (?<required_dep>.+?)\sbut\snone\sis\sinstalled.
/x
NPM8_PEER_DEP_ERROR_REGEX =

Error message returned by ‘npm install` (for NPM 8): npm ERR! Could not resolve dependency: npm ERR! peer react@“^16.14.0” from react-dom@16.14.0

or with two semver constraints: npm ERR! Could not resolve dependency: npm ERR! peer @opentelemetry/api@“>=1.0.0 <1.1.0” from @opentelemetry/context-async-hooks@1.0.1

/
  npm\s(?:WARN|ERR!)\sCould\snot\sresolve\sdependency:\n
  npm\s(?:WARN|ERR!)\speer\s(?<required_dep>\S+@\S+(\s\S+)?)\sfrom\s(?<requiring_dep>\S+@\S+)
/x

Instance Method Summary collapse

Constructor Details

#initialize(dependency:, dependency_files:, credentials:, latest_allowable_version:, latest_version_finder:, repo_contents_path:, dependency_group: nil, raise_on_ignored: false, update_cooldown: nil) ⇒ VersionResolver

rubocop:disable Metrics/AbcSize



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb', line 115

def initialize( # rubocop:disable Metrics/AbcSize
  dependency:,
  dependency_files:,
  credentials:,
  latest_allowable_version:,
  latest_version_finder:,
  repo_contents_path:,
  dependency_group: nil,
  raise_on_ignored: false,
  update_cooldown: nil
)
  @dependency               = dependency
  @dependency_files         = dependency_files
  @credentials              = credentials
  @latest_allowable_version = latest_allowable_version
  @dependency_group = dependency_group

  @latest_version_finder = T.let({}, T::Hash[Dependabot::Dependency, PackageLatestVersionFinder])
  @latest_version_finder[dependency] = latest_version_finder
  @repo_contents_path = repo_contents_path
  @raise_on_ignored = raise_on_ignored
  @update_cooldown = update_cooldown

  @types_package = T.let(nil, T.nilable(Dependabot::Dependency))
  @original_package = T.let(nil, T.nilable(Dependabot::Dependency))
  @latest_types_package_version = T.let(nil, T.nilable(Dependabot::Version))
  @dependency_files_builder = T.let(nil, T.nilable(DependencyFilesBuilder))
  # @latest_resolvable_version = T.let(nil, T.nilable(T.any(String, Gem::Version)))
  @resolve_latest_previous_version = T.let({}, T::Hash[Dependabot::Dependency, T.nilable(String)])
  @paths_requiring_update_check = T.let(nil, T.nilable(T::Array[String]))
  @top_level_dependencies = T.let(nil, T.nilable(T::Array[Dependabot::Dependency]))
  # @peer_dependency_errors_checked = T.let(false, T::Boolean)
  @old_peer_dependency_errors = T.let(
    nil, T.nilable(T::Array[T.any(T::Hash[String, T.nilable(String)], String)])
  )
  @peer_dependency_errors = T.let(nil, T.nilable(T::Array[T.any(T::Hash[String, T.nilable(String)], String)]))
  @trust_downgrade_detected = T.let(false, T::Boolean)
end

Instance Method Details

#dependency_updates_from_full_unlockObject



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb', line 195

def dependency_updates_from_full_unlock
  return if git_dependency?(dependency)
  return updated_monorepo_dependencies if part_of_tightly_locked_monorepo?
  return if newly_broken_peer_reqs_from_dep.any?
  return if original_package_update_available?

  updates = [{
    dependency: dependency,
    version: latest_allowable_version,
    previous_version: latest_resolvable_previous_version(
      latest_allowable_version
    )
  }]
  newly_broken_peer_reqs_on_dep.each do |peer_req|
    dep_name = peer_req.fetch(:requiring_dep_name)
    dep = top_level_dependencies.find { |d| d.name == dep_name }

    # Can't handle reqs from sub-deps or git source deps (yet)
    return nil if dep.nil?
    return nil if git_dependency?(dep)

    updated_version =
      latest_version_of_dep_with_satisfied_peer_reqs(dep)
    return nil unless updated_version

    updates << {
      dependency: dep,
      version: updated_version,
      previous_version: resolve_latest_previous_version(
        dep, updated_version
      )
    }
  end
  updates += updated_types_dependencies if types_update_available?
  updates.uniq
end

#latest_resolvable_previous_version(updated_version) ⇒ Object



189
190
191
# File 'lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb', line 189

def latest_resolvable_previous_version(updated_version)
  resolve_latest_previous_version(dependency, updated_version)
end

#latest_resolvable_versionObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb', line 155

def latest_resolvable_version
  return latest_allowable_version if git_dependency?(dependency)
  return if part_of_tightly_locked_monorepo?
  return if types_update_available?
  return if original_package_update_available?

  # Trigger peer dependency check which also detects trust downgrades
  has_unmet_peers = relevant_unmet_peer_dependencies.any?

  if @trust_downgrade_detected
    Dependabot.logger.info(
      "pnpm trust downgrade detected for #{dependency.name}@#{latest_allowable_version}, " \
      "trying older versions"
    )
    return find_version_without_trust_downgrade
  end

  return latest_allowable_version unless has_unmet_peers

  satisfying_versions.first
end

#latest_version_resolvable_with_full_unlock?Boolean

Returns:

  • (Boolean)


178
179
180
181
182
# File 'lib/dependabot/npm_and_yarn/update_checker/version_resolver.rb', line 178

def latest_version_resolvable_with_full_unlock?
  return false if dependency_updates_from_full_unlock.nil?

  true
end