dependabot-bundler 0.137.0 → 0.138.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10624630c42401b86b141da992ab3c2aca5e2f59c82068ba338f26545e133283
4
- data.tar.gz: 850b5758a654ce2326fabbf3cf50acd1e5aff2e0d6afec7e8dd14c698d412f30
3
+ metadata.gz: 2eb2653cea8a396b9d65f20ea24e8680bdcb0909c2e28ad045beca904f2a984b
4
+ data.tar.gz: d7ffc19ecc9db88a04e5222f132e18f94f353eb806ef454d0e958c531531dbd5
5
5
  SHA512:
6
- metadata.gz: c43fef6dcb5b1a57b696b29fd1c6936a2fa82a91edf00fa6f4a7fc573e5f4f4e452315bb4b5053017e1c3255c0e3a625857a4e78dd4e93790bc019ff86b3f538
7
- data.tar.gz: 03d91ae8abfd4980c6517b65d8ccad37257bd269044c5373bd10804ffb3763a8aefd2c1d30b59520304eeed13db6cd7234cf9e460cf5fdf86f96dab77f46b0b5
6
+ metadata.gz: 28ea0b95452a1c7bc2cf7fe5d3c211c678a89d5b062f7184dc23fd06dd5f8df8a61ccd9e66abb3a807fc10958f74623245ef4809830600a44ce6472095ce369a
7
+ data.tar.gz: 46c21b5dbc8ddaac57a40c1a6e48c16427562c829d6b29879a5b1b6d77b9d3c16ebdef515ef32c6f71d4c57c4feeed57ff22ee7f78a6839991e3444cefd44181
@@ -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,23 @@
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 install --without test
@@ -0,0 +1,115 @@
1
+ require "functions/file_parser"
2
+
3
+ module Functions
4
+ class NotImplementedError < StandardError; end
5
+
6
+ def self.parsed_gemfile(lockfile_name:, gemfile_name:, dir:)
7
+ set_bundler_flags_and_credentials(dir: dir, credentials: [],
8
+ using_bundler2: false)
9
+ FileParser.new(lockfile_name: lockfile_name).
10
+ parsed_gemfile(gemfile_name: gemfile_name)
11
+ end
12
+
13
+ def self.parsed_gemspec(lockfile_name:, gemspec_name:, dir:)
14
+ set_bundler_flags_and_credentials(dir: dir, credentials: [],
15
+ using_bundler2: false)
16
+ FileParser.new(lockfile_name: lockfile_name).
17
+ parsed_gemspec(gemspec_name: gemspec_name)
18
+ end
19
+
20
+ def self.vendor_cache_dir(dir:)
21
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
22
+ end
23
+
24
+ def self.update_lockfile(dir:, gemfile_name:, lockfile_name:, using_bundler2:,
25
+ credentials:, dependencies:)
26
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
27
+ end
28
+
29
+ def self.force_update(dir:, dependency_name:, target_version:, gemfile_name:,
30
+ lockfile_name:, using_bundler2:, credentials:,
31
+ update_multiple_dependencies:)
32
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
33
+ end
34
+
35
+ def self.dependency_source_type(gemfile_name:, dependency_name:, dir:,
36
+ credentials:)
37
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
38
+ end
39
+
40
+ def self.depencency_source_latest_git_version(gemfile_name:, dependency_name:,
41
+ dir:, credentials:,
42
+ dependency_source_url:,
43
+ dependency_source_branch:)
44
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
45
+ end
46
+
47
+ def self.private_registry_versions(gemfile_name:, dependency_name:, dir:,
48
+ credentials:)
49
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
50
+ end
51
+
52
+ def self.resolve_version(dependency_name:, dependency_requirements:,
53
+ gemfile_name:, lockfile_name:, using_bundler2:,
54
+ dir:, credentials:)
55
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
56
+ end
57
+
58
+ def self.jfrog_source(dir:, gemfile_name:, credentials:, using_bundler2:)
59
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
60
+ end
61
+
62
+ def self.git_specs(dir:, gemfile_name:, credentials:, using_bundler2:)
63
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
64
+ end
65
+
66
+ def self.set_bundler_flags_and_credentials(dir:, credentials:,
67
+ using_bundler2:)
68
+ dir = dir ? Pathname.new(dir) : dir
69
+ Bundler.instance_variable_set(:@root, dir)
70
+
71
+ # Remove installed gems from the default Rubygems index
72
+ Gem::Specification.all =
73
+ Gem::Specification.send(:default_stubs, "*.gemspec")
74
+
75
+ # Set auth details
76
+ relevant_credentials(credentials).each do |cred|
77
+ token = cred["token"] ||
78
+ "#{cred['username']}:#{cred['password']}"
79
+
80
+ Bundler.settings.set_command_option(
81
+ cred.fetch("host"),
82
+ token.gsub("@", "%40F").gsub("?", "%3F")
83
+ )
84
+ end
85
+
86
+ # NOTE: Prevent bundler from printing resolution information
87
+ Bundler.ui = Bundler::UI::Silent.new
88
+
89
+ # Use HTTPS for GitHub if lockfile
90
+ Bundler.settings.set_command_option("forget_cli_options", "true")
91
+ Bundler.settings.set_command_option("github.https", "true")
92
+ end
93
+
94
+ def self.relevant_credentials(credentials)
95
+ [
96
+ *git_source_credentials(credentials),
97
+ *private_registry_credentials(credentials)
98
+ ].select { |cred| cred["password"] || cred["token"] }
99
+ end
100
+
101
+ def self.private_registry_credentials(credentials)
102
+ credentials.
103
+ select { |cred| cred["type"] == "rubygems_server" }
104
+ end
105
+
106
+ def self.git_source_credentials(credentials)
107
+ credentials.
108
+ select { |cred| cred["type"] == "git_source" }
109
+ end
110
+
111
+ def self.conflicting_dependencies(dir:, dependency_name:, target_version:,
112
+ lockfile_name:, using_bundler2:, credentials:)
113
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
114
+ end
115
+ 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)
@@ -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,30 @@
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
+ def output(obj)
15
+ print JSON.dump(obj)
16
+ end
17
+
18
+ begin
19
+ request = JSON.parse($stdin.read)
20
+
21
+ function = request["function"]
22
+ args = request["args"].transform_keys(&:to_sym)
23
+
24
+ output({ result: Functions.send(function, **args) })
25
+ rescue => error
26
+ output(
27
+ { error: error.message, error_class: error.class, trace: error.backtrace }
28
+ )
29
+ exit(1)
30
+ 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,34 @@
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
+ conflicting_dependencies: [:dir, :dependency_name, :target_version, :lockfile_name, :using_bundler2, :credentials]
21
+ }.each do |function, kwargs|
22
+ describe "::#{function}" do
23
+ let(:args) do
24
+ kwargs.inject({}) do |args, keyword|
25
+ args.merge({ keyword => anything })
26
+ end
27
+ end
28
+
29
+ it "raises a NYI" do
30
+ expect { Functions.send(function, **args) }.to raise_error(Functions::NotImplementedError)
31
+ end
32
+ end
33
+ end
34
+ 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
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+ require "bundler/compact_index_client"
5
+ require "bundler/compact_index_client/updater"
6
+
7
+ TMP_DIR_PATH = File.expand_path("../tmp", __dir__)
8
+
9
+ RSpec.shared_context "in a temporary bundler directory" do
10
+ let(:project_name) { "gemfile" }
11
+
12
+ let(:tmp_path) do
13
+ Dir.mkdir(TMP_DIR_PATH) unless Dir.exist?(TMP_DIR_PATH)
14
+ dir = Dir.mktmpdir("native_helper_spec_", TMP_DIR_PATH)
15
+ Pathname.new(dir).expand_path
16
+ end
17
+
18
+ before do
19
+ project_dependency_files(project_name).each do |file|
20
+ File.write(File.join(tmp_path, file[:name]), file[:content])
21
+ end
22
+ end
23
+
24
+ def in_tmp_folder(&block)
25
+ Dir.chdir(tmp_path, &block)
26
+ end
27
+ end
28
+
29
+ RSpec.shared_context "without caching rubygems" do
30
+ before do
31
+ # Stub Bundler to stop it using a cached versions of Rubygems
32
+ allow_any_instance_of(Bundler::CompactIndexClient::Updater).
33
+ to receive(:etag_for).and_return("")
34
+ end
35
+ end
36
+
37
+ RSpec.shared_context "stub rubygems compact index" do
38
+ include_context "without caching rubygems"
39
+
40
+ before do
41
+ # Stub the Rubygems index
42
+ stub_request(:get, "https://index.rubygems.org/versions").
43
+ to_return(
44
+ status: 200,
45
+ body: fixture("ruby", "rubygems_responses", "index")
46
+ )
47
+
48
+ # Stub the Rubygems response for each dependency we have a fixture for
49
+ fixtures =
50
+ Dir[File.join("../../spec", "fixtures", "ruby", "rubygems_responses", "info-*")]
51
+ fixtures.each do |path|
52
+ dep_name = path.split("/").last.gsub("info-", "")
53
+ stub_request(:get, "https://index.rubygems.org/info/#{dep_name}").
54
+ to_return(
55
+ status: 200,
56
+ body: fixture("ruby", "rubygems_responses", "info-#{dep_name}")
57
+ )
58
+ end
59
+ end
60
+ end
@@ -23,6 +23,7 @@ module Dependabot
23
23
  dependency_set += gemspec_dependencies
