dependabot-bundler 0.280.0 → 0.281.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/bundler/helpers.rb +1 -13
  3. data/lib/dependabot/bundler/package_manager.rb +6 -6
  4. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +1 -2
  5. metadata +8 -33
  6. data/helpers/v1/.gitignore +0 -8
  7. data/helpers/v1/Gemfile +0 -7
  8. data/helpers/v1/build +0 -29
  9. data/helpers/v1/lib/functions/conflicting_dependency_resolver.rb +0 -89
  10. data/helpers/v1/lib/functions/dependency_source.rb +0 -90
  11. data/helpers/v1/lib/functions/file_parser.rb +0 -119
  12. data/helpers/v1/lib/functions/force_updater.rb +0 -173
  13. data/helpers/v1/lib/functions/lockfile_updater.rb +0 -218
  14. data/helpers/v1/lib/functions/version_resolver.rb +0 -141
  15. data/helpers/v1/lib/functions.rb +0 -172
  16. data/helpers/v1/monkey_patches/definition_bundler_version_patch.rb +0 -16
  17. data/helpers/v1/monkey_patches/definition_ruby_version_patch.rb +0 -22
  18. data/helpers/v1/monkey_patches/fileutils_keyword_splat_patch.rb +0 -20
  19. data/helpers/v1/monkey_patches/git_source_patch.rb +0 -62
  20. data/helpers/v1/monkey_patches/object_untaint_patch.rb +0 -17
  21. data/helpers/v1/monkey_patches/resolver_spec_group_sane_eql.rb +0 -18
  22. data/helpers/v1/patched_bundler +0 -34
  23. data/helpers/v1/run.rb +0 -38
  24. data/helpers/v1/spec/functions/conflicting_dependency_resolver_spec.rb +0 -118
  25. data/helpers/v1/spec/functions/dependency_source_spec.rb +0 -188
  26. data/helpers/v1/spec/functions/file_parser_spec.rb +0 -75
  27. data/helpers/v1/spec/functions/force_updater_spec.rb +0 -59
  28. data/helpers/v1/spec/functions/version_resolver_spec.rb +0 -105
  29. data/helpers/v1/spec/native_spec_helper.rb +0 -56
  30. data/helpers/v1/spec/shared_contexts.rb +0 -60
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ccd9c2016c9305a9e201302c1788179179dca0592aee95d03adef3ec1155f79a
4
- data.tar.gz: 857b17356df06747aa2a6b9780ecc1b9e38571ead11d03ff04c117449fb6b74f
3
+ metadata.gz: ced4096906c6340adbf69f1a72b3c84069622b1bf0c1e249a02419baae29b0c2
4
+ data.tar.gz: dc9d382839349a530ff0677c03791ebfa1b8e1013a72b93d9bb61677dc3a914d
5
5
  SHA512:
6
- metadata.gz: 94aa6c6289637df9cf3bf10fe68bcbd7cf7a205b731805b197ecaad1d44e69f725eedd203f9a858deea6679053240d0fc890ee8f6ec5ac6ad737218e830566b1
7
- data.tar.gz: 6be8f4cb1fba1a593b2fa590f7a7d995015062e1fa485747c3dbfacde84fc37b08947e68d9b0e7a38c7dd72cc6a2a69fa7df92505e9748a2cf5223d7f5b829dd
6
+ metadata.gz: b1e2d121706fcc5fcbc097677b64c3800741ff124ad92f0c6c49e0a5c233723a3a06a7302128072b128f69aea7618235c4ea67657b4d0e33a89654a7bc223477
7
+ data.tar.gz: 46e5908f2d55e1a315ba940fc1da53209354474c83c2b5e93f8dfb15452b83410aeaf8482c75ff9fcf955b93a562ca1bbc2d92ba66a0b104c462678d3edbf5d8
@@ -20,23 +20,11 @@ module Dependabot
20
20
 
21
21
  if (matches = lockfile.content&.match(BUNDLER_MAJOR_VERSION_REGEX))
22
22
  matches[:version].to_i >= 2 ? V2 : V1
23
- elsif Dependabot::Experiments.enabled?(:bundler_v1_unsupported_error)
24
- DEFAULT
25
23
  else
26
- failover_version
24
+ DEFAULT
27
25
  end
28
26
  end
29
27
 
