dependabot-bundler 0.137.1 → 0.138.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/v1/build +2 -1
  3. data/helpers/v1/run.rb +14 -0
  4. data/helpers/v2/.gitignore +8 -0
  5. data/helpers/v2/Gemfile +12 -0
  6. data/helpers/v2/build +24 -0
  7. data/helpers/v2/lib/functions.rb +122 -0
  8. data/helpers/v2/lib/functions/conflicting_dependency_resolver.rb +86 -0
  9. data/helpers/v2/lib/functions/file_parser.rb +106 -0
  10. data/helpers/v2/monkey_patches/definition_bundler_version_patch.rb +15 -0
  11. data/helpers/v2/monkey_patches/definition_ruby_version_patch.rb +20 -0
  12. data/helpers/v2/monkey_patches/git_source_patch.rb +62 -0
  13. data/helpers/v2/run.rb +44 -0
  14. data/helpers/v2/spec/functions/conflicting_dependency_resolver_spec.rb +133 -0
  15. data/helpers/v2/spec/functions/file_parser_spec.rb +142 -0
  16. data/helpers/v2/spec/functions_spec.rb +33 -0
  17. data/helpers/v2/spec/native_spec_helper.rb +49 -0
  18. data/helpers/v2/spec/shared_contexts.rb +60 -0
  19. data/lib/dependabot/bundler/file_parser.rb +13 -1
  20. data/lib/dependabot/bundler/file_updater.rb +3 -2
  21. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +4 -3
  22. data/lib/dependabot/bundler/helpers.rb +15 -3
  23. data/lib/dependabot/bundler/native_helpers.rb +8 -1
  24. data/lib/dependabot/bundler/update_checker.rb +12 -6
  25. data/lib/dependabot/bundler/update_checker/conflicting_dependency_resolver.rb +5 -2
  26. data/lib/dependabot/bundler/update_checker/force_updater.rb +6 -3
  27. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +6 -3
  28. data/lib/dependabot/bundler/update_checker/latest_version_finder/dependency_source.rb +5 -3
  29. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +0 -4
  30. data/lib/dependabot/bundler/update_checker/version_resolver.rb +8 -4
  31. metadata +19 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9250fba22b9b1d480c1a2e9c9d47887fbcedc6204ef8f9a874af781b4b5e3bdf
4
- data.tar.gz: 0ec5ea2610ce9cd805ea259234d0bf1050b0db6390ab7611201452f83f365798
3
+ metadata.gz: 6fe43d8a687e3405df9f2704529fe54f9e79cdb75b6246d43a32e32e5a7b1928
4
+ data.tar.gz: 67b1b3afd35f559613e57f6ef2a26ef9ee0c50910072b40c5d9ac412b830ed91
5
5
  SHA512:
6
- metadata.gz: 59690230cb90627b8fce31ecdb43e17ec7a1acb4c95c417444534661106bd9a84d5dbfa2f3f3ced1f0f35cbd67be93a60204f41daac0af8d039533b4bf0f28e2
7
- data.tar.gz: c103d732b44c9f7eb9cbbfd94d68b3c207f0b153660d2dec9588cbc1126a8bb066144ff287a28458f9fec110fe93bf28e55ff92f23dcdd9970169875c097f30c
6
+ metadata.gz: 55bd706fffed2c9caa866237eea57fde3f91a83f465014865dacf2252094ad31e9098d0894c9b85da77f08e5ae0941f8a74ca1f1cfecec447775aa20541bcfae
7
+ data.tar.gz: 563832a40283d4c9f29175b0cddc850ac88e461e65905e255f94234aee9c913690525016a2d12cbfdedb17f4d5c0873214863b324207277bbfc80bccee7c7f8f
data/helpers/v1/build CHANGED
@@ -21,4 +21,5 @@ cd "$install_dir"
21
21
 
22
22
  # NOTE: Sets `BUNDLED WITH` to match the installed v1 version in Gemfile.lock
23
23
  # forcing native helpers to run with the same version
24
- BUNDLER_VERSION=1 bundle install --without test
24
+ BUNDLER_VERSION=1 bundle config set --local without "test"
25
+ BUNDLER_VERSION=1 bundle install
data/helpers/v1/run.rb CHANGED
@@ -11,11 +11,25 @@ require "git_source_patch"
11
11
 
12
12
  require "functions"
13
13
 
14
+ MAX_BUNDLER_VERSION="2.0.0"
15
+
16
+ def validate_bundler_version!
17
+ return true if correct_bundler_version?
18
+
19
+ raise StandardError, "Called with Bundler '#{Bundler::VERSION}', expected < '#{MAX_BUNDLER_VERSION}'"
20
+ end
21
+
22
+ def correct_bundler_version?
23
+ Gem::Version.new(Bundler::VERSION) < Gem::Version.new(MAX_BUNDLER_VERSION)
24
+ end
25
+
14
26
  def output(obj)
