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
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/source"
4
+
5
+ module Bundler
6
+ class Source
7
+ class Git
8
+ class GitProxy
9
+ private
10
+
11
+ # Bundler allows ssh authentication when talking to GitHub but there's
12
+ # no way for Dependabot to do so (it doesn't have any ssh keys).
13
+ # Instead, we convert all `git@github.com:` URLs to use HTTPS.
14
+ def configured_uri_for(uri)
15
+ uri = uri.gsub(%r{git@(.*?):/?}, 'https://\1/')
16
+ if /https?:/ =~ uri
17
+ remote = Bundler::URI(uri)
18
+ config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
19
+ remote.userinfo ||= config_auth
20
+ remote.to_s
21
+ else
22
+ uri
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ module Bundler
31
+ class Source
32
+ class Git < Path
33
+ private
34
+
35
+ def serialize_gemspecs_in(destination)
36
+ original_load_paths = $LOAD_PATH.dup
37
+ reduced_load_paths = original_load_paths.
38
+ reject { |p| p.include?("/gems/") }
39
+
40
+ $LOAD_PATH.shift until $LOAD_PATH.empty?
41
+ reduced_load_paths.each { |p| $LOAD_PATH << p }
42
+
43
+ if destination.relative?
44
+ destination = destination.expand_path(Bundler.root)
45
+ end
46
+ Dir["#{destination}/#{@glob}"].each do |spec_path|
47
+ # Evaluate gemspecs and cache the result. Gemspecs
48
+ # in git might require git or other dependencies.
49
+ # The gemspecs we cache should already be evaluated.
50
+ spec = Bundler.load_gemspec(spec_path)
51
+ next unless spec
52
+
53
+ Bundler.rubygems.set_installed_by_version(spec)
54
+ Bundler.rubygems.validate(spec)
55
+ File.open(spec_path, "wb") { |file| file.write(spec.to_ruby) }
56
+ end
57
+ $LOAD_PATH.shift until $LOAD_PATH.empty?
58
+ original_load_paths.each { |p| $LOAD_PATH << p }
59
+ end
60
+ end
61
+ end
62
+ end
data/helpers/v2/run.rb ADDED
@@ -0,0 +1,44 @@
1
+ require "bundler"
2
+ require "json"
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("./lib", __dir__))
5
+ $LOAD_PATH.unshift(File.expand_path("../v1/monkey_patches", __dir__))
6
+
7
+ # Bundler monkey patches
8
+ require "definition_ruby_version_patch"
9
+ require "definition_bundler_version_patch"
10
+ require "git_source_patch"
11
+
12
+ require "functions"
13
+
14
+ MIN_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 >= '#{MIN_BUNDLER_VERSION}'"
20
+ end
21
+
22
+ def correct_bundler_version?
23
+ Gem::Version.new(Bundler::VERSION) >= Gem::Version.new(MIN_BUNDLER_VERSION)
24
+ end
25
+
26
+ def output(obj)
27
+ print JSON.dump(obj)
28
+ end
29
+
30
+ begin
31
+ validate_bundler_version!
32
+
33
+ request = JSON.parse($stdin.read)
34
+
35
+ function = request["function"]
36
+ args = request["args"].transform_keys(&:to_sym)
37
+
38
+ output({ result: Functions.send(function, **args) })
39
+ rescue => error
40
+ output(
41
+ { error: error.message, error_class: error.class, trace: error.backtrace }
42
+ )
43
+ exit(1)
44
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "native_spec_helper"
4
+ require "shared_contexts"
5
+
6
+ RSpec.describe Functions::ConflictingDependencyResolver do
7
+ include_context "in a temporary bundler directory"
8
+
9
+ let(:conflicting_dependency_resolver) do
10
+ described_class.new(
11
+ dependency_name: dependency_name,
12
+ target_version: target_version,
13
+ lockfile_name: "Gemfile.lock"
14
+ )
15
+ end
16
+
17
+ let(:dependency_name) { "dummy-pkg-a" }
18
+ let(:target_version) { "2.0.0" }
19
+
20
+ let(:project_name) { "blocked_by_subdep" }
21
+
22
+ describe "#conflicting_dependencies" do
23
+ subject(:conflicting_dependencies) do
24
+ in_tmp_folder { conflicting_dependency_resolver.conflicting_dependencies }
25
+ end
26
+
27
+ it "returns a list of dependencies that block the update" do
28
+ expect(conflicting_dependencies).to eq(
29
+ [{
30
+ "explanation" => "dummy-pkg-b (1.0.0) requires dummy-pkg-a (< 2.0.0)",
31
+ "name" => "dummy-pkg-b",
32
+ "version" => "1.0.0",
33
+ "requirement" => "< 2.0.0"
34
+ }]
35
+ )
36
+ end
37
+
38
+ context "for nested transitive dependencies" do
39
+ let(:project_name) { "transitive_blocking" }
40
+ let(:dependency_name) { "activesupport" }
41
+ let(:target_version) { "6.0.0" }
42
+
43
+ it "returns a list of dependencies that block the update" do
44
+ expect(conflicting_dependencies).to match_array(
45
+ [
46
+ {
47
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0)",
48
+ "name" => "rails",
49
+ "requirement" => "= 5.2.0",
50
+ "version" => "5.2.0"
51
+ },
52
+ {
53
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via actionpack (5.2.0)",
54
+ "name" => "actionpack",
55
+ "version" => "5.2.0",
56
+ "requirement" => "= 5.2.0"
57
+ },
58
+ {
59
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via actionview (5.2.0)",
60
+ "name" => "actionview",
61
+ "version" => "5.2.0",
62
+ "requirement" => "= 5.2.0"
63
+ },
64
+ {
65
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via activejob (5.2.0)",
66
+ "name" => "activejob",
67
+ "version" => "5.2.0",
68
+ "requirement" => "= 5.2.0"
69
+ },
70
+ {
71
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via activemodel (5.2.0)",
72
+ "name" => "activemodel",
73
+ "version" => "5.2.0",
74
+ "requirement" => "= 5.2.0"
75
+ },
76
+ {
77
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via activerecord (5.2.0)",
78
+ "name" => "activerecord",
79
+ "version" => "5.2.0",
80
+ "requirement" => "= 5.2.0"
81
+ },
82
+ {
83
+ "explanation" => "rails (5.2.0) requires activesupport (= 5.2.0) via railties (5.2.0)",
84
+ "name" => "railties",
85
+ "version" => "5.2.0",
86
+ "requirement" => "= 5.2.0"
87
+ }
88
+ ]
89
+ )
90
+ end
91
+ end
92
+
93
+ context "with multiple blocking dependencies" do
94
+ let(:dependency_name) { "activesupport" }
95
+ let(:current_version) { "5.0.0" }
96
+ let(:target_version) { "6.0.0" }
97
+ let(:project_name) { "multiple_blocking" }
98
+
99
+ it "returns all of the blocking dependencies" do
100
+ expect(conflicting_dependencies).to match_array(
101
+ [
102
+ {
103
+ "explanation" => "actionmailer (5.0.0) requires activesupport (= 5.0.0) via actionpack (5.0.0)",
104
+ "name" => "actionpack",
105
+ "version" => "5.0.0",
106
+ "requirement" => "= 5.0.0"
107
+ },
108
+ {
109
+ "explanation" => "actionview (5.0.0) requires activesupport (= 5.0.0)",
110
+ "name" => "actionview",
111
+ "version" => "5.0.0",
112
+ "requirement" => "= 5.0.0"
113
+ },
114
+ {
115
+ "explanation" => "actionmailer (5.0.0) requires activesupport (= 5.0.0) via activejob (5.0.0)",
116
+ "name" => "activejob",
117
+ "version" => "5.0.0",
118
+ "requirement" => "= 5.0.0"
119
+ }
120
+ ]
121
+ )
122
+ end
123
+ end
124
+
125
+ context "without any blocking dependencies" do
126
+ let(:target_version) { "1.0.0" }
127
+
128
+ it "returns an empty list" do
129
+ expect(conflicting_dependencies).to eq([])
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "native_spec_helper"
4
+ require "shared_contexts"
5
+
6
+ RSpec.describe Functions::FileParser do
7
+ include_context "in a temporary bundler directory"
8
+
9
+ let(:dependency_source) do
10
+ described_class.new(
11
+ lockfile_name: "Gemfile.lock"
12
+ )
13
+ end
14
+
15
+ let(:project_name) { "gemfile" }
16
+
17
+ describe "#parsed_gemfile" do
18
+ subject(:parsed_gemfile) do
19
+ in_tmp_folder do
20
+ dependency_source.parsed_gemfile(gemfile_name: "Gemfile")
21
+ end
22
+ end
23
+
24
+ it "parses gemfile" do
25
+ parsed_gemfile = [
26
+ {
27
+ groups: [:default],
28
+ name: "business",
29
+ requirement: Gem::Requirement.new("~> 1.4.0"),
30
+ source: nil,
31
+ type: :runtime
32
+ },
33
+ {
34
+ groups: [:default],
35
+ name: "statesman",
36
+ requirement: Gem::Requirement.new("~> 1.2.0"),
37
+ source: nil,
38
+ type: :runtime
39
+ }
40
+ ]
41
+ is_expected.to eq(parsed_gemfile)
42
+ end
43
+
44
+ context "with a git source" do
45
+ let(:project_name) { "git_source" }
46
+
47
+ it "parses gemfile" do
48
+ parsed_gemfile = [
49
+ {
50
+ groups: [:default],
51
+ name: "business",
52
+ requirement: Gem::Requirement.new("~> 1.6.0"),
53
+ source: {
54
+ branch: "master",
55
+ ref: "a1b78a9",
56
+ type: "git",
57
+ url: "git@github.com:gocardless/business"
58
+ },
59
+ type: :runtime
60
+ },
61
+ {
62
+ groups: [:default],
63
+ name: "statesman",
64
+ requirement: Gem::Requirement.new("~> 1.2.0"),
65
+ source: nil,
66
+ type: :runtime
67
+ },
68
+ {
69
+ groups: [:default],
70
+ name: "prius",
71
+ requirement: Gem::Requirement.new(">= 0"),
72
+ source: {
73
+ branch: "master",
74
+ ref: "master",
75
+ type: "git",
76
+ url: "https://github.com/gocardless/prius"
77
+ },
78
+ type: :runtime
79
+ },
80
+ {
81
+ groups: [:default],
82
+ name: "que",
83
+ requirement: Gem::Requirement.new(">= 0"),
84
+ source: {
85
+ branch: "master",
86
+ ref: "v0.11.6",
87
+ type: "git",
88
+ url: "git@github.com:chanks/que"
89
+ },
90
+ type: :runtime
91
+ },
92
+ {
93
+ groups: [:default],
94
+ name: "uk_phone_numbers",
95
+ requirement: Gem::Requirement.new(">= 0"),
96
+ source: {
97
+ branch: "master",
98
+ ref: "master",
99
+ type: "git",
100
+ url: "http://github.com/gocardless/uk_phone_numbers"
101
+ },
102
+ type: :runtime
103
+ }
104
+ ]
105
+ is_expected.to eq(parsed_gemfile)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "#parsed_gemspec" do
111
+ let!(:gemspec_fixture) do
112
+ fixture("ruby", "gemspecs", "exact")
113
+ end
114
+
115
+ subject(:parsed_gemspec) do
116
+ in_tmp_folder do |tmp_path|
117
+ File.write(File.join(tmp_path, "test.gemspec"), gemspec_fixture)
118
+ dependency_source.parsed_gemspec(gemspec_name: "test.gemspec")
119
+ end
120
+ end
121
+
122
+ it "parses gemspec" do
123
+ parsed_gemspec = [
124
+ {
125
+ groups: nil,
126
+ name: "business",
127
+ requirement: Gem::Requirement.new("= 1.0.0"),
128
+ source: nil,
129
+ type: :runtime
130
+ },
131
+ {
132
+ groups: nil,
133
+ name: "statesman",
134
+ requirement: Gem::Requirement.new("= 1.0.0"),
135
+ source: nil,
136
+ type: :runtime
137
+ }
138
+ ]
139
+ is_expected.to eq(parsed_gemspec)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "native_spec_helper"
4
+
5
+ RSpec.describe Functions do
6
+ # Verify v1 method signatures are exist, but raise as NYI
7
+ {
8
+ vendor_cache_dir: [ :dir ],
9
+ update_lockfile: [ :dir, :gemfile_name, :lockfile_name, :using_bundler2, :credentials, :dependencies ],
10
+ force_update: [ :dir, :dependency_name, :target_version, :gemfile_name, :lockfile_name, :using_bundler2,
11
+ :credentials, :update_multiple_dependencies ],
12
+ dependency_source_type: [ :gemfile_name, :dependency_name, :dir, :credentials ],
13
+ depencency_source_latest_git_version: [ :gemfile_name, :dependency_name, :dir, :credentials, :dependency_source_url,
14
+ :dependency_source_branch ],
15
+ private_registry_versions: [:gemfile_name, :dependency_name, :dir, :credentials ],
16
+ resolve_version: [:dependency_name, :dependency_requirements, :gemfile_name, :lockfile_name, :using_bundler2,
17
+ :dir, :credentials],
18
+ jfrog_source: [:dir, :gemfile_name, :credentials, :using_bundler2],
19
+ git_specs: [:dir, :gemfile_name, :credentials, :using_bundler2],
20
+ }.each do |function, kwargs|
21
+ describe "::#{function}" do
22
+ let(:args) do
23
+ kwargs.inject({}) do |args, keyword|
24
+ args.merge({ keyword => anything })
25
+ end
26
+ end
27
+
28
+ it "raises a NYI" do
29
+ expect { Functions.send(function, **args) }.to raise_error(Functions::NotImplementedError)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/its"
4
+ require "webmock/rspec"
5
+ require "byebug"
6
+
7
+ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
8
+ $LOAD_PATH.unshift(File.expand_path("../monkey_patches", __dir__))
9
+
10
+ # Bundler monkey patches
11
+ require "definition_ruby_version_patch"
12
+ require "definition_bundler_version_patch"
13
+ require "git_source_patch"
14
+
15
+ require "functions"
16
+
17
+ RSpec.configure do |config|
18
+ config.color = true
19
+ config.order = :rand
20
+ config.mock_with(:rspec) { |mocks| mocks.verify_partial_doubles = true }
21
+ config.raise_errors_for_deprecations!
22
+ end
23
+
24
+ # Duplicated in lib/dependabot/bundler/file_updater/lockfile_updater.rb
25
+ # TODO: Stop sanitizing the lockfile once we have bundler 2 installed
26
+ LOCKFILE_ENDING = /(?<ending>\s*(?:RUBY VERSION|BUNDLED WITH).*)/m.freeze
27
+
28
+ def project_dependency_files(project)
29
+ project_path = File.expand_path(File.join("../../spec/fixtures/projects/bundler1", project))
30
+ Dir.chdir(project_path) do
31
+ # NOTE: Include dotfiles (e.g. .npmrc)
32
+ files = Dir.glob("**/*", File::FNM_DOTMATCH)
33
+ files = files.select { |f| File.file?(f) }
34
+ files.map do |filename|
35
+ content = File.read(filename)
36
+ if filename == "Gemfile.lock"
37
+ content = content.gsub(LOCKFILE_ENDING, "")
38
+ end
39
+ {
40
+ name: filename,
41
+ content: content
42
+ }
43
+ end
44
+ end
45
+ end
46
+
47
+ def fixture(*name)
48
+ File.read(File.join("../../spec/fixtures", File.join(*name)))
49
+ end