dependabot-bundler 0.331.0 → 0.333.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d913e03bbafff1bf6a1e905d3f6102572f398f2231748e7f3dc431cf67dae8b
4
- data.tar.gz: df7093e86cc83e024a6d6527fa6b8e1b180e1db936a7ae98f216e537a7561600
3
+ metadata.gz: ed2db4c89c832c17cf73a1183c1ca6645f8f8c141a357526eb997691227dade6
4
+ data.tar.gz: 41f43a96a647a712ba845860aaecd0bcca8a5aa316c93e578c271f5f7aabd50d
5
5
  SHA512:
6
- metadata.gz: ae5d37a7e93a31327fd166a9f491bcc032a77e739b45de5e0abf306c4f0cd0c9a5c09036ebad1540c88b539ede05abb0164f185c5434144db81d003279a43320
7
- data.tar.gz: fb3c5408c9a52a44df3caa36cd962bf29aa9ecca9d2ffec30d0a23ba5e58c743db813c072b5864bc273675b48c8bca28700b48e7c4adc8b410d7115aff97d0d5
6
+ metadata.gz: 8e1ed157de7a72eb7d78ca0cafce120e2517e1142a1f38a2fb676edd787db470da46f782f091123028c9a45c647d77f15740e4ee12fa189be9dbd1176f77aae8
7
+ data.tar.gz: 99d4ccb5ccb39ed52adca7aaaeb84e34356b7efa263541fbdd7a00cbca100b818d52311c3145f2abc73da100fb8e318b05c2fa9fbc5d501c5a7a0d06ff936bad
@@ -4,6 +4,7 @@
4
4
  require "sorbet-runtime"
5
5
  require "dependabot/file_fetchers"
6
6
  require "dependabot/file_fetchers/base"
7
+ require "dependabot/file_filtering"
7
8
  require "dependabot/bundler/file_updater/lockfile_updater"
8
9
  require "dependabot/bundler/cached_lockfile_parser"
9
10
  require "dependabot/errors"
@@ -52,7 +53,13 @@ module Dependabot
52
53
  fetched_files += path_gemspecs
53
54
  fetched_files += find_included_files(fetched_files)
54
55
 
55
- uniq_files(fetched_files)
56
+ # Filter excluded files from final collection
57
+ unique_files = uniq_files(fetched_files)
58
+ filtered_files = unique_files.reject do |file|
59
+ Dependabot::FileFiltering.should_exclude_path?(file.name, "file from final collection", @exclude_paths)
60
+ end
61
+
62
+ filtered_files
56
63
  end
57
64
 
58
65
  private
@@ -174,8 +181,12 @@ module Dependabot
174
181
  end
175
182
 
176
183
  @find_included_files ||= T.let(
177
- paths.map { |path| fetch_file_from_host(path) }
178
- .tap { |req_files| req_files.each { |f| f.support_file = true } },
184
+ paths.filter_map do |path|
185
+ # Skip excluded included files
186
+ next nil if Dependabot::FileFiltering.should_exclude_path?(path, "included file", @exclude_paths)
187
+
188
+ fetch_file_from_host(path)
189
+ end.tap { |req_files| req_files.each { |f| f.support_file = true } }, # rubocop:disable Style/MultilineBlockChain
179
190
  T.nilable(T::Array[DependencyFile])
180
191
  )
181
192
  end
@@ -238,6 +249,15 @@ module Dependabot
238
249
  next if previously_fetched_files.map(&:name).include?(path)
239
250
  next if file.name == path
240
251
 
252
+ # Skip excluded child Gemfiles
253
+ if Dependabot::Experiments.enabled?(:enable_exclude_paths_subdirectory_manifest_files) &&
254
+ !@exclude_paths.empty? && Dependabot::FileFiltering.exclude_path?(path, @exclude_paths)
255
+ raise Dependabot::DependencyFileNotEvaluatable,
256
+ "Cannot process requirements: '#{file.name}' references excluded file '#{path}'. " \
257
+ "Please either remove the reference from '#{file.name}' " \
258
+ "or update your exclude_paths configuration."
259
+ end
260
+
241
261
  fetched_file = fetch_file_from_host(path)
242
262
  grandchild_gemfiles = fetch_child_gemfiles(
243
263
  file: fetched_file,
@@ -20,6 +20,7 @@ module Dependabot
20
20
  module Bundler
21
21
  class FileParser < Dependabot::FileParsers::Base # rubocop:disable Metrics/ClassLength
22
22
  extend T::Sig
23
+
23
24
  require "dependabot/file_parsers/base/dependency_set"
24
25
  require "dependabot/bundler/file_parser/file_preparer"
25
26
  require "dependabot/bundler/file_parser/gemfile_declaration_finder"
@@ -122,21 +123,17 @@ module Dependabot
122
123
  parsed_gemfile.each do |dep|
123
124
  next unless gemfile_declaration_finder.gemfile_includes_dependency?(dep)
124
125
 
125
- dep =
126
- Dependency.new(
127
- name: dep.fetch("name"),
128
- version: dependency_version(dep.fetch("name"))&.to_s,
129
- requirements: [{
130
- requirement: gemfile_declaration_finder.enhanced_req_string(dep),
131
- groups: dep.fetch("groups").map(&:to_sym),
132
- source: dep.fetch("source")&.transform_keys(&:to_sym),
133
- file: file.name
134
- }],
135
- package_manager: "bundler"
136
- )
137
-
138
- file.dependencies << dep
139
- dependencies << dep
126
+ dependencies << Dependency.new(
127
+ name: dep.fetch("name"),
128
+ version: dependency_version(dep.fetch("name"))&.to_s,
129
+ requirements: [{
130
+ requirement: gemfile_declaration_finder.enhanced_req_string(dep),
131
+ groups: dep.fetch("groups").map(&:to_sym),
132
+ source: dep.fetch("source")&.transform_keys(&:to_sym),
133
+ file: file.name
134
+ }],
135
+ package_manager: "bundler"
136
+ )
140
137
  end
141
138
  end
142
139
 
@@ -144,7 +141,7 @@ module Dependabot
144
141
  end
145
142
 
146
143
  sig { returns(DependencySet) }
147
- def gemspec_dependencies # rubocop:disable Metrics/PerceivedComplexity,Metrics/AbcSize
144
+ def gemspec_dependencies # rubocop:disable Metrics/PerceivedComplexity
148
145
  @gemspec_dependencies = T.let(@gemspec_dependencies, T.nilable(DependencySet))
149
146
  return @gemspec_dependencies if @gemspec_dependencies
150
147
 
@@ -159,7 +156,7 @@ module Dependabot
159
156
  parsed_gemspec(gemspec).each do |dependency|
160
157
  next unless gemspec_declaration_finder.gemspec_includes_dependency?(dependency)
161
158
 
162
- dep = Dependency.new(
159
+ queue << Dependency.new(
163
160
  name: dependency.fetch("name"),
164
161
  version: dependency_version(dependency.fetch("name"))&.to_s,
165
162
  requirements: [{
@@ -174,9 +171,6 @@ module Dependabot
174
171
  }],
175
172
  package_manager: "bundler"
176
173
  )
177
-
178
- gemspec.dependencies << dep
179
- queue << dep
180
174
  end
181
175
  end
182
176
  end
@@ -198,23 +192,15 @@ module Dependabot
198
192
  parsed_lockfile.specs.each do |dependency|
199
193
  next if dependency.source.is_a?(::Bundler::Source::Path)
200
194
 
201
- # if a dependency is listed in the lockfiles' DEPENDENCIES section,
202
- # then it is a direct dependency & we want to keep track of that fact
203
- is_direct = parsed_lockfile.dependencies.key?(dependency.name)
204
-
205
- dep = Dependency.new(
195
+ dependencies << Dependency.new(
206
196
  name: dependency.name,
207
197
  version: dependency_version(dependency.name)&.to_s,
208
198
  requirements: [],
209
199
  package_manager: "bundler",
210
200
  subdependency_metadata: [{
211
201
  production: production_dep_names.include?(dependency.name)
212
- }],
213
- direct_relationship: is_direct
202
+ }]
214
203
  )
215
-
216
- T.must(lockfile).dependencies << dep
217
- dependencies << dep
218
204
  end
219
205
 
220
206
  dependencies
@@ -357,10 +343,6 @@ module Dependabot
357
343
  get_original_file("Gemfile.lock") || get_original_file("gems.locked"),
358
344
  T.nilable(Dependabot::DependencyFile)
359
345
  )