30
- # If we are updating a project with a Gemfile.lock that does not specify
31
- # the version it was bundled with, we failover to V1 on the assumption
32
- # it was created with an old version that didn't add this information
33
- sig { returns(String) }
34
- def self.failover_version
35
- return V2 if Dependabot::Experiments.enabled?(:bundler_v1_unsupported_error)
36
-
37
- V1
38
- end
39
-
40
28
  sig { params(lockfile: T.nilable(Dependabot::DependencyFile)).returns(String) }
41
29
  def self.detected_bundler_version(lockfile)
42
30
  return "unknown" unless lockfile
@@ -12,9 +12,11 @@ module Dependabot
12
12
  # Keep versions in ascending order
13
13
  SUPPORTED_BUNDLER_VERSIONS = T.let([Version.new("2")].freeze, T::Array[Dependabot::Version])
14
14
 
15
- DEPRECATED_BUNDLER_VERSIONS = T.let([
16
- Version.new("1")
17
- ].freeze, T::Array[Dependabot::Version])
15
+ # Currently, we don't support any deprecated versions of Bundler
16
+ # When a version is going to be unsupported, it will be added here for a while to give users time to upgrade
17
+ # Example for deprecation:
18
+ # DEPRECATED_BUNDLER_VERSIONS = T.let([Version.new("1")].freeze, T::Array[Dependabot::Version])
19
+ DEPRECATED_BUNDLER_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
18
20
 
19
21
  class PackageManager < PackageManagerBase
20
22
  extend T::Sig
@@ -41,9 +43,7 @@ module Dependabot
41
43
 
42
44
  sig { override.returns(T::Boolean) }
43
45
  def unsupported?
44
- # Check if the feature flag for Bundler v1 unsupported error is enabled.
45
- return false unless Dependabot::Experiments.enabled?(:bundler_v1_unsupported_error)
46
-
46
+ # Check if the version is not supported
47
47
  supported_versions.all? { |supported| supported > version }
48
48
  end
49
49
  end
@@ -31,8 +31,7 @@ module Dependabot
31
31
  PATH_REGEX = /The path `(?<path>.*)` does not exist/
32
32
 
33
33
  module BundlerErrorPatterns
34
- # The `set --global` optional part can be made required when Bundler 1 support is dropped
35
- MISSING_AUTH_REGEX = /bundle config (?:set --global )?(?<source>.*) username:password/
34
+ MISSING_AUTH_REGEX = /bundle config set --global (?<source>.*) username:password/
36
35
 
37
36
  BAD_AUTH_REGEX = /Bad username or password for (?<source>.*)\.$/
38
37
  FORBIDDEN_AUTH_REGEX = /Access token could not be authenticated for (?<source>.*)\.$/
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.280.0
4
+ version: 0.281.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-10 00:00:00.000000000 Z
11
+ date: 2024-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.280.0
19
+ version: 0.281.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.280.0
26
+ version: 0.281.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: parallel
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -257,31 +257,6 @@ extensions: []
257
257
  extra_rdoc_files: []
258
258
  files:
259
259
  - helpers/spec_helpers/gem_net_http_adapter.rb
260
- - helpers/v1/.gitignore
261
- - helpers/v1/Gemfile
262
- - helpers/v1/build
263
- - helpers/v1/lib/functions.rb
264
- - helpers/v1/lib/functions/conflicting_dependency_resolver.rb
265
- - helpers/v1/lib/functions/dependency_source.rb
266
- - helpers/v1/lib/functions/file_parser.rb
267
- - helpers/v1/lib/functions/force_updater.rb
268
- - helpers/v1/lib/functions/lockfile_updater.rb
269
- - helpers/v1/lib/functions/version_resolver.rb
270
- - helpers/v1/monkey_patches/definition_bundler_version_patch.rb
271
- - helpers/v1/monkey_patches/definition_ruby_version_patch.rb
272
- - helpers/v1/monkey_patches/fileutils_keyword_splat_patch.rb
273
- - helpers/v1/monkey_patches/git_source_patch.rb
274
- - helpers/v1/monkey_patches/object_untaint_patch.rb
275
- - helpers/v1/monkey_patches/resolver_spec_group_sane_eql.rb
276
- - helpers/v1/patched_bundler
277
- - helpers/v1/run.rb
278
- - helpers/v1/spec/functions/conflicting_dependency_resolver_spec.rb
279
- - helpers/v1/spec/functions/dependency_source_spec.rb
280
- - helpers/v1/spec/functions/file_parser_spec.rb
281
- - helpers/v1/spec/functions/force_updater_spec.rb
282
- - helpers/v1/spec/functions/version_resolver_spec.rb
283
- - helpers/v1/spec/native_spec_helper.rb
284
- - helpers/v1/spec/shared_contexts.rb
285
260
  - helpers/v2/.gitignore