24
24
  dependency_set += lockfile_dependencies
25
25
  check_external_code(dependency_set.dependencies)
26
+ instrument_package_manager_version
26
27
  dependency_set.dependencies
27
28
  end
28
29
 
@@ -42,6 +43,17 @@ module Dependabot
42
43
  end
43
44
  end
44
45
 
46
+ def instrument_package_manager_version
47
+ version = Helpers.detected_bundler_version(lockfile)
48
+ Dependabot.instrument(
49
+ Notifications::FILE_PARSER_PACKAGE_MANAGER_VERSION_PARSED,
50
+ ecosystem: "bundler",
51
+ package_managers: {
52
+ "bundler" => version
53
+ }
54
+ )
55
+ end
56
+
45
57
  def gemfile_dependencies
46
58
  dependencies = DependencySet.new
47
59
 
@@ -301,7 +313,7 @@ module Dependabot
301
313
  end
302
314
 
303
315
  def bundler_version
304
- @bundler_version ||= Helpers.bundler_version(lockfile)
316
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
305
317
  end
306
318
  end
307
319
  end
@@ -151,7 +151,8 @@ module Dependabot
151
151
  dependencies: dependencies,
152
152
  dependency_files: dependency_files,
153
153
  repo_contents_path: repo_contents_path,
