dependabot-bundler 0.136.0 → 0.138.1

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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/v1/Gemfile +8 -3
  3. data/helpers/v1/build +2 -2
  4. data/helpers/v1/spec/functions/conflicting_dependency_resolver_spec.rb +133 -0
  5. data/helpers/v1/spec/functions/dependency_source_spec.rb +187 -0
  6. data/helpers/v1/spec/functions/file_parser_spec.rb +77 -0
  7. data/helpers/v1/spec/functions/version_resolver_spec.rb +97 -0
  8. data/helpers/v1/spec/native_spec_helper.rb +49 -0
  9. data/helpers/v1/spec/shared_contexts.rb +59 -0
  10. data/helpers/v2/.bundle/config +2 -0
  11. data/helpers/v2/.gitignore +9 -0
  12. data/helpers/v2/Gemfile +12 -0
  13. data/helpers/v2/build +23 -0
  14. data/helpers/v2/lib/functions.rb +67 -0
  15. data/helpers/v2/run.rb +30 -0
  16. data/helpers/v2/spec/functions_spec.rb +37 -0
  17. data/helpers/v2/spec/native_spec_helper.rb +50 -0
  18. data/lib/dependabot/bundler/file_parser.rb +13 -1
  19. data/lib/dependabot/bundler/file_updater.rb +3 -2
  20. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +4 -3
  21. data/lib/dependabot/bundler/helpers.rb +15 -3
  22. data/lib/dependabot/bundler/native_helpers.rb +8 -1
  23. data/lib/dependabot/bundler/update_checker.rb +12 -6
  24. data/lib/dependabot/bundler/update_checker/conflicting_dependency_resolver.rb +5 -2
  25. data/lib/dependabot/bundler/update_checker/force_updater.rb +6 -3
  26. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +6 -3
  27. data/lib/dependabot/bundler/update_checker/latest_version_finder/dependency_source.rb +5 -3
  28. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +0 -4
  29. data/lib/dependabot/bundler/update_checker/version_resolver.rb +8 -4
  30. metadata +18 -4