15
27
  print JSON.dump(obj)
16
28
  end
17
29
 
18
30
  begin
31
+ validate_bundler_version!
32
+
19
33
  request = JSON.parse($stdin.read)
20
34
 
21
35
  function = request["function"]
@@ -0,0 +1,8 @@
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
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # NOTE: Used to run native helper specs
6
+ group :test do
7
+ gem "byebug", "11.1.3"
8
+ gem "rspec", "3.10.0"
9
+ gem "rspec-its", "1.3.0"
10
+ gem "vcr", "6.0.0"
11
+ gem "webmock", "3.12.1"
12
+ end
data/helpers/v2/build ADDED
@@ -0,0 +1,24 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ install_dir=$1
6
+ if [ -z "$install_dir" ]; then
7
+ echo "usage: $0 INSTALL_DIR"
8
+ exit 1
9
+ fi
10
+
11
+ helpers_dir="$(dirname "${BASH_SOURCE[0]}")"
12
+ cp -r \
13
+ "$helpers_dir/lib" \
14
+ "$helpers_dir/run.rb" \
15
+ "$helpers_dir/Gemfile" \
16
+ "$install_dir"
17
+
18
+ cd "$install_dir"
19
+
20
+ # NOTE: Sets `BUNDLED WITH` to match the installed v1 version in Gemfile.lock
21
+ # forcing specs and native helpers to run with the same version
22
+ BUNDLER_VERSION=2 bundle config set --local path ".bundle"
23
+ BUNDLER_VERSION=2 bundle config set --local without "test"
24
+ BUNDLER_VERSION=2 bundle install
@@ -0,0 +1,122 @@
1
+ require "functions/file_parser"
2
+ require "functions/conflicting_dependency_resolver"
3
+
4
+ module Functions
5
+ class NotImplementedError < StandardError; end
6
+
7
+ def self.parsed_gemfile(lockfile_name:, gemfile_name:, dir:)
8
+ set_bundler_flags_and_credentials(dir: dir, credentials: [],
9
+ using_bundler2: false)
10
+ FileParser.new(lockfile_name: lockfile_name).
11
+ parsed_gemfile(gemfile_name: gemfile_name)
12
+ end
13
+
14
+ def self.parsed_gemspec(lockfile_name:, gemspec_name:, dir:)
15
+ set_bundler_flags_and_credentials(dir: dir, credentials: [],
16
+ using_bundler2: false)
17
+ FileParser.new(lockfile_name: lockfile_name).
18
+ parsed_gemspec(gemspec_name: gemspec_name)
19
+ end
20
+
21
+ def self.vendor_cache_dir(dir:)
22
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
23
+ end
24
+
25
+ def self.update_lockfile(dir:, gemfile_name:, lockfile_name:, using_bundler2:,
26
+ credentials:, dependencies:)
27
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
28
+ end
29
+
30
+ def self.force_update(dir:, dependency_name:, target_version:, gemfile_name:,
31
+ lockfile_name:, using_bundler2:, credentials:,
32
+ update_multiple_dependencies:)
33
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
34
+ end
35
+
36
+ def self.dependency_source_type(gemfile_name:, dependency_name:, dir:,
37
+ credentials:)
38
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
39
+ end
40
+
41
+ def self.depencency_source_latest_git_version(gemfile_name:, dependency_name:,
42
+ dir:, credentials:,
43
+ dependency_source_url:,
44
+ dependency_source_branch:)
45
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
46
+ end
47
+
48
+ def self.private_registry_versions(gemfile_name:, dependency_name:, dir:,
49
+ credentials:)
50
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
51
+ end
52
+
53
+ def self.resolve_version(dependency_name:, dependency_requirements:,
54
+ gemfile_name:, lockfile_name:, using_bundler2:,
55
+ dir:, credentials:)
56
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
57
+ end
58
+
59
+ def self.jfrog_source(dir:, gemfile_name:, credentials:, using_bundler2:)
60
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
61
+ end
62
+
63
+ def self.git_specs(dir:, gemfile_name:, credentials:, using_bundler2:)
64
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
65
+ end
66
+
67
+ def self.set_bundler_flags_and_credentials(dir:, credentials:,
68
+ using_bundler2:)
69
+ dir = dir ? Pathname.new(dir) : dir
70
+ Bundler.instance_variable_set(:@root, dir)
71
+
72
+ # Remove installed gems from the default Rubygems index
73
+ Gem::Specification.all =
74
+ Gem::Specification.send(:default_stubs, "*.gemspec")
75
+
76
+ # Set auth details
77
+ relevant_credentials(credentials).each do |cred|
78
+ token = cred["token"] ||
79
+ "#{cred['username']}:#{cred['password']}"
80
+
81
+ Bundler.settings.set_command_option(
82
+ cred.fetch("host"),
83
+ token.gsub("@", "%40F").gsub("?", "%3F")
84
+ )
85
+ end
86
+
87
+ # NOTE: Prevent bundler from printing resolution information
88
+ Bundler.ui = Bundler::UI::Silent.new
89
+
90
+ # Use HTTPS for GitHub if lockfile
91
+ Bundler.settings.set_command_option("forget_cli_options", "true")
92
+ Bundler.settings.set_command_option("github.https", "true")
93
+ end
94
+
95
+ def self.relevant_credentials(credentials)
96
+ [
97
+ *git_source_credentials(credentials),
98
+ *private_registry_credentials(credentials)
99
+ ].select { |cred| cred["password"] || cred["token"] }
100
+ end
101
+
102
+ def self.private_registry_credentials(credentials)
103
+ credentials.
104
+ select { |cred| cred["type"] == "rubygems_server" }
105
+ end
106
+
107
+ def self.git_source_credentials(credentials)
108
+ credentials.
109
+ select { |cred| cred["type"] == "git_source" }
110
+ end
111
+
112
+ def self.conflicting_dependencies(dir:, dependency_name:, target_version:,
113
+ lockfile_name:, using_bundler2:, credentials:)
114
+ set_bundler_flags_and_credentials(dir: dir, credentials: credentials,
115
+ using_bundler2: using_bundler2)
116
+ ConflictingDependencyResolver.new(
117
+ dependency_name: dependency_name,
118
+ target_version: target_version,
119
+ lockfile_name: lockfile_name
120
+ ).conflicting_dependencies
121
+ end
122
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Functions
4
+ class ConflictingDependencyResolver
5
+ def initialize(dependency_name:, target_version:, lockfile_name:)
6
+ @dependency_name = dependency_name
7
+ @target_version = target_version
8
+ @lockfile_name = lockfile_name
9
+ end
10
+
11
+ # Finds any dependencies in the lockfile that have a subdependency on the
12
+ # given dependency that does not satisfly the target_version.
13
+ # @return [Array<Hash{String => String}]
14
+ # * explanation [String] a sentence explaining the conflict
15
+ # * name [String] the blocking dependencies name
16
+ # * version [String] the version of the blocking dependency
17
+ # * requirement [String] the requirement on the target_dependency
18
+ def conflicting_dependencies
19
+ Bundler.settings.set_command_option("only_update_to_newer_versions", true)
20
+
21
+ parent_specs.flat_map do |parent_spec|
22
+ top_level_specs_for(parent_spec).map do |top_level|
23
+ dependency = parent_spec.dependencies.find { |bd| bd.name == dependency_name }
24
+ {
25
+ "explanation" => explanation(parent_spec, dependency, top_level),
26
+ "name" => parent_spec.name,
27
+ "version" => parent_spec.version.to_s,
28
+ "requirement" => dependency.requirement.to_s
29
+ }
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :dependency_name, :target_version, :lockfile_name
37
+
38
+ def parent_specs
39
+ version = Gem::Version.new(target_version)
40
+ parsed_lockfile.specs.filter do |spec|
41
+ spec.dependencies.any? do |dep|
42
+ dep.name == dependency_name &&
43
+ !dep.requirement.satisfied_by?(version)
44
+ end
45
+ end
46
+ end
47
+
48
+ def top_level_specs_for(parent_spec)
49
+ return [parent_spec] if top_level?(parent_spec)
50
+
51
+ parsed_lockfile.specs.filter do |spec|
52
+ spec.dependencies.any? do |dep|
53
+ dep.name == parent_spec.name && top_level?(spec)
54
+ end
55
+ end
56
+ end
57
+
58
+ def top_level?(spec)
59
+ parsed_lockfile.dependencies.key?(spec.name)
60
+ end
61
+
62
+ def explanation(spec, dependency, top_level)
63
+ if spec.name == top_level.name
64
+ "#{spec.name} (#{spec.version}) requires #{dependency_name} (#{dependency.requirement})"
65
+ else
66
+ "#{top_level.name} (#{top_level.version}) requires #{dependency_name} "\
67
+ "(#{dependency.requirement}) via #{spec.name} (#{spec.version})"
68
+ end
69
+ end
70
+
71
+ def parsed_lockfile
72
+ @parsed_lockfile ||= Bundler::LockfileParser.new(lockfile)
73
+ end
74
+
75
+ def lockfile
76
+ return @lockfile if defined?(@lockfile)
77
+
78
+ @lockfile =
79
+ begin
80
+ return unless lockfile_name && File.exist?(lockfile_name)
81
+
82
+ File.read(lockfile_name)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,106 @@
1
+ module Functions
2
+ class FileParser
3
+ def initialize(lockfile_name:)
4
+ @lockfile_name = lockfile_name
5
+ end
6
+
7
+ attr_reader :lockfile_name
8
+
9
+ def parsed_gemfile(gemfile_name:)
10
+ Bundler::Definition.build(gemfile_name, nil, {}).
11
+ dependencies.select(&:current_platform?).
12
+ reject { |dep| dep.source.is_a?(Bundler::Source::Gemspec) }.
13
+ map(&method(:serialize_bundler_dependency))
14
+ end
15
+
16
+ def parsed_gemspec(gemspec_name:)
17
+ Bundler.load_gemspec_uncached(gemspec_name).
18
+ dependencies.
19
+ map(&method(:serialize_bundler_dependency))
20
+ end
21
+
22
+ private
23
+
24
+ def lockfile
25
+ return @lockfile if defined?(@lockfile)
26
+
27
+ @lockfile =
28
+ begin
29
+ return unless lockfile_name && File.exist?(lockfile_name)
30
+
31
+ File.read(lockfile_name)
32
+ end
33
+ end
34
+
35
+ def parsed_lockfile
36
+ return unless lockfile
37
+
38
+ @parsed_lockfile ||= Bundler::LockfileParser.new(lockfile)
39
+ end
40
+
41
+ def source_from_lockfile(dependency_name)
42
+ parsed_lockfile&.specs.find { |s| s.name == dependency_name }&.source
43
+ end
44
+
45
+ def source_for(dependency)
46
+ source = dependency.source
47
+ if lockfile && default_rubygems?(source)
48
+ # If there's a lockfile and the Gemfile doesn't have anything
49
+ # interesting to say about the source, check that.
50
+ source = source_from_lockfile(dependency.name)
51
+ end
52
+ raise "Bad source: #{source}" unless sources.include?(source.class)
53
+
54
+ return nil if default_rubygems?(source)
55
+
56
+ details = { type: source.class.name.split("::").last.downcase }
57
+ if source.is_a?(Bundler::Source::Git)
58
+ details.merge!(git_source_details(source))
59
+ end
60
+ if source.is_a?(Bundler::Source::Rubygems)
61
+ details[:url] = source.remotes.first.to_s
62
+ end
63
+ details
64
+ end
65
+
66
+ # TODO: Remove default `master` branch
67
+ def git_source_details(source)
68
+ {
69
+ url: source.uri,
70
+ branch: source.branch || "master",
71
+ ref: source.ref || "master"
72
+ }
73
+ end
74
+
75
+ def default_rubygems?(source)
76
+ return true if source.nil?
77
+ return false unless source.is_a?(Bundler::Source::Rubygems)
78
+
79
+ source.remotes.any? { |r| r.to_s.include?("rubygems.org") }
80
+ end
81
+
82
+ def serialize_bundler_dependency(dependency)
83
+ {
84
+ name: dependency.name,
85
+ requirement: dependency.requirement,
86
+ groups: dependency.groups,
87
+ source: source_for(dependency),
88
+ type: dependency.type
89
+ }
90
+ end
91
+
92
+ # Can't be a constant because some of these don't exist in bundler
93
+ # 1.15, which used to cause issues on Heroku (causing exception on boot).
94
+ # TODO: Check if this will be an issue with multiple bundler versions
95
+ def sources
96
+ [
97
+ NilClass,
98
+ Bundler::Source::Rubygems,
99
+ Bundler::Source::Git,
100
+ Bundler::Source::Path,
101
+ Bundler::Source::Gemspec,
102
+ Bundler::Source::Metadata
103
+ ]
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/definition"
4
+
5
+ # Ignore the Bundler version specified in the Gemfile (since the only Bundler
6
+ # version available to us is the one we're using).
7
+ module BundlerDefinitionBundlerVersionPatch
8
+ def expanded_dependencies
9
+ @expanded_dependencies ||=
10
+ expand_dependencies(dependencies + metadata_dependencies, @remote).
11
+ reject { |d| d.name == "bundler" }
12
+ end
13
+ end
14
+
15
+ Bundler::Definition.prepend(BundlerDefinitionBundlerVersionPatch)
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/definition"
4
+
5
+ module BundlerDefinitionRubyVersionPatch
6
+ def index
7
+ @index ||= super.tap do
8
+ if ruby_version
9
+ requested_version = ruby_version.to_gem_version_with_patchlevel
10
+ sources.metadata_source.specs <<
11
+ Gem::Specification.new("ruby\0", requested_version)
12
+ end
13
+
14
+ sources.metadata_source.specs <<
15
+ Gem::Specification.new("ruby\0", "2.5.3p105")
16
+ end
17
+ end
18
+ end
19
+
20
+ Bundler::Definition.prepend(BundlerDefinitionRubyVersionPatch)