154
- credentials: credentials
154
+ credentials: credentials,
155
+ options: options
155
156
  ).updated_lockfile_content
156
157
  end
157
158
 
@@ -162,7 +163,7 @@ module Dependabot
162
163
  end
163
164
 
164
165
  def bundler_version
165
- @bundler_version ||= Helpers.bundler_version(lockfile)
166
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
166
167
  end
167
168
  end
168
169
  end
@@ -33,11 +33,12 @@ module Dependabot
33
33
  end
34
34
 
35
35
  def initialize(dependencies:, dependency_files:,
36
- repo_contents_path: nil, credentials:)
36
+ repo_contents_path: nil, credentials:, options:)
37
37
  @dependencies = dependencies
38
38
  @dependency_files = dependency_files
39
39
  @repo_contents_path = repo_contents_path
40
40
  @credentials = credentials
41
+ @options = options
41
42
  end
42
43
 
43
44
  def updated_lockfile_content
@@ -54,7 +55,7 @@ module Dependabot
54
55
  private
55
56
 
56
57
  attr_reader :dependencies, :dependency_files, :repo_contents_path,
57
- :credentials
58
+ :credentials, :options
58
59
 
59
60
  def build_updated_lockfile
60
61
  base_dir = dependency_files.first.directory
@@ -304,7 +305,7 @@ module Dependabot
304
305
  end
305
306
 
306
307
  def bundler_version
307
- @bundler_version ||= Helpers.bundler_version(lockfile)
308
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
308
309
  end
309
310
  end
310
311
  end
@@ -6,9 +6,21 @@ module Dependabot
6
6
  V1 = "1"
7
7
  V2 = "2"
8
8
 