@@ -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,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/compact_index_client"
4
+ require "bundler/compact_index_client/updater"
5
+
6
+ TMP_DIR_PATH = File.expand_path("../tmp", __dir__)
7
+
8
+ RSpec.shared_context "in a temporary bundler directory" do
9
+ let(:project_name) { "gemfile" }
10
+
11
+ let(:tmp_path) do
12
+ Dir.mkdir(TMP_DIR_PATH) unless Dir.exist?(TMP_DIR_PATH)
13
+ dir = Dir.mktmpdir("native_helper_spec_", TMP_DIR_PATH)
14
+ Pathname.new(dir).expand_path
15
+ end
16
+
17
+ before do
18
+ project_dependency_files(project_name).each do |file|
19
+ File.write(File.join(tmp_path, file[:name]), file[:content])
20
+ end
21
+ end
22
+
23
+ def in_tmp_folder(&block)
24
+ Dir.chdir(tmp_path, &block)
25
+ end
26
+ end
27
+
28
+ RSpec.shared_context "without caching rubygems" do
29
+ before do
30
+ # Stub Bundler to stop it using a cached versions of Rubygems
31
+ allow_any_instance_of(Bundler::CompactIndexClient::Updater).
32
+ to receive(:etag_for).and_return("")
33
+ end
34
+ end
35
+
36
+ RSpec.shared_context "stub rubygems compact index" do
37
+ include_context "without caching rubygems"
38
+
39
+ before do
40
+ # Stub the Rubygems index
41
+ stub_request(:get, "https://index.rubygems.org/versions").
42
+ to_return(
43
+ status: 200,
44
+ body: fixture("ruby", "rubygems_responses", "index")
45
+ )
46
+
47
+ # Stub the Rubygems response for each dependency we have a fixture for
48
+ fixtures =
49
+ Dir[File.join("../../spec", "fixtures", "ruby", "rubygems_responses", "info-*")]
50
+ fixtures.each do |path|
51
+ dep_name = path.split("/").last.gsub("info-", "")
52
+ stub_request(:get, "https://index.rubygems.org/info/#{dep_name}").
53
+ to_return(
54
+ status: 200,
55
+ body: fixture("ruby", "rubygems_responses", "info-#{dep_name}")
56
+ )
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_PATH: ".bundle"
@@ -0,0 +1,9 @@
1
+ /.bundle/*
2
+ !/.bundle/config
3
+ /.env
4
+ /tmp
5
+ /dependabot-*.gem
6
+ Gemfile.lock
7
+ spec/fixtures/projects/*/.bundle/
8
+ !spec/fixtures/projects/**/Gemfile.lock
9
+ !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/.bundle" \
14
+ "$helpers_dir/lib" \
15
+ "$helpers_dir/run.rb" \
16
+ "$helpers_dir/Gemfile" \
17
+ "$install_dir"
18
+
19
+ cd "$install_dir"
20
+
21
+ # NOTE: Sets `BUNDLED WITH` to match the installed v1 version in Gemfile.lock
22
+ # forcing specs and native helpers to run with the same version
23
+ BUNDLER_VERSION=2 bundle install
@@ -0,0 +1,67 @@
1
+ module Functions
2
+ class NotImplementedError < StandardError; end
3
+
4
+ def self.parsed_gemfile(lockfile_name:, gemfile_name:, dir:)
5
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
6
+ end
7
+
8
+ def self.parsed_gemspec(lockfile_name:, gemspec_name:, dir:)
9
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
10
+ end
11
+
12
+ def self.vendor_cache_dir(dir:)
13
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
14
+ end
15
+
16
+ def self.update_lockfile(dir:, gemfile_name:, lockfile_name:, using_bundler2:,
17
+ credentials:, dependencies:)
18
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
19
+ end
20
+
21
+ def self.force_update(dir:, dependency_name:, target_version:, gemfile_name:,
22
+ lockfile_name:, using_bundler2:, credentials:,
23
+ update_multiple_dependencies:)
24
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
25
+ end
26
+
27
+ def self.dependency_source_type(gemfile_name:, dependency_name:, dir:,
28
+ credentials:)
29
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
30
+ end
31
+
32
+ def self.depencency_source_latest_git_version(gemfile_name:, dependency_name:,
33
+ dir:, credentials:,
34
+ dependency_source_url:,
35
+ dependency_source_branch:)
36
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
37
+ end
38
+
39
+ def self.private_registry_versions(gemfile_name:, dependency_name:, dir:,
40
+ credentials:)
41
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
42
+ end
43
+
44
+ def self.resolve_version(dependency_name:, dependency_requirements:,
45
+ gemfile_name:, lockfile_name:, using_bundler2:,
46
+ dir:, credentials:)
47
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
48
+ end
49
+
50
+ def self.jfrog_source(dir:, gemfile_name:, credentials:, using_bundler2:)
51
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
52
+ end
53
+
54
+ def self.git_specs(dir:, gemfile_name:, credentials:, using_bundler2:)
55
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
56
+ end
57
+
58
+ def self.set_bundler_flags_and_credentials(dir:, credentials:,
59
+ using_bundler2:)
60
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
61
+ end
62
+
63
+ def self.conflicting_dependencies(dir:, dependency_name:, target_version:,
64
+ lockfile_name:, using_bundler2:, credentials:)
65
+ raise NotImplementedError, "Bundler 2 adapter does not yet implement #{__method__}"
66
+ end
67
+ 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,37 @@
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
+ parsed_gemfile: [ :lockfile_name, :gemfile_name, :dir ],
9
+ parsed_gemspec: [ :lockfile_name, :gemspec_name, :dir ],
10
+ vendor_cache_dir: [ :dir ],
11
+ update_lockfile: [ :dir, :gemfile_name, :lockfile_name, :using_bundler2, :credentials, :dependencies ],
12
+ force_update: [ :dir, :dependency_name, :target_version, :gemfile_name, :lockfile_name, :using_bundler2,
13
+ :credentials, :update_multiple_dependencies ],
14
+ dependency_source_type: [ :gemfile_name, :dependency_name, :dir, :credentials ],
15
+ depencency_source_latest_git_version: [ :gemfile_name, :dependency_name, :dir, :credentials, :dependency_source_url,
16
+ :dependency_source_branch ],
17
+ private_registry_versions: [:gemfile_name, :dependency_name, :dir, :credentials ],
18
+ resolve_version: [:dependency_name, :dependency_requirements, :gemfile_name, :lockfile_name, :using_bundler2,
19
+ :dir, :credentials],
20
+ jfrog_source: [:dir, :gemfile_name, :credentials, :using_bundler2],
21
+ git_specs: [:dir, :gemfile_name, :credentials, :using_bundler2],
22
+ set_bundler_flags_and_credentials: [:dir, :credentials, :using_bundler2],
23
+ conflicting_dependencies: [:dir, :dependency_name, :target_version, :lockfile_name, :using_bundler2, :credentials]
24
+ }.each do |function, kwargs|
25
+ describe "::#{function}" do
26
+ let(:args) do
27
+ kwargs.inject({}) do |args, keyword|
28
+ args.merge({ keyword => anything })
29
+ end
30
+ end
31
+
32
+ it "raises a NYI" do
33
+ expect { Functions.send(function, **args) }.to raise_error(Functions::NotImplementedError)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
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
+ # TODO: Fork `v1/monkey_patches` into `v2/monkey_patches` ?
9
+ $LOAD_PATH.unshift(File.expand_path("../../v1/monkey_patches", __dir__))
10
+
11
+ # Bundler monkey patches
12
+ require "definition_ruby_version_patch"
13
+ require "definition_bundler_version_patch"
14
+ require "git_source_patch"
15
+
16
+ require "functions"
17
+
18
+ RSpec.configure do |config|
19
+ config.color = true
20
+ config.order = :rand
21
+ config.mock_with(:rspec) { |mocks| mocks.verify_partial_doubles = true }
22
+ config.raise_errors_for_deprecations!
23
+ end
24
+
25
+ # Duplicated in lib/dependabot/bundler/file_updater/lockfile_updater.rb
26
+ # TODO: Stop sanitizing the lockfile once we have bundler 2 installed
27
+ LOCKFILE_ENDING = /(?<ending>\s*(?:RUBY VERSION|BUNDLED WITH).*)/m.freeze
28
+
29
+ def project_dependency_files(project)
30
+ project_path = File.expand_path(File.join("../../spec/fixtures/projects/bundler1", project))
31
+ Dir.chdir(project_path) do
32
+ # NOTE: Include dotfiles (e.g. .npmrc)
33
+ files = Dir.glob("**/*", File::FNM_DOTMATCH)
34
+ files = files.select { |f| File.file?(f) }
35
+ files.map do |filename|
36
+ content = File.read(filename)
37
+ if filename == "Gemfile.lock"
38
+ content = content.gsub(LOCKFILE_ENDING, "")
39
+ end
40
+ {
41
+ name: filename,
42
+ content: content
43
+ }
44
+ end
45
+ end
46
+ end
47
+
48
+ def fixture(*name)
49
+ File.read(File.join("../../spec/fixtures", File.join(*name)))
50
+ 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