360
-
361
- # Set the lockfile as higher priority so we know to ignore the Gemfile, etc
362
- # when producing a graph.
363
- @lockfile&.tap { |f| f.priority = 1 }
364
346
  end
365
347
 
366
348
  sig { returns(T.untyped) }
@@ -204,7 +204,7 @@ module Dependabot
204
204
  cred["type"] == "rubygems_server" && cred.replaces_base?
205
205
  end
206
206
  host = credential ? credential["host"] : "rubygems.org"
207
- @base_url = "https://#{host}#{host&.end_with?('/') ? '' : '/'}"
207
+ @base_url = "https://#{host}#{'/' unless host&.end_with?('/')}"
208
208
  end
209
209
 
210
210
  def registry_auth_headers
@@ -43,17 +43,25 @@ module Dependabot
43
43
  @credentials = credentials
44
44
 
45
45
  @source_type = T.let(nil, T.nilable(String))
46
+ @options = T.let({}, T::Hash[Symbol, T.untyped])
47
+ @repo_contents_path = T.let(nil, T.nilable(String))
46
48
  end
47
49
 
48
50
  sig { returns(Dependabot::Dependency) }
49
51
  attr_reader :dependency
50
52
 
51
- sig { returns(T::Array[T.untyped]) }
53
+ sig { override.returns(T::Array[T.untyped]) }
52
54
  attr_reader :dependency_files
53
55
 
54
- sig { returns(T::Array[T.untyped]) }
56
+ sig { override.returns(T::Array[T.untyped]) }
55
57
  attr_reader :credentials
56
58
 
59
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
60
+ attr_reader :options
61
+
62
+ sig { override.returns(T.nilable(String)) }
63
+ attr_reader :repo_contents_path
64
+
57
65
  sig { returns(Dependabot::Package::PackageDetails) }
58
66
  def fetch
59
67
  case source_type
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/bundler/update_checker"
@@ -10,11 +10,33 @@ module Dependabot
10
10
  module Bundler
11
11
  class UpdateChecker < UpdateCheckers::Base
12
12
  class ConflictingDependencyResolver
13
+ extend T::Sig
14
+
13
15
  require_relative "shared_bundler_helpers"
16
+
14
17
  include SharedBundlerHelpers
15
18
 
19
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
16
20
  attr_reader :options
17
21
 
22
+ sig { override.returns(T::Array[Dependabot::Credential]) }
23
+ attr_reader :credentials
24
+
25
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
26
+ attr_reader :dependency_files
27
+
28
+ sig { override.returns(T.nilable(String)) }
29
+ attr_reader :repo_contents_path
30
+
31
+ sig do
32
+ params(
33
+ dependency_files: T::Array[Dependabot::DependencyFile],
34
+ repo_contents_path: T.nilable(String),
35
+ credentials: T::Array[Dependabot::Credential],
36
+ options: T::Hash[Symbol, T.untyped]
37
+ )
38
+ .void
39
+ end
18
40
  def initialize(dependency_files:, repo_contents_path:, credentials:, options:)
19
41
  @dependency_files = dependency_files
20
42
  @repo_contents_path = repo_contents_path
@@ -31,6 +53,13 @@ module Dependabot
31
53
  # * name [String] the blocking dependencies name
32
54
  # * version [String] the version of the blocking dependency
33
55
  # * requirement [String] the requirement on the target_dependency
56
+ sig do
57
+ params(
58
+ dependency: Dependabot::Dependency,
59
+ target_version: String
60
+ )
61
+ .returns(T::Array[T::Hash[String, String]])
62
+ end
34
63
  def conflicting_dependencies(dependency:, target_version:)
35
64
  return [] if lockfile.nil?
36
65
 
@@ -44,7 +73,7 @@ module Dependabot
44
73
  dependency_name: dependency.name,
45
74
  target_version: target_version,
46
75
  credentials: credentials,
47
- lockfile_name: lockfile.name
76
+ lockfile_name: T.must(lockfile).name
48
77
  }
49
78
  )
50
79
  end
@@ -52,8 +81,12 @@ module Dependabot
52
81
 
53
82
  private
54
83
 
84
+ sig { override.returns(String) }
55
85
  def bundler_version
56
- @bundler_version ||= Helpers.bundler_version(lockfile)
86
+ @bundler_version ||= T.let(
87
+ Helpers.bundler_version(lockfile),
88
+ T.nilable(String)
89
+ )
57
90
  end
58
91
  end
59
92
  end
@@ -1,6 +1,7 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
+ require "sorbet-runtime"
4
5
  require "dependabot/bundler/file_parser"
5
6
  require "dependabot/bundler/file_updater/lockfile_updater"
6
7
  require "dependabot/bundler/native_helpers"
@@ -14,14 +15,28 @@ module Dependabot
14
15
  module Bundler
15
16
  class UpdateChecker
16
17
  class ForceUpdater
18
+ extend T::Sig
19
+
17
20
  require_relative "shared_bundler_helpers"
21
+
18
22
  include SharedBundlerHelpers
19
23
 
20
- def initialize(dependency:, dependency_files:, repo_contents_path: nil,
21
- credentials:, target_version:,
22
- requirements_update_strategy:,
23
- update_multiple_dependencies: true,
24
- options:)
24
+ sig do
25
+ params(
26
+ dependency: Dependabot::Dependency,
27
+ dependency_files: T::Array[Dependabot::DependencyFile],
28
+ credentials: T::Array[Dependabot::Credential],
29
+ target_version: Dependabot::Version,
30
+ requirements_update_strategy: Dependabot::RequirementsUpdateStrategy,
31
+ options: T::Hash[Symbol, T.untyped],
32
+ repo_contents_path: T.nilable(String),
33
+ update_multiple_dependencies: T::Boolean
34
+ ).void
35
+ end
36
+ def initialize(dependency:, dependency_files:, credentials:, target_version:,
37
+ requirements_update_strategy:, options:,
38
+ repo_contents_path: nil,
39
+ update_multiple_dependencies: true)
25
40
  @dependency = dependency