9
- # TODO: Add support for bundler v2
10
- # return "v2" if lockfile.content.match?(/BUNDLED WITH\s+2/m)
11
- def self.bundler_version(_lockfile)
9
+ # NOTE: options is a manditory argument to ensure we pass it from all calling classes
10
+ def self.bundler_version(_lockfile, options:)
11
+ # For now, force V2 if bundler_2_available
12
+ return V2 if options[:bundler_2_available]
13
+
14
+ # TODO: Add support for bundler v2 based on lockfile
15
+ # return V2 if lockfile.content.match?(/BUNDLED WITH\s+2/m)
16
+
17
+ V1
18
+ end
19
+
20
+ def self.detected_bundler_version(lockfile)
21
+ return "unknown" unless lockfile
22
+ return V2 if lockfile.content.match?(/BUNDLED WITH\s+2/m)
23
+
12
24
  V1
13
25
  end
14
26
  end
@@ -17,9 +17,16 @@ module Dependabot
17
17
  # Bundler will pick the matching installed major version
18
18
  "BUNDLER_VERSION" => bundler_version,
19
19
  "BUNDLE_GEMFILE" => File.join(versioned_helper_path(bundler_version: bundler_version), "Gemfile"),
20
- "BUNDLE_PATH" => File.join(versioned_helper_path(bundler_version: bundler_version), ".bundle")
20
+ "BUNDLE_PATH" => File.join(versioned_helper_path(bundler_version: bundler_version), ".bundle"),
21
+ # Prevent the GEM_HOME from being set to a folder owned by root
22
+ "GEM_HOME" => File.join(versioned_helper_path(bundler_version: bundler_version), ".bundle")
21
23
  }
22
24
  )
25
+ rescue SharedHelpers::HelperSubprocessFailed => e
26
+ # TODO: Remove once we stop stubbing out the V2 native helper
27
+ raise Dependabot::NotImplemented, e.message if e.error_class == "Functions::NotImplementedError"
28
+
29
+ raise
23
30
  end
24
31
  end
25
32
 
@@ -110,7 +110,8 @@ module Dependabot
110
110
  ConflictingDependencyResolver.new(
111
111
  dependency_files: dependency_files,
112
112
  repo_contents_path: repo_contents_path,
113
- credentials: credentials
113
+ credentials: credentials,
114
+ options: options
114
115
  ).conflicting_dependencies(
115
116
  dependency: dependency,
116
117
  target_version: lowest_security_fix_version
@@ -162,7 +163,8 @@ module Dependabot
162
163
  credentials: credentials,
163
164
  target_version: version,
164
165
  requirements_update_strategy: requirements_update_strategy,
165
- update_multiple_dependencies: false
166
+ update_multiple_dependencies: false,
167
+ options: options
166
168
  ).updated_dependencies
167
169
  true
168
170
  rescue Dependabot::DependencyFileNotResolvable
@@ -183,7 +185,8 @@ module Dependabot
183
185
  credentials: credentials,
184
186
  ignored_versions: ignored_versions,
185
187
  raise_on_ignored: raise_on_ignored,
186
- replacement_git_pin: tag
188
+ replacement_git_pin: tag,
189
+ options: options
187
190
  ).latest_resolvable_version_details
188
191
  true
189
192
  rescue Dependabot::DependencyFileNotResolvable
@@ -339,7 +342,8 @@ module Dependabot
339
342
  repo_contents_path: repo_contents_path,
340
343
  credentials: credentials,
341
344
  target_version: latest_version,
342
- requirements_update_strategy: requirements_update_strategy
345
+ requirements_update_strategy: requirements_update_strategy,
346
+ options: options
343
347
  )
344
348
  end
345
349
 
@@ -365,7 +369,8 @@ module Dependabot
365
369
  raise_on_ignored: raise_on_ignored,
366
370
  remove_git_source: remove_git_source,
367
371
  unlock_requirement: unlock_requirement,
368
- latest_allowable_version: latest_version
372
+ latest_allowable_version: latest_version,
373
+ options: options
369
374
  )
370
375
  end
371
376
  end
@@ -386,7 +391,8 @@ module Dependabot
386
391
  credentials: credentials,
387
392
  ignored_versions: ignored_versions,
388
393
  raise_on_ignored: raise_on_ignored,
389
- security_advisories: security_advisories
394
+ security_advisories: security_advisories,
395
+ options: options
390
396
  )
391
397
  end