286
261
  - helpers/v2/Gemfile
287
262
  - helpers/v2/build
@@ -346,8 +321,8 @@ licenses:
346
321
  - MIT
347
322
  metadata:
348
323
  bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
349
- changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.280.0
350
- post_install_message:
324
+ changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.281.0
325
+ post_install_message:
351
326
  rdoc_options: []
352
327
  require_paths:
353
328
  - lib
@@ -363,7 +338,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
363
338
  version: 3.1.0
364
339
  requirements: []
365
340
  rubygems_version: 3.5.9
366
- signing_key:
341
+ signing_key:
367
342
  specification_version: 4
368
343
  summary: Provides Dependabot support for Ruby (bundler)
369
344
  test_files: []
@@ -1,8 +0,0 @@
1
- /.bundle
2
- /.env
3
- /tmp
4
- /dependabot-*.gem
5
- Gemfile.lock
6
- spec/fixtures/projects/*/.bundle/
7
- !spec/fixtures/projects/**/Gemfile.lock
8
- !spec/fixtures/projects/**/vendor
data/helpers/v1/Gemfile DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "dependabot-common", path: "../../../common"
6
-
7
- gemspec path: "../.."
data/helpers/v1/build DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -e
4
-
5
- helpers_dir=$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)
6
-
7
- if [ -z "$DEPENDABOT_NATIVE_HELPERS_PATH" ]; then
8
- install_dir="$helpers_dir"
9
- else
10
- install_dir="$DEPENDABOT_NATIVE_HELPERS_PATH/bundler/v1"
11
- mkdir -p "$install_dir"
12
-
13
- cp -r \
14
- "$helpers_dir/lib" \
15
- "$helpers_dir/monkey_patches" \
16
- "$helpers_dir/run.rb" \
17
- "$helpers_dir/patched_bundler" \
18
- "$install_dir"
19
- fi
20
-
21
- cd "$install_dir"
22
-
23
- export GEM_HOME=$install_dir/.bundle
24
-
25
- gem install bundler -v 1.17.3 --no-document
26
-
27
- if [ -z "$DEPENDABOT_NATIVE_HELPERS_PATH" ]; then
28
- BUNDLER_VERSION=1.17.3 ./patched_bundler install
29
- fi
@@ -1,89 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- module Functions
5
- class ConflictingDependencyResolver
6
- def initialize(dependency_name:, target_version:, lockfile_name:)
7
- @dependency_name = dependency_name
8
- @target_version = target_version
9
- @lockfile_name = lockfile_name
10
- end
11
-
12
- # Finds any dependencies in the lockfile that have a subdependency on the
13
- # given dependency that does not satisfly the target_version.
14
- # @return [Array<Hash{String => String}]
15
- # * explanation [String] a sentence explaining the conflict
16
- # * name [String] the blocking dependencies name
17
- # * version [String] the version of the blocking dependency
18
- # * requirement [String] the requirement on the target_dependency
19
- def conflicting_dependencies
20
- Bundler.settings.set_command_option("only_update_to_newer_versions", true)
21
-
22
- parent_specs.flat_map do |parent_spec|
23
- top_level_specs_for(parent_spec).map do |top_level|
24
- dependency = parent_spec.dependencies.find { |bd| bd.name == dependency_name }
25
- {
26
- "explanation" => explanation(parent_spec, dependency, top_level),
27
- "name" => parent_spec.name,
28
- "version" => parent_spec.version.to_s,
29
- "requirement" => dependency.requirement.to_s
30
- }
31
- end
32
- end
33
- end
34
-
35
- private
36
-
37
- attr_reader :dependency_name
38
- attr_reader :target_version
39
- attr_reader :lockfile_name
40
-
41
- def parent_specs
42
- version = Gem::Version.new(target_version)
43
- parsed_lockfile.specs.filter do |spec|
44
- spec.dependencies.any? do |dep|
45
- dep.name == dependency_name &&
46
- !dep.requirement.satisfied_by?(version)
47
- end
48
- end
49
- end
50
-
51
- def top_level_specs_for(parent_spec)
52
- return [parent_spec] if top_level?(parent_spec)
53
-
54
- parsed_lockfile.specs.filter do |spec|
55
- spec.dependencies.any? do |dep|
56
- dep.name == parent_spec.name && top_level?(spec)
57
- end
58
- end
59
- end
60
-
61
- def top_level?(spec)
62
- parsed_lockfile.dependencies.key?(spec.name)
63
- end
64
-
65
- def explanation(spec, dependency, top_level)
66
- if spec.name == top_level.name
67
- "#{spec.name} (#{spec.version}) requires #{dependency_name} (#{dependency.requirement})"
68
- else
69
- "#{top_level.name} (#{top_level.version}) requires #{dependency_name} " \
70
- "(#{dependency.requirement}) via #{spec.name} (#{spec.version})"
71
- end
72
- end
73
-
74
- def parsed_lockfile
75
- @parsed_lockfile ||= Bundler::LockfileParser.new(lockfile)
76
- end
77
-
78
- def lockfile
79
- return @lockfile if defined?(@lockfile)
80
-
81
- @lockfile =
82
- begin
83
- return unless lockfile_name && File.exist?(lockfile_name)
84
-
85
- File.read(lockfile_name)
86
- end
87
- end
88
- end
89
- end
@@ -1,90 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- module Functions
5
- class DependencySource
6
- attr_reader :gemfile_name
7
- attr_reader :dependency_name
8
-
9
- RUBYGEMS = "rubygems"
10
- PRIVATE_REGISTRY = "private"
11
- GIT = "git"
12
- OTHER = "other"
13
-
14
- def initialize(gemfile_name:, dependency_name:)
15
- @gemfile_name = gemfile_name
16
- @dependency_name = dependency_name
17
- end
18
-
19
- def type
20
- bundler_source = specified_source || default_source
21
- type_of(bundler_source)
22
- end
23
-
24
- def latest_git_version(dependency_source_url:, dependency_source_branch:)
25
- source = Bundler::Source::Git.new(
26
- "uri" => dependency_source_url,
27
- "branch" => dependency_source_branch,
28
- "name" => dependency_name,
29
- "submodules" => true
30
- )
31
-
32
- # Tell Bundler we're fine with fetching the source remotely
33
- source.instance_variable_set(:@allow_remote, true)
34
-
35
- spec = source.specs.first
36
- { version: spec.version, commit_sha: spec.source.revision }
37
- end
38
-
39
- def private_registry_versions
40
- bundler_source = specified_source || default_source
41
-
42
- bundler_source
43
- .fetchers.flat_map do |fetcher|
44
- fetcher
45
- .specs_with_retry([dependency_name], bundler_source)
46
- .search_all(dependency_name)
47
- end
48
- .map(&:version)
49
- end
50
-
51
- private
52
-
53
- def type_of(bundler_source)
54
- case bundler_source
55
- when Bundler::Source::Rubygems
56
- remote = bundler_source.remotes.first
57
- if remote.nil? || remote.to_s == "https://rubygems.org/"
58
- RUBYGEMS
59
- else
60
- PRIVATE_REGISTRY
61
- end
62
- when Bundler::Source::Git
63
- GIT
64
- else
65
- OTHER
66
- end
67
- end
68
-
69
- def specified_source
70
- return @specified_source if defined? @specified_source
71
-
72
- @specified_source = definition.dependencies
73
- .find { |dep| dep.name == dependency_name }&.source
74
- end
75
-
76
- def default_source
77
- definition.send(:sources).default_source
78
- end
79
-
80
- def definition
81
- @definition ||= Bundler::Definition.build(gemfile_name, nil, {})
82
- end
83
-
84
- def serialize_bundler_source(source)
85
- {
86
- type: source.class.to_s
87
- }
88
- end
89
- end
90
- end
@@ -1,119 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- require "uri"
5
-
6
- module Functions
7
- class FileParser
8
- def initialize(lockfile_name:)
9
- @lockfile_name = lockfile_name
10
- end
11
-
12
- attr_reader :lockfile_name
13
-
14
- def parsed_gemfile(gemfile_name:)
15
- Bundler::Definition.build(gemfile_name, nil, {})
16
- .dependencies.select(&:current_platform?)
17
- .reject { |dep| local_sources.include?(dep.source.class) }
18
- .map { |dep| serialize_bundler_dependency(dep) }
19
- end
20
-
21
- def parsed_gemspec(gemspec_name:)
22
- Bundler.load_gemspec_uncached(gemspec_name)
23
- .dependencies
24
- .map { |dep| serialize_bundler_dependency(dep) }
25
- end
26
-
27
- private
28
-
29
- def lockfile
30
- return @lockfile if defined?(@lockfile)
31
-
32
- @lockfile =
33
- begin
34
- return unless lockfile_name && File.exist?(lockfile_name)
35
-
36
- File.read(lockfile_name)
37
- end
38
- end
39
-
40
- def parsed_lockfile
41
- return unless lockfile
42
-
43
- @parsed_lockfile ||= Bundler::LockfileParser.new(lockfile)
44
- end
45
-
46
- def source_from_lockfile(dependency_name)
47
- parsed_lockfile&.specs&.find { |s| s.name == dependency_name }&.source
48
- end
49
-
50
- def source_for(dependency)
51
- source = dependency.source
52
- if lockfile && default_rubygems?(source)
53
- # If there's a lockfile and the Gemfile doesn't have anything
54
- # interesting to say about the source, check that.
55
- source = source_from_lockfile(dependency.name)
56
- end
57
- raise "Bad source: #{source}" unless sources.include?(source.class)
58
-
59
- return nil if default_rubygems?(source)
60
-
61
- details = { type: source.class.name.split("::").last.downcase }
62
- details.merge!(git_source_details(source)) if source.is_a?(Bundler::Source::Git)
63
- details[:url] = source.remotes.first.to_s if source.is_a?(Bundler::Source::Rubygems)
64
- details
65
- end
66
-
67
- def git_source_details(source)
68
- {
69
- url: source.uri,
70
- branch: source.branch,
71
- ref: source.ref
72
- }
73
- end
74
-
75
- RUBYGEMS_HOSTS = [
76
- "rubygems.org",
77
- "www.rubygems.org"
78
- ].freeze
79
-
80
- def default_rubygems?(source)
81
- return true if source.nil?
82
- return false unless source.is_a?(Bundler::Source::Rubygems)
83
-
84
- source.remotes.any? do |r|
85
- RUBYGEMS_HOSTS.include?(URI(r.to_s).host)
86
- end
87
- end
88
-
89
- def serialize_bundler_dependency(dependency)
90
- {
91
- name: dependency.name,
92
- requirement: dependency.requirement,
93
- groups: dependency.groups,
94
- source: source_for(dependency),
95
- type: dependency.type
96
- }
97
- end
98
-
99
- # Can't be a constant because some of these don't exist in bundler
100
- # 1.15, which used to cause issues on Heroku (causing exception on boot).
101
- # TODO: Check if this will be an issue with multiple bundler versions
102
- def sources
103
- [
104
- NilClass,
105
- Bundler::Source::Rubygems,
106
- Bundler::Source::Git,
107
- *local_sources,
108
- Bundler::Source::Metadata
109
- ]
110
- end
111
-
112
- def local_sources
113
- [
114
- Bundler::Source::Path,
115
- Bundler::Source::Gemspec
116
- ]
117
- end
118
- end
119
- end
@@ -1,173 +0,0 @@
1
- # typed: true
2
- # frozen_string_literal: true
3
-
4
- module Functions
5
- class ForceUpdater
6
- class TransitiveDependencyError < StandardError; end
7
-
8
- def initialize(dependency_name:, target_version:, gemfile_name:,
9
- lockfile_name:, update_multiple_dependencies:)
10
- @dependency_name = dependency_name
11
- @target_version = target_version
12
- @gemfile_name = gemfile_name
13
- @lockfile_name = lockfile_name
14
- @update_multiple_dependencies = update_multiple_dependencies
15
- end
16
-
17
- def run
18
- # Only allow upgrades. Otherwise it's unlikely that this
19
- # resolution will be found by the FileUpdater
20
- Bundler.settings.set_command_option(
21
- "only_update_to_newer_versions",
22
- true
23
- )
24
-
25
- dependencies_to_unlock = []
26
-
27
- begin
28
- definition = build_definition(dependencies_to_unlock: dependencies_to_unlock)
29
- definition.resolve_remotely!
30
- specs = definition.resolve
31
- updates = [{ name: dependency_name }] +
32
- dependencies_to_unlock.map { |dep| { name: dep.name } }
33
- specs = specs.map do |dep|
34
- {
35
- name: dep.name,
36
- version: dep.version
37
- }
38
- end
39
- [updates, specs]
40
- rescue Bundler::VersionConflict => e
41
- raise unless update_multiple_dependencies?
42
-
43
- # TODO: Not sure this won't unlock way too many things...
44
- new_dependencies_to_unlock =
45
- new_dependencies_to_unlock_from(
46
- error: e,
47
- already_unlocked: dependencies_to_unlock
48
- )
49
-
50
- raise if new_dependencies_to_unlock.none?
51
-
52
- dependencies_to_unlock += new_dependencies_to_unlock
53
- retry
54
- end
55
- end
56
-
57
- private
58
-
59
- attr_reader :dependency_name
60
- attr_reader :target_version
61
- attr_reader :gemfile_name
62
- attr_reader :lockfile_name
63
- attr_reader :credentials
64
- attr_reader :update_multiple_dependencies
65
- alias update_multiple_dependencies? update_multiple_dependencies
66
-
67
- def new_dependencies_to_unlock_from(error:, already_unlocked:)
68
- potentials_deps =
69
- relevant_conflicts(error, already_unlocked)
70
- .flat_map(&:requirement_trees)
71
- .reject do |tree|
72
- # If the final requirement wasn't specific, it can't be binding
73
- next true if tree.last.requirement == Gem::Requirement.new(">= 0")
74
-
75
- # If the conflict wasn't for the dependency we're updating then
76
- # we don't have enough info to reject it
77
- next false unless tree.last.name == dependency_name
78
-
79
- # If the final requirement *was* for the dependency we're updating
80
- # then we can ignore the tree if it permits the target version
81
- tree.last.requirement.satisfied_by?(
82
- Gem::Version.new(target_version)
83
- )
84
- end.map(&:first)
85
-
86
- potentials_deps
87
- .reject { |dep| already_unlocked.map(&:name).include?(dep.name) }
88
- .reject { |dep| [dependency_name, "ruby\0"].include?(dep.name) }
89
- .uniq
90
- end
91
-
92
- def relevant_conflicts(error, dependencies_being_unlocked)
93
- names = [*dependencies_being_unlocked.map(&:name), dependency_name]
94
-
95
- # For a conflict to be relevant to the updates we're making it must be
96
- # 1) caused by a new requirement introduced by our unlocking, or
97
- # 2) caused by an old requirement that prohibits the update.
98
- # Hence, we look at the beginning and end of the requirement trees
99
- error.cause.conflicts.values
100
- .select do |conflict|
101
- conflict.requirement_trees.any? do |t|
102
- names.include?(t.last.name) || names.include?(t.first.name)
103
- end
104
- end
105
- end
106
-
107
- def build_definition(dependencies_to_unlock:)
108
- gems_to_unlock = dependencies_to_unlock.map(&:name) + [dependency_name]
109
- definition = Bundler::Definition.build(
110
- gemfile_name,
111
- lockfile_name,
112
- gems: gems_to_unlock + subdependencies,
113
- lock_shared_dependencies: true
114
- )
115
-
116
- # Remove the Gemfile / gemspec requirements on the gems we're
117
- # unlocking (i.e., completely unlock them)
118
- gems_to_unlock.each do |gem_name|
119
- unlock_gem(definition: definition, gem_name: gem_name)
120
- end
121
-
122
- dep = definition.dependencies
123
- .find { |d| d.name == dependency_name }
124
-
125
- # If the dependency is not found in the Gemfile it means this is a
126
- # transitive dependency that we can't force update.
127
- raise TransitiveDependencyError unless dep
128
-
129
- # Set the requirement for the gem we're forcing an update of
130
- new_req = Gem::Requirement.create("= #{target_version}")
131
- dep.instance_variable_set(:@requirement, new_req)
132
- dep.source = nil if dep.source.is_a?(Bundler::Source::Git)
133
-
134
- definition
135
- end
136
-
137
- def lockfile
138
- return @lockfile if defined?(@lockfile)
139
-
140
- @lockfile =
141
- begin
142
- return unless lockfile_name && File.exist?(lockfile_name)
143
-
144
- File.read(lockfile_name)
145
- end
146
- end
147
-
148
- def subdependencies
149
- # If there's no lockfile we don't need to worry about
150
- # subdependencies
151
- return [] unless lockfile
152
-
153
- all_deps = Bundler::LockfileParser.new(lockfile)
154
- .specs.map { |x| x.name.to_s }
155
- top_level = Bundler::Definition
156
- .build(gemfile_name, lockfile_name, {})
157
- .dependencies.map { |x| x.name.to_s }
158
-
159
- all_deps - top_level
160
- end
161
-
162
- def unlock_gem(definition:, gem_name:)
163
- dep = definition.dependencies.find { |d| d.name == gem_name }
164
- version = definition.locked_gems.specs
165
- .find { |d| d.name == gem_name }.version
166
-
167
- dep&.instance_variable_set(
168
- :@requirement,
169
- Gem::Requirement.create(">= #{version}")
170
- )
171
- end
172
- end
173
- end