26
41
  @dependency_files = dependency_files
27
42
  @repo_contents_path = repo_contents_path
@@ -30,28 +45,50 @@ module Dependabot
30
45
  @requirements_update_strategy = requirements_update_strategy
31
46
  @update_multiple_dependencies = update_multiple_dependencies
32
47
  @options = options
48
+
49
+ @updated_dependencies = T.let(nil, T.nilable(T::Array[Dependabot::Dependency]))
50
+ @original_dependencies = T.let(nil, T.nilable(T::Array[Dependabot::Dependency]))
51
+ @bundler_version = T.let(nil, T.nilable(String))
33
52
  end
34
53
 
54
+ sig { returns(T::Array[Dependabot::Dependency]) }
35
55
  def updated_dependencies
36
56
  @updated_dependencies ||= force_update
37
57
  end
38
58
 
39
- private
40
-
41
- attr_reader :dependency
59
+ # Abstract method implementations
60
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
42
61
  attr_reader :dependency_files
62
+
63
+ sig { override.returns(T.nilable(String)) }
43
64
  attr_reader :repo_contents_path
65
+
66
+ sig { override.returns(T::Array[Dependabot::Credential]) }
44
67
  attr_reader :credentials
68
+
69
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
70
+ attr_reader :options
71
+
72
+ private
73
+
74
+ sig { returns(Dependabot::Dependency) }
75
+ attr_reader :dependency
76
+
77
+ sig { returns(Dependabot::Version) }
45
78
  attr_reader :target_version
79
+
80
+ sig { returns(Dependabot::RequirementsUpdateStrategy) }
46
81
  attr_reader :requirements_update_strategy
47
- attr_reader :options
48
82
 
83
+ sig { returns(T::Boolean) }
49
84
  def update_multiple_dependencies?
50
85
  @update_multiple_dependencies
51
86
  end
52
87
 
88
+ # rubocop:disable Metrics/AbcSize
89
+ sig { returns(T::Array[Dependabot::Dependency]) }
53
90
  def force_update
54
- requirement = dependency.requirements.find { |req| req[:file] == gemfile.name }
91
+ requirement = dependency.requirements.find { |req| req[:file] == T.must(gemfile).name }
55
92
 
56
93
  valid_gem_version?(target_version)
57
94
 
@@ -71,8 +108,8 @@ module Dependabot
71
108
  dependency_name: dependency.name,
72
109
  target_version: target_version,
73
110
  credentials: credentials,
74
- gemfile_name: gemfile.name,
75
- lockfile_name: lockfile.name,
111
+ gemfile_name: T.must(gemfile).name,
112
+ lockfile_name: T.must(lockfile).name,
76
113
  update_multiple_dependencies: update_multiple_dependencies?
77
114
  }
78
115
  )
@@ -82,7 +119,9 @@ module Dependabot
82
119
  raise Dependabot::DependencyFileNotResolvable, msg
83
120
  end
84
121
  end
122
+ # rubocop:enable Metrics/AbcSize
85
123
 
124
+ sig { params(target_version: T.nilable(Dependabot::Version)).returns(TrueClass) }
86
125
  def valid_gem_version?(target_version)
87
126
  # to rule out empty, non gem info ending up in as target_version
88
127
  return true if target_version.is_a?(Gem::Version)
@@ -92,6 +131,7 @@ module Dependabot
92
131
  raise Dependabot::DependencyFileNotResolvable
93
132
  end
94
133
 
134
+ sig { returns(T::Array[Dependabot::Dependency]) }
95
135
  def original_dependencies
96
136
  @original_dependencies ||=
97
137
  FileParser.new(
@@ -101,6 +141,13 @@ module Dependabot
101
141
  ).parse
102
142
  end
103
143
 
144
+ sig do
145
+ params(
146
+ updated_deps: T::Array[T::Hash[String, T.untyped]],
147
+ specs: T::Array[T::Hash[String, T.untyped]]
148
+ )
149
+ .returns(T::Array[Dependabot::Dependency])
150
+ end
104
151
  def dependencies_from(updated_deps, specs)
105
152
  # You might think we'd want to remove dependencies whose version
106
153
  # hadn't changed from this array. We don't. We still need to unlock
@@ -114,12 +161,13 @@ module Dependabot
114
161
  original_dependencies.find { |d| d.name == dep.fetch("name") }
115
162
  spec = specs.find { |d| d.fetch("name") == dep.fetch("name") }
116
163
 
117
- next if spec.fetch("version") == original_dep.version
164
+ next if T.must(spec).fetch("version") == T.must(original_dep).version
118
165
 
119
166
  build_dependency(original_dep, spec)
120
167
  end
121
168
  end
122
169
 
170
+ sig { params(original_dep: T.untyped, updated_spec: T.untyped).returns(Dependabot::Dependency) }
123
171
  def build_dependency(original_dep, updated_spec)
124
172
  Dependency.new(
125
173
  name: updated_spec.fetch("name"),
@@ -138,27 +186,32 @@ module Dependabot
138
186
  )
139
187
  end
140
188
 
189
+ sig { params(dependency: Dependabot::Dependency).returns(T.nilable(T::Hash[String, T.untyped])) }
141
190
  def source_for(dependency)
142
191
  dependency.requirements
143
192
  .find { |r| r.fetch(:source) }
144
193
  &.fetch(:source)
145
194
  end
146
195
 
196
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
147
197
  def gemfile
148
198
  dependency_files.find { |f| f.name == "Gemfile" } ||
149
199
  dependency_files.find { |f| f.name == "gems.rb" }
150
200
  end
151
201
 
202
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
152
203
  def lockfile
153
204
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
154
205
  dependency_files.find { |f| f.name == "gems.locked" }
155
206
  end
156
207
 
208
+ sig { returns(String) }
157
209
  def sanitized_lockfile_body
158
210
  re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
159
- lockfile.content.gsub(re, "")
211
+ T.must(T.must(lockfile).content).gsub(re, "")
160
212
  end
161
213
 
214
+ sig { void }
162
215
  def write_temporary_dependency_files
163
216
  dependency_files.each do |file|
164
217
  path = file.name
@@ -166,9 +219,10 @@ module Dependabot
166
219
  File.write(path, file.content)
167
220
  end
168
221
 
169
- File.write(lockfile.name, sanitized_lockfile_body) if lockfile
222
+ File.write(T.must(lockfile).name, sanitized_lockfile_body) if lockfile
170
223
  end
171
224
 
225
+ sig { override.returns(String) }
172
226
  def bundler_version
173
227
  @bundler_version ||= Helpers.bundler_version(lockfile)
174
228
  end
@@ -122,12 +122,8 @@ module Dependabot
122
122
  @wants_prerelease ||= T.let(
123
123
  begin
124
124
  current_version = dependency.numeric_version
125
- if current_version&.prerelease?
126
- true
127
- else
128
- dependency.requirements.any? do |req|
129
- req[:requirement].match?(/[a-z]/i)
130
- end
125
+ current_version&.prerelease? || dependency.requirements.any? do |req|
126
+ req[:requirement].match?(/[a-z]/i)
131
127
  end
132
128
  end, T.nilable(T::Boolean)
133
129
  )
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
@@ -23,8 +23,17 @@ module Dependabot
23
23
 