392
398
  end
@@ -12,10 +12,13 @@ module Dependabot
12
12
  require_relative "shared_bundler_helpers"
13
13
  include SharedBundlerHelpers
14
14
 
15
- def initialize(dependency_files:, repo_contents_path:, credentials:)
15
+ attr_reader :options
16
+
17
+ def initialize(dependency_files:, repo_contents_path:, credentials:, options:)
16
18
  @dependency_files = dependency_files
17
19
  @repo_contents_path = repo_contents_path
18
20
  @credentials = credentials
21
+ @options = options
19
22
  end
20
23
 
21
24
  # Finds any dependencies in the lockfile that have a subdependency on
@@ -47,7 +50,7 @@ module Dependabot
47
50
  private
48
51
 
49
52
  def bundler_version
50
- @bundler_version ||= Helpers.bundler_version(lockfile)
53
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
51
54
  end
52
55
  end
53
56
  end
@@ -19,7 +19,8 @@ module Dependabot
19
19
  def initialize(dependency:, dependency_files:, repo_contents_path: nil,
20
20
  credentials:, target_version:,
21
21
  requirements_update_strategy:,
22
- update_multiple_dependencies: true)
22
+ update_multiple_dependencies: true,
23
+ options:)
23
24
  @dependency = dependency
24
25
  @dependency_files = dependency_files
25
26
  @repo_contents_path = repo_contents_path
@@ -27,6 +28,7 @@ module Dependabot
27
28
  @target_version = target_version
28
29
  @requirements_update_strategy = requirements_update_strategy
29
30
  @update_multiple_dependencies = update_multiple_dependencies
31
+ @options = options
30
32
  end
31
33
 
32
34
  def updated_dependencies
@@ -36,7 +38,8 @@ module Dependabot
36
38
  private
37
39
 
38
40
  attr_reader :dependency, :dependency_files, :repo_contents_path,
39
- :credentials, :target_version, :requirements_update_strategy
41
+ :credentials, :target_version, :requirements_update_strategy,
42
+ :options
40
43
 
41
44
  def update_multiple_dependencies?
42
45
  @update_multiple_dependencies
@@ -149,7 +152,7 @@ module Dependabot
149
152
  end
150
153
 
151
154
  def bundler_version
152
- @bundler_version ||= Helpers.bundler_version(lockfile)
155
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
153
156
  end
154
157
  end
155
158
  end
@@ -15,7 +15,7 @@ module Dependabot
15
15
  class LatestVersionFinder
16
16
  def initialize(dependency:, dependency_files:, repo_contents_path: nil,
17
17
  credentials:, ignored_versions:, raise_on_ignored: false,
18
- security_advisories:)
18
+ security_advisories:, options:)
19
19
  @dependency = dependency
20
20
  @dependency_files = dependency_files
21
21
  @repo_contents_path = repo_contents_path
@@ -23,6 +23,7 @@ module Dependabot
23
23
  @ignored_versions = ignored_versions
24
24
  @raise_on_ignored = raise_on_ignored
25
25
  @security_advisories = security_advisories
26
+ @options = options
26
27
  end
27
28
 
28
29
  def latest_version_details
@@ -36,7 +37,8 @@ module Dependabot
36
37
  private
37
38
 
38
39
  attr_reader :dependency, :dependency_files, :repo_contents_path,
39
- :credentials, :ignored_versions, :security_advisories
40
+ :credentials, :ignored_versions, :security_advisories,
41
+ :options
40
42
 
41
43
  def fetch_latest_version_details
42
44
  return dependency_source.latest_git_version_details if dependency_source.git?
@@ -103,7 +105,8 @@ module Dependabot
103
105
  @dependency_source ||= DependencySource.new(
104
106
  dependency: dependency,
105
107
  dependency_files: dependency_files,
106
- credentials: credentials
108
+ credentials: credentials,
109
+ options: options
107
110
  )
108
111
  end
109
112
 
@@ -17,14 +17,16 @@ module Dependabot
17
17
  OTHER = "other"
18
18
 
19
19
  attr_reader :dependency, :dependency_files, :repo_contents_path,
20
- :credentials
20
+ :credentials, :options
21
21
 
22
22
  def initialize(dependency:,
23
23
  dependency_files:,
24
- credentials:)
24
+ credentials:,
25
+ options:)
25
26
  @dependency = dependency
26
27
  @dependency_files = dependency_files
27
28
  @credentials = credentials
29
+ @options = options
28
30
  end
29
31
 
30
32
  # The latest version details for the dependency from a registry
@@ -145,7 +147,7 @@ module Dependabot
145
147
  end
146
148
 
147
149
  def bundler_version
148
- @bundler_version ||= Helpers.bundler_version(lockfile)
150
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
149
151
  end
150
152
  end
151
153
  end
@@ -237,10 +237,6 @@ module Dependabot
237
237
 
238
238
  lockfile.content.match?(/BUNDLED WITH\s+2/m)
239
239
  end
240
-
241
- def bundler_version
242
- @bundler_version ||= Helpers.bundler_version(lockfile)
243
- end
244
240
  end
245
241
  end
246
242
  end
@@ -23,7 +23,8 @@ module Dependabot
23
23
  raise_on_ignored: false,
24
24
  replacement_git_pin: nil, remove_git_source: false,
25
25
  unlock_requirement: true,
26
- latest_allowable_version: nil)
26
+ latest_allowable_version: nil,
27
+ options:)
27
28
  @dependency = dependency
28
29
  @unprepared_dependency_files = unprepared_dependency_files
29
30
  @credentials = credentials
@@ -34,6 +35,7 @@ module Dependabot
34
35
  @remove_git_source = remove_git_source
35
36
  @unlock_requirement = unlock_requirement
36
37
  @latest_allowable_version = latest_allowable_version
38
+ @options = options
37
39
  end
38
40
 
39
41
  def latest_resolvable_version_details
@@ -45,7 +47,8 @@ module Dependabot
45
47
 
46
48
  attr_reader :dependency, :unprepared_dependency_files,
47
49
  :repo_contents_path, :credentials, :ignored_versions,
48
- :replacement_git_pin, :latest_allowable_version
50
+ :replacement_git_pin, :latest_allowable_version,
51
+ :options
49
52
 
50
53
  def remove_git_source?
51
54
  @remove_git_source
@@ -164,7 +167,8 @@ module Dependabot
164
167
  credentials: credentials,
165
168
  ignored_versions: ignored_versions,
166
169
  raise_on_ignored: @raise_on_ignored,
167
- security_advisories: []
170
+ security_advisories: [],
171
+ options: options
168
172
  ).latest_version_details
169
173
  end
170
174
 
@@ -221,7 +225,7 @@ module Dependabot
221
225
  end
222
226
 
223
227
  def bundler_version
224
- @bundler_version ||= Helpers.bundler_version(lockfile)
228
+ @bundler_version ||= Helpers.bundler_version(lockfile, options: options)
225
229
  end
226
230
  end
227
231
  end
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.137.0
4
+ version: 0.138.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-15 00:00:00.000000000 Z
11
+ date: 2021-03-23 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.137.0
19
+ version: 0.138.2
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.137.0
26
+ version: 0.138.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +206,19 @@ files:
206
206
  - helpers/v1/spec/functions/version_resolver_spec.rb
207
207
  - helpers/v1/spec/native_spec_helper.rb
208
208
  - helpers/v1/spec/shared_contexts.rb
209
+ - helpers/v2/.gitignore
210
+ - helpers/v2/Gemfile
211
+ - helpers/v2/build
212
+ - helpers/v2/lib/functions.rb
213
+ - helpers/v2/lib/functions/file_parser.rb
214
+ - helpers/v2/monkey_patches/definition_bundler_version_patch.rb
215
+ - helpers/v2/monkey_patches/definition_ruby_version_patch.rb
216
+ - helpers/v2/monkey_patches/git_source_patch.rb
217
+ - helpers/v2/run.rb
218
+ - helpers/v2/spec/functions/file_parser_spec.rb
219
+ - helpers/v2/spec/functions_spec.rb
220
+ - helpers/v2/spec/native_spec_helper.rb
221
+ - helpers/v2/spec/shared_contexts.rb
209
222
  - lib/dependabot/bundler.rb
210
223
  - lib/dependabot/bundler/file_fetcher.rb
211
224
  - lib/dependabot/bundler/file_fetcher/child_gemfile_finder.rb