24
24
  abstract!
25
25
 
26
- sig { returns(T::Hash[Symbol, T.untyped]) }
27
- attr_reader :options
26
+ sig { abstract.returns(T::Hash[Symbol, T.untyped]) }
27
+ def options; end
28
+
29
+ sig { abstract.returns(T::Array[Dependabot::DependencyFile]) }
30
+ def dependency_files; end
31
+
32
+ sig { abstract.returns(T.nilable(String)) }
33
+ def repo_contents_path; end
34
+
35
+ sig { abstract.returns(T::Array[Dependabot::Credential]) }
36
+ def credentials; end
28
37
 
29
38
  GIT_REGEX = /reset --hard [^\s]*` in directory (?<path>[^\s]*)/
30
39
  GIT_REF_REGEX = /not exist in the repository (?<path>[^\s]*)\./
@@ -51,21 +60,24 @@ module Dependabot
51
60
  Bundler::Fetcher::FallbackError
52
61
  ).freeze
53
62
 
54
- attr_reader :dependency_files
55
- attr_reader :repo_contents_path
56
- attr_reader :credentials
57
-
58
63
  #########################
59
64
  # Bundler context setup #
60
65
  #########################
61
66
 
62
- def in_a_native_bundler_context(error_handling: true)
67
+ sig do
68
+ params(
69
+ error_handling: T::Boolean,
70
+ _blk: T.proc.params(arg0: String).returns(T.untyped)
71
+ )
72
+ .returns(T.untyped)
73
+ end
74
+ def in_a_native_bundler_context(error_handling: true, &_blk)
63
75
  SharedHelpers
64
76
  .in_a_temporary_repo_directory(base_directory,
65
77
  repo_contents_path) do |tmp_dir|
66
78
  write_temporary_dependency_files
67
79
 
68
- yield(tmp_dir)
80
+ yield(tmp_dir.to_s)
69
81
  end
70
82
  rescue SharedHelpers::HelperSubprocessFailed => e
71
83
  retry_count ||= 0
@@ -75,10 +87,12 @@ module Dependabot
75
87
  error_handling ? handle_bundler_errors(e) : raise
76
88
  end
77
89
 
90
+ sig { returns(String) }
78
91
  def base_directory
79
- dependency_files.first.directory
92
+ T.must(dependency_files.first).directory
80
93
  end
81
94
 
95
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
82
96
  def retryable_error?(error)
83
97
  return true if error.error_class == "JSON::ParserError"
84
98
  return true if RETRYABLE_ERRORS.include?(error.error_class)
@@ -92,6 +106,7 @@ module Dependabot
92
106
  # rubocop:disable Metrics/PerceivedComplexity
93
107
  # rubocop:disable Metrics/AbcSize
94
108
  # rubocop:disable Metrics/MethodLength
109
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).void }
95
110
  def handle_bundler_errors(error)
96
111
  if error.error_class == "JSON::ParserError"
97
112
  msg = "Error evaluating your dependency files: #{error.message}"
@@ -105,25 +120,22 @@ module Dependabot
105
120
  # We couldn't evaluate the Gemfile, let alone resolve it
106
121
  raise Dependabot::DependencyFileNotEvaluatable, msg
107
122
  when "Bundler::Source::Git::MissingGitRevisionError"
108
- gem_name =
109
- error.message.match(GIT_REF_REGEX)
110
- .named_captures["path"]
111
- .split("/").last
112
- raise GitDependencyReferenceNotFound, gem_name
123
+ match_data = error.message.match(GIT_REF_REGEX)
124
+ gem_name = T.must(T.must(match_data).named_captures["path"])
125
+ .split("/").last
126
+ raise GitDependencyReferenceNotFound, T.must(gem_name)
113
127
  when "Bundler::PathError"
114
- gem_name =
115
- error.message.match(PATH_REGEX)
116
- .named_captures["path"]
117
- .split("/").last.split("-")[0..-2].join
128
+ match_data = error.message.match(PATH_REGEX)
129
+ path = T.must(T.must(match_data).named_captures["path"])
130
+ gem_name = T.must(T.must(path.split("/").last).split("-")[0..-2]).join
118
131
  raise Dependabot::PathDependenciesNotReachable, [gem_name]
119
132
  when "Bundler::Source::Git::GitCommandError"
120
133
  if error.message.match?(GIT_REGEX)
121
134
  # We couldn't find the specified branch / commit (or the two
122
135
  # weren't compatible).
123
- gem_name =
124
- error.message.match(GIT_REGEX)
125
- .named_captures["path"]
126
- .split("/").last.split("-")[0..-2].join
136
+ match_data = error.message.match(GIT_REGEX)
137
+ path = T.must(T.must(match_data).named_captures["path"])
138
+ gem_name = T.must(T.must(path.split("/").last).split("-")[0..-2]).join
127
139
  raise GitDependencyReferenceNotFound, gem_name
128
140
  end
129
141
 
@@ -144,24 +156,24 @@ module Dependabot
144
156
  raise Dependabot::DependencyFileNotResolvable, msg
145
157
  when "Bundler::Fetcher::AuthenticationRequiredError"
146
158
  regex = BundlerErrorPatterns::MISSING_AUTH_REGEX
147
- source = error.message.match(regex)[:source]
159
+ source = T.must(T.must(error.message.match(regex))[:source])
148
160
  raise Dependabot::PrivateSourceAuthenticationFailure, source
149
161
  when "Bundler::Fetcher::AuthenticationForbiddenError"
150
162
  regex = BundlerErrorPatterns::FORBIDDEN_AUTH_REGEX
151
- source = error.message.match(regex)[:source]
163
+ source = T.must(T.must(error.message.match(regex))[:source])
152
164
  raise Dependabot::PrivateSourceAuthenticationFailure, source
153
165
  when "Bundler::Fetcher::BadAuthenticationError"
154
166
  regex = BundlerErrorPatterns::BAD_AUTH_REGEX
155
- source = error.message.match(regex)[:source]
167
+ source = T.must(T.must(error.message.match(regex))[:source])
156
168
  raise Dependabot::PrivateSourceAuthenticationFailure, source
157
169
  when "Bundler::Fetcher::CertificateFailureError"
158
170
  regex = BundlerErrorPatterns::BAD_CERT_REGEX
159
- source = error.message.match(regex)[:source]
171
+ source = T.must(T.must(error.message.match(regex))[:source])
160
172
  raise Dependabot::PrivateSourceCertificateFailure, source
161
173
  when "Bundler::HTTPError"
162
174
  regex = BundlerErrorPatterns::HTTP_ERR_REGEX
163
175
  if error.message.match?(regex)
164
- source = error.message.match(regex)[:source]
176
+ source = T.must(T.must(error.message.match(regex))[:source])
165
177
  raise if [
166
178
  "rubygems.org",
167
179
  "www.rubygems.org"
@@ -184,6 +196,7 @@ module Dependabot
184
196
  # rubocop:enable Metrics/AbcSize
185
197
  # rubocop:enable Metrics/MethodLength
186
198
 
199
+ sig { returns(T::Array[T::Hash[String, T.untyped]]) }
187
200
  def inaccessible_git_dependencies
188
201
  in_a_native_bundler_context(error_handling: false) do |tmp_dir|
189
202
  git_specs = NativeHelpers.run_bundler_subprocess(
@@ -192,7 +205,7 @@ module Dependabot
192
205
  options: options,
193
206
  args: {
194
207
  dir: tmp_dir,
195
- gemfile_name: gemfile.name,
208
+ gemfile_name: T.must(gemfile).name,
196
209
  credentials: credentials
197
210
  }
198
211
  )
@@ -210,8 +223,10 @@ module Dependabot
210
223
  end
211
224
  end
212
225
 
226
+ sig { returns(T.nilable(String)) }
213
227
  def jfrog_source
214
- return @jfrog_source unless defined?(@jfrog_source)
228
+ @jfrog_source = T.let(@jfrog_source, T.nilable(String)) if @jfrog_source.nil?
229
+ return @jfrog_source unless @jfrog_source.nil?
215
230
 
216
231
  @jfrog_source = in_a_native_bundler_context(error_handling: false) do |dir|
217
232
  NativeHelpers.run_bundler_subprocess(
@@ -220,16 +235,14 @@ module Dependabot
220
235
  options: options,
221
236
  args: {
222
237
  dir: dir,
223
- gemfile_name: gemfile.name,
238
+ gemfile_name: T.must(gemfile).name,
224
239
  credentials: credentials
225
240
  }
226
241
  )
227
242
  end
228
243
  end
229
244
 
230
- sig { abstract.returns(String) }
231
- def bundler_version; end
232
-
245
+ sig { void }
233
246
  def write_temporary_dependency_files
234
247
  dependency_files.each do |file|
235
248
  path = file.name
@@ -237,23 +250,32 @@ module Dependabot
237
250
  File.write(path, file.content)
238
251
  end
239
252
 
240
- File.write(lockfile.name, lockfile.content) if lockfile
253
+ lockfile_obj = lockfile
254
+ File.write(lockfile_obj.name, lockfile_obj.content) if lockfile_obj
241
255
  end
242
256
 
257
+ sig { returns(T::Array[Dependabot::Credential]) }
243
258
  def private_registry_credentials
244
259
  credentials
245
260
  .select { |cred| cred["type"] == "rubygems_server" }
246
261
  end
247
262
 
263
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
248
264
  def gemfile
249
265
  dependency_files.find { |f| f.name == "Gemfile" } ||
250
266
  dependency_files.find { |f| f.name == "gems.rb" }
251
267
  end
252
268
 
269
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
253
270
  def lockfile
254
271
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
255
272
  dependency_files.find { |f| f.name == "gems.locked" }
256
273
  end
274
+
275
+ private
276
+
277
+ sig { abstract.returns(String) }
278
+ def bundler_version; end
257
279
  end
258
280
  end
259
281
  end
@@ -1,7 +1,8 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "excon"
5
+ require "sorbet-runtime"
5
6
 
6
7
  require "dependabot/bundler/helpers"
7
8
  require "dependabot/bundler/update_checker"
@@ -15,19 +16,36 @@ module Dependabot
15
16
  module Bundler
16
17
  class UpdateChecker
17
18
  class VersionResolver
19
+ extend T::Sig
20
+
18
21
  require_relative "file_preparer"
19
22
  require_relative "latest_version_finder"
20
23
  require_relative "shared_bundler_helpers"
21
24
  include SharedBundlerHelpers
22
25
 
23
- def initialize(dependency:, unprepared_dependency_files:,
24
- repo_contents_path: nil, credentials:, ignored_versions:,
26
+ sig do
27
+ params(
28
+ dependency: Dependabot::Dependency,
29
+ unprepared_dependency_files: T::Array[Dependabot::DependencyFile],
30
+ credentials: T::Array[Dependabot::Credential],
31
+ ignored_versions: T::Array[String],
32
+ options: T::Hash[Symbol, T.untyped],
33
+ repo_contents_path: T.nilable(String),
34
+ raise_on_ignored: T::Boolean,
35
+ replacement_git_pin: T.nilable(String),
36
+ remove_git_source: T::Boolean,
37
+ unlock_requirement: T::Boolean,
38
+ latest_allowable_version: T.nilable(T.any(String, Dependabot::Version)),
39
+ cooldown_options: T.nilable(Dependabot::Package::ReleaseCooldownOptions)
40
+ ).void
41
+ end
42
+ def initialize(dependency:, unprepared_dependency_files:, credentials:, ignored_versions:, options:,
43
+ repo_contents_path: nil,
25
44
  raise_on_ignored: false,
26
45
  replacement_git_pin: nil, remove_git_source: false,
27
46
  unlock_requirement: true,
28
47
  latest_allowable_version: nil,
29
- cooldown_options: nil,
30
- options:)
48
+ cooldown_options: nil)
31
49
  @dependency = dependency
32
50
  @unprepared_dependency_files = unprepared_dependency_files
33
51
  @credentials = credentials
@@ -41,50 +59,77 @@ module Dependabot
41
59
  @cooldown_options = cooldown_options
42
60
  @options = options
43
61
 
44
- @latest_allowable_version_incompatible_with_ruby = false
62
+ @latest_allowable_version_incompatible_with_ruby = T.let(false, T::Boolean)
63
+ @latest_resolvable_version_details = T.let(nil, T.nilable(T::Hash[Symbol, T.untyped]))
64
+ @dependency_files = T.let(nil, T.nilable(T::Array[Dependabot::DependencyFile]))
65
+ @latest_version_details = T.let(nil, T.nilable(T::Hash[Symbol, T.untyped]))
66
+ @gemspec_ruby_unlocked = T.let(false, T::Boolean)
67
+ @bundler_version = T.let(nil, T.nilable(String))
45
68
  end
46
69
 
70
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
47
71
  def latest_resolvable_version_details
48
72
  @latest_resolvable_version_details ||=
49
73
  fetch_latest_resolvable_version_details
50
74
  end
51
75
 
76
+ sig { returns(T::Boolean) }
52
77
  def latest_allowable_version_incompatible_with_ruby?
53
78
  @latest_allowable_version_incompatible_with_ruby
54
79
  end
55
80
 
81
+ # Abstract method implementations
82
+ sig { override.returns(T::Hash[Symbol, T.untyped]) }
83
+ attr_reader :options
84
+
85
+ sig { override.returns(T::Array[Dependabot::DependencyFile]) }
86
+ def dependency_files
87
+ @dependency_files ||=
88
+ FilePreparer.new(
89
+ dependency: dependency,
90
+ dependency_files: unprepared_dependency_files,
91
+ replacement_git_pin: replacement_git_pin,
92
+ remove_git_source: remove_git_source?,
93
+ unlock_requirement: unlock_requirement?,
94
+ latest_allowable_version: latest_allowable_version
95
+ ).prepared_dependency_files
96
+ end
97
+
98
+ sig { override.returns(T.nilable(String)) }
99
+ attr_reader :repo_contents_path
100
+
101
+ sig { override.returns(T::Array[Dependabot::Credential]) }
102
+ attr_reader :credentials
103
+
56
104
  private
57
105
 
106
+ sig { returns(Dependabot::Dependency) }
58
107
  attr_reader :dependency
108
+
109
+ sig { returns(T::Array[Dependabot::DependencyFile]) }
59
110
  attr_reader :unprepared_dependency_files
60
- attr_reader :repo_contents_path
61
- attr_reader :credentials
111
+
112
+ sig { returns(T::Array[String]) }
62
113
  attr_reader :ignored_versions
114
+
115
+ sig { returns(T.nilable(String)) }
63
116
  attr_reader :replacement_git_pin
117
+
118
+ sig { returns(T.nilable(T.any(String, Dependabot::Version))) }
64
119
  attr_reader :latest_allowable_version
65
- attr_reader :options
66
120
 
121
+ sig { returns(T::Boolean) }
67
122
  def remove_git_source?
68
123
  @remove_git_source
69
124
  end
70
125
 
126
+ sig { returns(T::Boolean) }
71
127
  def unlock_requirement?
72
128
  @unlock_requirement
73
129
  end
74
130
 
75
- def dependency_files
76
- @dependency_files ||=
77
- FilePreparer.new(
78
- dependency: dependency,
79
- dependency_files: unprepared_dependency_files,
80
- replacement_git_pin: replacement_git_pin,
81
- remove_git_source: remove_git_source?,
82
- unlock_requirement: unlock_requirement?,
83
- latest_allowable_version: latest_allowable_version
84
- ).prepared_dependency_files
85
- end
86
-
87
131
  # rubocop:disable Metrics/PerceivedComplexity
132
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
88
133
  def fetch_latest_resolvable_version_details
89
134
  return latest_version_details unless gemfile
90
135
 
@@ -100,7 +145,7 @@ module Dependabot
100
145
  args: {
101
146
  dependency_name: dependency.name,
102
147
  dependency_requirements: dependency.requirements,
103
- gemfile_name: gemfile.name,
148
+ gemfile_name: T.must(gemfile).name,
104
149
  lockfile_name: lockfile&.name,
105
150
  dir: tmp_dir,
106
151
  credentials: credentials
@@ -136,12 +181,14 @@ module Dependabot
136
181
  end
137
182
  # rubocop:enable Metrics/PerceivedComplexity
138
183
 
184
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
139
185
  def circular_dependency_at_new_version?(error)
140
186
  return false unless error.error_class.include?("CyclicDependencyError")
141
187
 
142
188
  error.message.include?("'#{dependency.name}'")
143
189
  end
144
190
 
191
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
145
192
  def error_due_to_restrictive_upper_bound?(error)
146
193
  # We see this when the dependency doesn't appear in the lockfile and
147
194
  # has an overly restrictive upper bound that we've added, either due
@@ -152,6 +199,7 @@ module Dependabot
152
199
  error.message.include?("#{dependency.name} ")
153
200
  end
154
201
 
202
+ sig { params(error: T.untyped).returns(T::Boolean) }
155
203
  def ruby_lock_error?(error)
156
204
  return false unless conflict_on_ruby?(error)
157
205
  return false if @gemspec_ruby_unlocked
@@ -159,6 +207,7 @@ module Dependabot
159
207
  dependency_files.any? { |f| f.name.end_with?(".gemspec") }
160
208
  end
161
209
 
210
+ sig { params(error: Dependabot::SharedHelpers::HelperSubprocessFailed).returns(T::Boolean) }
162
211
  def conflict_on_ruby?(error)
163
212
  if bundler_version == "1"
164
213
  error.message.include?(" for gem \"ruby\0\"")
@@ -167,6 +216,7 @@ module Dependabot
167
216
  end
168
217
  end
169
218
 
219
+ sig { returns(T::Boolean) }
170
220
  def regenerate_dependency_files_without_ruby_lock
171
221
  @dependency_files =
172
222
  FilePreparer.new(
@@ -178,8 +228,10 @@ module Dependabot
178
228
  latest_allowable_version: latest_allowable_version,
179
229
  lock_ruby_version: false
180
230
  ).prepared_dependency_files
231
+ true
181
232
  end
182
233
 
234
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
183
235
  def latest_version_details
184
236
  @latest_version_details ||=
185
237
  LatestVersionFinder.new(
@@ -194,6 +246,7 @@ module Dependabot
194
246
  ).latest_version_details
195
247
  end
196
248
 
249
+ sig { params(details: T.untyped).returns(T::Boolean) }
197
250
  def ruby_version_incompatible?(details)
198
251
  # It's only the old index we have a problem with
199
252
  return false unless details[:fetcher] == "Bundler::Fetcher::Dependency"
@@ -231,16 +284,19 @@ module Dependabot
231
284
  false
232
285
  end
233
286
 
287
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
234
288
  def gemfile
235
289
  dependency_files.find { |f| f.name == "Gemfile" } ||
236
290
  dependency_files.find { |f| f.name == "gems.rb" }
237
291
  end
238
292
 
293
+ sig { returns(T.nilable(Dependabot::DependencyFile)) }
239
294
  def lockfile
240
295
  dependency_files.find { |f| f.name == "Gemfile.lock" } ||
241
296
  dependency_files.find { |f| f.name == "gems.locked" }
242
297
  end
243
298
 
299
+ sig { override.returns(String) }
244
300
  def bundler_version
245
301
  @bundler_version ||= Helpers.bundler_version(lockfile)
246
302
  end
@@ -1,4 +1,4 @@
1
- # typed: true
1
+ # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "dependabot/bundler/file_updater/requirement_replacer"
@@ -17,40 +17,53 @@ module Dependabot
17
17
  require_relative "update_checker/version_resolver"
18
18
  require_relative "update_checker/latest_version_finder"
19
19
  require_relative "update_checker/conflicting_dependency_resolver"
20
+ extend T::Sig
20
21
 
22
+ sig { override.returns(T.nilable(T.any(String, Dependabot::Bundler::Version))) }
21
23
  def latest_version
22
24
  return latest_version_for_git_dependency if git_dependency?
23
25
 
24
26
  latest_version_details&.fetch(:version)
25
27
  end
26
28
 
29
+ sig { override.returns(T.nilable(T.any(String, Dependabot::Bundler::Version))) }
27
30
  def latest_resolvable_version
28
31
  return latest_resolvable_version_for_git_dependency if git_dependency?
29
32
 
30
33
  latest_resolvable_version_details&.fetch(:version)
31
34
  end
32
35
 
36
+ sig { override.returns(T.nilable(Dependabot::Bundler::Version)) }
33
37
  def lowest_security_fix_version
34
- latest_version_finder(remove_git_source: false)
35
- .lowest_security_fix_version
38
+ T.cast(
39
+ latest_version_finder(remove_git_source: false).lowest_security_fix_version,
40
+ T.nilable(Dependabot::Bundler::Version)
41
+ )
36
42
  end
37
43
 
44
+ sig { override.returns(T.nilable(Dependabot::Bundler::Version)) }
38
45
  def lowest_resolvable_security_fix_version
39
46
  raise "Dependency not vulnerable!" unless vulnerable?
40
- return latest_resolvable_version if git_dependency?
47
+ return T.cast(latest_resolvable_version, T.nilable(Dependabot::Bundler::Version)) if git_dependency?
41
48
 
42
49
  lowest_fix =
43
50
  latest_version_finder(remove_git_source: false)
44
51
  .lowest_security_fix_version
45
- return unless lowest_fix && resolvable?(lowest_fix)
52
+ return unless lowest_fix && resolvable?(T.cast(lowest_fix, Dependabot::Bundler::Version))
46
53
 
47
- lowest_fix
54
+ T.cast(lowest_fix, Dependabot::Bundler::Version)
48
55
  end
49
56
 
57
+ sig { override.returns(T.nilable(T.any(String, Dependabot::Bundler::Version))) }
50
58
  def latest_resolvable_version_with_no_unlock
51
59
  current_ver = dependency.version
52
60
  return current_ver if git_dependency? && git_commit_checker.pinned?
53
61
 
62
+ @latest_resolvable_version_detail_with_no_unlock = T.let(
63
+ @latest_resolvable_version_detail_with_no_unlock,
64
+ T.nilable(T::Hash[Symbol, T.untyped])
65
+ )
66
+
54
67
  @latest_resolvable_version_detail_with_no_unlock ||=
55
68
  version_resolver(remove_git_source: false, unlock_requirement: false)
56
69
  .latest_resolvable_version_details
@@ -62,6 +75,7 @@ module Dependabot
62
75
  end
63
76
  end
64
77
 
78
+ sig { override.returns(T::Array[T::Hash[Symbol, T.untyped]]) }
65
79
  def updated_requirements
66
80
  latest_version_for_req_updater = latest_version_details&.fetch(:version)&.to_s
67
81
  latest_resolvable_version_for_req_updater = preferred_resolvable_version_details&.fetch(:version)&.to_s
@@ -75,9 +89,10 @@ module Dependabot
75
89
  ).updated_requirements
76
90
  end
77
91
 
92
+ sig { returns(T::Boolean) }
78
93
  def requirements_unlocked_or_can_be?
79
94
  return true if requirements_unlocked?
80
- return false if requirements_update_strategy.lockfile_only?
95
+ return false if T.must(requirements_update_strategy).lockfile_only?
81
96
 
82
97
  dependency.specific_requirements
83
98
  .all? do |req|
@@ -92,6 +107,7 @@ module Dependabot
92
107
  end
93
108
  end
94
109
 
110
+ sig { returns(T.nilable(Dependabot::RequirementsUpdateStrategy)) }
95
111
  def requirements_update_strategy
96
112
  # If passed in as an option (in the base class) honour that option
97
113
  return @requirements_update_strategy if @requirements_update_strategy
@@ -104,6 +120,7 @@ module Dependabot
104
120
  end
105
121
  end
106
122
 
123
+ sig { override.returns(T::Array[T::Hash[String, String]]) }
107
124
  def conflicting_dependencies
108
125
  ConflictingDependencyResolver.new(
109
126
  dependency_files: dependency_files,
@@ -112,16 +129,18 @@ module Dependabot
112
129
  options: options
113
130
  ).conflicting_dependencies(
114
131
  dependency: dependency,
115
- target_version: lowest_security_fix_version
132
+ target_version: lowest_security_fix_version.to_s # Convert Version to String
116
133
  )
117
134
  end
118
135
 
119
136
  private
120
137
 
138
+ sig { returns(T::Boolean) }
121
139
  def requirements_unlocked?
122
140
  dependency.specific_requirements.none?
123
141
  end
124
142
 
143
+ sig { override.returns(T::Boolean) }
125
144
  def latest_version_resolvable_with_full_unlock?
126
145
  return false unless latest_version
127
146
  return false if version_resolver(remove_git_source: false).latest_allowable_version_incompatible_with_ruby?
@@ -139,22 +158,26 @@ module Dependabot
139
158
  false
140
159
  end
141
160
 
161
+ sig { override.returns(T::Array[Dependabot::Dependency]) }
142
162
  def updated_dependencies_after_full_unlock
143
163
  force_updater.updated_dependencies
144
164
  end
145
165
 
166
+ sig { returns(T.nilable(T::Hash[Symbol, T.untyped])) }
146
167
  def preferred_resolvable_version_details
147
168
  return { version: lowest_resolvable_security_fix_version } if vulnerable?
148
169
 
149
170
  latest_resolvable_version_details
150
171
  end
151
172
 
173
+ sig { returns(T::Boolean) }
152
174
  def git_dependency?
153
175
  git_commit_checker.git_dependency?
154
176
  end
155
177
 
178
+ sig { params(version: Dependabot::Bundler::Version).returns(T.untyped) }
156
179
  def resolvable?(version)
157
- @resolvable ||= {}
180
+ @resolvable ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
158
181
  return @resolvable[version] if @resolvable.key?(version)
159
182
 
160
183
  @resolvable[version] =
@@ -165,7 +188,7 @@ module Dependabot
165
188
  repo_contents_path: repo_contents_path,
166
189
  credentials: credentials,
167
190
  target_version: version,
168
- requirements_update_strategy: requirements_update_strategy,
191
+ requirements_update_strategy: T.must(requirements_update_strategy),
169
192
  update_multiple_dependencies: false,
170
193
  options: options
171
194
  ).updated_dependencies
@@ -175,8 +198,9 @@ module Dependabot
175
198
  end
176
199
  end
177
200
 
201
+ sig { params(tag: T.nilable(String)).returns(T.untyped) }
178
202
  def git_tag_resolvable?(tag)
179
- @git_tag_resolvable ||= {}
203
+ @git_tag_resolvable ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
180
204
  return @git_tag_resolvable[tag] if @git_tag_resolvable.key?(tag)
181
205
 
182
206
  @git_tag_resolvable[tag] =
@@ -198,20 +222,23 @@ module Dependabot
198
222
  end
199
223
  end
200
224
 
225
+ sig { params(remove_git_source: T::Boolean).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
201
226
  def latest_version_details(remove_git_source: false)
202
- @latest_version_details ||= {}
227
+ @latest_version_details ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
203
228
  @latest_version_details[remove_git_source] ||=
204
229
  latest_version_finder(remove_git_source: remove_git_source)
205
230
  .latest_version_details
206
231
  end
207
232
 
233
+ sig { params(remove_git_source: T::Boolean).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
208
234
  def latest_resolvable_version_details(remove_git_source: false)
209
- @latest_resolvable_version_details ||= {}
235
+ @latest_resolvable_version_details ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
210
236
  @latest_resolvable_version_details[remove_git_source] ||=
211
237
  version_resolver(remove_git_source: remove_git_source)
212
238
  .latest_resolvable_version_details
213
239
  end
214
240
 
241
+ sig { returns(T.nilable(T.any(String, Dependabot::Bundler::Version))) }
215
242
  def latest_version_for_git_dependency
216
243
  latest_release =
217
244
  latest_version_details(remove_git_source: true)
@@ -238,6 +265,7 @@ module Dependabot
238
265
  dependency.version
239
266
  end
240
267
 
268
+ sig { returns(T.any(String, T.nilable(Dependabot::Bundler::Version))) }
241
269
  def latest_resolvable_version_for_git_dependency
242
270
  latest_release = latest_resolvable_version_without_git_source
243
271
 
@@ -255,7 +283,7 @@ module Dependabot
255
283
  if git_commit_checker.pinned_ref_looks_like_version? &&
256
284
  latest_git_tag_is_resolvable?
257
285
  new_tag = git_commit_checker.local_tag_for_latest_version
258
- return new_tag.fetch(:tag_sha)
286
+ return new_tag&.fetch(:tag_sha)
259
287
  end
260
288
 
261
289
  # If the dependency is pinned to a tag that doesn't look like a
@@ -263,6 +291,7 @@ module Dependabot
263
291
  dependency.version
264
292
  end
265
293
 
294
+ sig { returns(T.any(String, T.nilable(Dependabot::Bundler::Version))) }
266
295
  def latest_resolvable_version_without_git_source
267
296
  return nil unless latest_version.is_a?(Gem::Version)
268
297
 
@@ -272,6 +301,7 @@ module Dependabot
272
301
  nil
273
302
  end
274
303
 
304
+ sig { returns(T.any(String, T.nilable(Dependabot::Bundler::Version))) }
275
305
  def latest_resolvable_commit_with_unchanged_git_source
276
306
  details = latest_resolvable_version_details(remove_git_source: false)
277
307
 
@@ -285,6 +315,7 @@ module Dependabot
285
315
  nil
286
316
  end
287
317
 
318
+ sig { returns(T::Boolean) }
288
319
  def latest_git_tag_is_resolvable?
289
320
  latest_tag_details = git_commit_checker.local_tag_for_latest_version
290
321
  return false unless latest_tag_details
@@ -292,12 +323,14 @@ module Dependabot
292
323
  git_tag_resolvable?(latest_tag_details.fetch(:tag))
293
324
  end
294
325
 
326
+ sig { params(release: T.untyped).returns(T::Boolean) }
295
327
  def git_branch_or_ref_in_release?(release)
296
328
  return false unless release
297
329
 
298
330
  git_commit_checker.branch_or_ref_in_release?(release)
299
331
  end
300
332
 
333
+ sig { returns(T.nilable(T::Hash[T.untyped, T.untyped])) }
301
334
  def updated_source
302
335
  # Never need to update source, unless a git_dependency
303
336
  return dependency_source_details unless git_dependency?
@@ -306,31 +339,42 @@ module Dependabot
306
339
  if git_commit_checker.pinned_ref_looks_like_version? &&
307
340
  latest_git_tag_is_resolvable?
308
341
  new_tag = git_commit_checker.local_tag_for_latest_version
309
- return dependency_source_details.merge(ref: new_tag.fetch(:tag))
342
+ return T.must(dependency_source_details).merge(ref: T.must(new_tag).fetch(:tag))
310
343
  end
311
344
 
312
345
  # Otherwise return the original source
313
346
  dependency_source_details
314
347
  end
315
348
 
349
+ sig { returns(T.nilable(T::Hash[T.any(String, Symbol), T.untyped])) }
316
350
  def dependency_source_details
317
351
  dependency.source_details
318
352
  end
319
353
 
354
+ sig { returns(Dependabot::Bundler::UpdateChecker::ForceUpdater) }
320
355
  def force_updater
356
+ if @force_updater.nil?
357
+ @force_updater = T.let(@force_updater,
358
+ T.nilable(Dependabot::Bundler::UpdateChecker::ForceUpdater))
359
+ end
321
360
  @force_updater ||=
322
361
  ForceUpdater.new(
323
362
  dependency: dependency,
324
363
  dependency_files: dependency_files,
325
364
  repo_contents_path: repo_contents_path,
326
365
  credentials: credentials,
327
- target_version: latest_version,
328
- requirements_update_strategy: requirements_update_strategy,
366
+ target_version: T.cast(latest_version, Dependabot::Version),
367
+ requirements_update_strategy: T.must(requirements_update_strategy),
329
368
  options: options
330
369
  )
331
370
  end
332
371
 
372
+ sig { returns(Dependabot::GitCommitChecker) }
333
373
  def git_commit_checker
374
+ if @git_commit_checker.nil?
375
+ @git_commit_checker = T.let(@git_commit_checker,
376
+ T.nilable(Dependabot::GitCommitChecker))
377
+ end
334
378
  @git_commit_checker ||=
335
379
  GitCommitChecker.new(
336
380
  dependency: dependency,
@@ -338,8 +382,9 @@ module Dependabot
338
382
  )
339
383
  end
340
384
 
385
+ sig { params(remove_git_source: T::Boolean, unlock_requirement: T::Boolean).returns(T.untyped) }
341
386
  def version_resolver(remove_git_source:, unlock_requirement: true)
342
- @version_resolver ||= {}
387
+ @version_resolver ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
343
388
  @version_resolver[remove_git_source] ||= {}
344
389
  @version_resolver[remove_git_source][unlock_requirement] ||=
345
390
  VersionResolver.new(
@@ -357,8 +402,9 @@ module Dependabot
357
402
  )
358
403
  end
359
404
 
405
+ sig { params(remove_git_source: T::Boolean).returns(Dependabot::Bundler::UpdateChecker::LatestVersionFinder) }
360
406
  def latest_version_finder(remove_git_source:)
361
- @latest_version_finder ||= {}
407
+ @latest_version_finder ||= T.let({}, T.nilable(T::Hash[T.untyped, T.untyped]))
362
408
  @latest_version_finder[remove_git_source] ||=
363
409
  begin
364
410
  prepared_dependency_files = prepared_dependency_files(
@@ -379,6 +425,13 @@ module Dependabot
379
425
  end
380
426
  end
381
427
 
428
+ sig do
429
+ params(
430
+ remove_git_source: T::Boolean,
431
+ unlock_requirement: T::Boolean,
432
+ latest_allowable_version: T.nilable(T.any(String, Dependabot::Bundler::Version))
433
+ ).returns(T::Array[Dependabot::DependencyFile])
434
+ end
382
435
  def prepared_dependency_files(remove_git_source:, unlock_requirement:,
383
436
  latest_allowable_version: nil)
384
437
  FilePreparer.new(
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.331.0
4
+ version: 0.333.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.331.0
18
+ version: 0.333.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.331.0
25
+ version: 0.333.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: parallel
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -225,14 +225,14 @@ dependencies:
225
225
  requirements:
226
226
  - - "~>"
227
227
  - !ruby/object:Gem::Version
228
- version: '3.18'
228
+ version: '3.25'
229
229
  type: :development
230
230
  prerelease: false
231
231
  version_requirements: !ruby/object:Gem::Requirement
232
232
  requirements:
233
233
  - - "~>"
234
234
  - !ruby/object:Gem::Version
235
- version: '3.18'
235
+ version: '3.25'
236
236
  - !ruby/object:Gem::Dependency
237
237
  name: webrick
238
238
  requirement: !ruby/object:Gem::Requirement
@@ -322,7 +322,7 @@ licenses:
322
322
  - MIT
323
323
  metadata:
324
324
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
325
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.331.0
325
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.333.0
326
326
  rdoc_options: []
327
327
  require_paths:
328
328
  - lib