dependabot-bundler 0.137.2 → 0.138.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/helpers/v1/build +2 -1
  3. data/helpers/v1/run.rb +16 -0
  4. data/helpers/v2/.gitignore +8 -0
  5. data/helpers/v2/Gemfile +12 -0
  6. data/helpers/v2/build +25 -0
  7. data/helpers/v2/lib/functions.rb +189 -0
  8. data/helpers/v2/lib/functions/conflicting_dependency_resolver.rb +86 -0
  9. data/helpers/v2/lib/functions/dependency_source.rb +86 -0
  10. data/helpers/v2/lib/functions/file_parser.rb +106 -0
  11. data/helpers/v2/lib/functions/force_updater.rb +167 -0
  12. data/helpers/v2/lib/functions/lockfile_updater.rb +224 -0
  13. data/helpers/v2/lib/functions/version_resolver.rb +140 -0
  14. data/helpers/v2/monkey_patches/definition_bundler_version_patch.rb +15 -0
  15. data/helpers/v2/monkey_patches/definition_ruby_version_patch.rb +20 -0
  16. data/helpers/v2/monkey_patches/git_source_patch.rb +62 -0
  17. data/helpers/v2/run.rb +44 -0
  18. data/helpers/v2/spec/functions/conflicting_dependency_resolver_spec.rb +133 -0
  19. data/helpers/v2/spec/functions/dependency_source_spec.rb +185 -0
  20. data/helpers/v2/spec/functions/file_parser_spec.rb +142 -0
  21. data/helpers/v2/spec/functions/version_resolver_spec.rb +97 -0
  22. data/helpers/v2/spec/functions_spec.rb +22 -0
  23. data/helpers/v2/spec/native_spec_helper.rb +49 -0
  24. data/helpers/v2/spec/shared_contexts.rb +60 -0
  25. data/lib/dependabot/bundler/file_parser.rb +13 -1
  26. data/lib/dependabot/bundler/file_updater.rb +3 -2
  27. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +4 -3
  28. data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +4 -2
  29. data/lib/dependabot/bundler/helpers.rb +15 -3
  30. data/lib/dependabot/bundler/native_helpers.rb +5 -0
  31. data/lib/dependabot/bundler/update_checker.rb +12 -6
  32. data/lib/dependabot/bundler/update_checker/conflicting_dependency_resolver.rb +5 -2
  33. data/lib/dependabot/bundler/update_checker/force_updater.rb +6 -3
  34. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +6 -3
  35. data/lib/dependabot/bundler/update_checker/latest_version_finder/dependency_source.rb +5 -3
  36. data/lib/dependabot/bundler/update_checker/requirements_updater.rb +2 -2
  37. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +0 -4
  38. data/lib/dependabot/bundler/update_checker/version_resolver.rb +8 -4
  39. metadata +25 -4
@@ -0,0 +1,140 @@
1
+ module Functions
2
+ class VersionResolver
3
+ GEM_NOT_FOUND_ERROR_REGEX = /locked to (?<name>[^\s]+) \(/.freeze
4
+
5
+ attr_reader :dependency_name, :dependency_requirements,
6
+ :gemfile_name, :lockfile_name
7
+
8
+ def initialize(dependency_name:, dependency_requirements:,
9
+ gemfile_name:, lockfile_name:)
10
+ @dependency_name = dependency_name
11
+ @dependency_requirements = dependency_requirements
12
+ @gemfile_name = gemfile_name
13
+ @lockfile_name = lockfile_name
14
+ end
15
+
16
+ def version_details
17
+ dep = dependency_from_definition
18
+
19
+ # If the dependency wasn't found in the definition, but *is*
20
+ # included in a gemspec, it's because the Gemfile didn't import
21
+ # the gemspec. This is unusual, but the correct behaviour if/when
22
+ # it happens is to behave as if the repo was gemspec-only.
23
+ if dep.nil? && dependency_requirements.any?
24
+ return "latest"
25
+ end
26
+
27
+ # Otherwise, if the dependency wasn't found it's because it is a
28
+ # subdependency that was removed when attempting to update it.
29
+ return nil if dep.nil?
30
+
31
+ # If the dependency is Bundler itself then we can't trust the
32
+ # version that has been returned (it's the version Dependabot is
33
+ # running on, rather than the true latest resolvable version).
34
+ return nil if dep.name == "bundler"
35
+
36
+ details = {
37
+ version: dep.version,
38
+ ruby_version: ruby_version,
39
+ fetcher: fetcher_class(dep)
40
+ }
41
+ if dep.source.instance_of?(::Bundler::Source::Git)
42
+ details[:commit_sha] = dep.source.revision
43
+ end
44
+ details
45
+ end
46
+
47
+ private
48
+
49
+ # rubocop:disable Metrics/PerceivedComplexity
50
+ def dependency_from_definition(unlock_subdependencies: true)
51
+ dependencies_to_unlock = [dependency_name]
52
+ dependencies_to_unlock += subdependencies if unlock_subdependencies
53
+ begin
54
+ definition = build_definition(dependencies_to_unlock)
55
+ definition.resolve_remotely!
56
+ rescue ::Bundler::GemNotFound => e
57
+ unlock_yanked_gem(dependencies_to_unlock, e) && retry
58
+ rescue ::Bundler::HTTPError => e
59
+ # Retry network errors
60
+ # Note: in_a_native_bundler_context will also retry `Bundler::HTTPError` errors
61
+ # up to three times meaning we'll end up retrying this error up to six times
62
+ # TODO: Could we get rid of this retry logic and only rely on
63
+ # SharedBundlerHelpers.in_a_native_bundler_context
64
+ attempt ||= 1
65
+ attempt += 1
66
+ raise if attempt > 3 || !e.message.include?("Network error")
67
+
68
+ retry
69
+ end
70
+
71
+ dep = definition.resolve.find { |d| d.name == dependency_name }
72
+ return dep if dep
73
+ return if dependency_requirements.any? || !unlock_subdependencies
74
+
75
+ # If no definition was found and we're updating a sub-dependency,
76
+ # try again but without unlocking any other sub-dependencies
77
+ dependency_from_definition(unlock_subdependencies: false)
78
+ end
79
+ # rubocop:enable Metrics/PerceivedComplexity
80
+
81
+ def subdependencies
82
+ # If there's no lockfile we don't need to worry about
83
+ # subdependencies
84
+ return [] unless lockfile
85
+
86
+ all_deps = ::Bundler::LockfileParser.new(lockfile).
87
+ specs.map(&:name).map(&:to_s).uniq
88
+ top_level = build_definition([]).dependencies.
89
+ map(&:name).map(&:to_s)
90
+
91
+ all_deps - top_level
92
+ end
93
+
94
+ def build_definition(dependencies_to_unlock)
95
+ # Note: we lock shared dependencies to avoid any top-level
96
+ # dependencies getting unlocked (which would happen if they were
97
+ # also subdependencies of the dependency being unlocked)
98
+ ::Bundler::Definition.build(
99
+ gemfile_name,
100
+ lockfile_name,
101
+ gems: dependencies_to_unlock,
102
+ lock_shared_dependencies: true
103
+ )
104
+ end
105
+
106
+ def unlock_yanked_gem(dependencies_to_unlock, error)
107
+ raise unless error.message.match?(GEM_NOT_FOUND_ERROR_REGEX)
108
+
109
+ gem_name = error.message.match(GEM_NOT_FOUND_ERROR_REGEX).
110
+ named_captures["name"]
111
+ raise if dependencies_to_unlock.include?(gem_name)
112
+
113
+ dependencies_to_unlock << gem_name
114
+ end
115
+
116
+ def lockfile
117
+ return @lockfile if defined?(@lockfile)
118
+
119
+ @lockfile =
120
+ begin
121
+ return unless lockfile_name
122
+ return unless File.exist?(lockfile_name)
123
+
124
+ File.read(lockfile_name)
125
+ end
126
+ end
127
+
128
+ def fetcher_class(dep)
129
+ return unless dep.source.is_a?(::Bundler::Source::Rubygems)
130
+
131
+ dep.source.fetchers.first.fetchers.first.class.to_s
132
+ end
133
+
134
+ def ruby_version
135
+ return nil unless gemfile_name
136
+
137
+ @ruby_version ||= build_definition([]).ruby_version&.gem_version
138
+ end
139
+ end
140
+ 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,44 @@
1
+ require "bundler"
2
+ require "json"
3
+
4
+ $LOAD_PATH.unshift(File.expand_path("./lib", __dir__))
5
+ $LOAD_PATH.unshift(File.expand_path("./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,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "native_spec_helper"
4
+ require "shared_contexts"
5
+
6
+ RSpec.describe Functions::DependencySource do
7
+ include_context "in a temporary bundler directory"
8
+
9
+ let(:dependency_source) do
10
+ described_class.new(
11
+ gemfile_name: "Gemfile",
12
+ dependency_name: dependency_name
13
+ )
14
+ end
15
+
16
+ let(:dependency_name) { "business" }
17
+
18
+ let(:project_name) { "specified_source_no_lockfile" }
19
+ let(:registry_url) { "https://repo.fury.io/greysteil/" }
20
+ let(:gemfury_business_url) do
21
+ "https://repo.fury.io/greysteil/api/v1/dependencies?gems=business"
22
+ end
23
+
24
+ before do
25
+ stub_request(:get, registry_url + "versions").
26
+ with(basic_auth: ["SECRET_CODES", ""]).
27
+ to_return(status: 404)
28
+ stub_request(:get, registry_url + "api/v1/dependencies").
29
+ with(basic_auth: ["SECRET_CODES", ""]).
30
+ to_return(status: 200)
31
+ stub_request(:get, gemfury_business_url).
32
+ with(basic_auth: ["SECRET_CODES", ""]).
33
+ to_return(status: 200, body: fixture("ruby", "gemfury_response"))
34
+ end
35
+
36
+ describe "#private_registry_versions" do
37
+ subject(:private_registry_versions) do
38
+ in_tmp_folder { dependency_source.private_registry_versions }
39
+ end
40
+
41
+ it "returns all versions from the private source" do
42
+ is_expected.to eq([
43
+ Gem::Version.new("1.5.0"),
44
+ Gem::Version.new("1.9.0"),
45
+ Gem::Version.new("1.10.0.beta")
46
+ ])
47
+ end
48
+
49
+ context "specified as the default source" do
50
+ let(:project_name) { "specified_default_source_no_lockfile" }
51
+
52
+ it "returns all versions from the private source" do
53
+ is_expected.to eq([
54
+ Gem::Version.new("1.5.0"),
55
+ Gem::Version.new("1.9.0"),
56
+ Gem::Version.new("1.10.0.beta")
57
+ ])
58
+ end
59
+ end
60
+
61
+ context "that we don't have authentication details for" do
62
+ before do
63
+ stub_request(:get, registry_url + "versions").
64
+ with(basic_auth: ["SECRET_CODES", ""]).
65
+ to_return(status: 401)
66
+ stub_request(:get, registry_url + "api/v1/dependencies").
67
+ with(basic_auth: ["SECRET_CODES", ""]).
68
+ to_return(status: 401)
69
+ stub_request(:get, registry_url + "specs.4.8.gz").
70
+ with(basic_auth: ["SECRET_CODES", ""]).
71
+ to_return(status: 401)
72
+ end
73
+
74
+ it "blows up with a useful error" do
75
+ error_class = Bundler::Fetcher::BadAuthenticationError
76
+ expect { private_registry_versions }.
77
+ to raise_error do |error|
78
+ expect(error).to be_a(error_class)
79
+ expect(error.message).to include("Bad username or password for")
80
+ end
81
+ end
82
+ end
83
+
84
+ context "that we have bad authentication details for" do
85
+ before do
86
+ stub_request(:get, registry_url + "versions").
87
+ with(basic_auth: ["SECRET_CODES", ""]).
88
+ to_return(status: 403)
89
+ stub_request(:get, registry_url + "api/v1/dependencies").
90
+ with(basic_auth: ["SECRET_CODES", ""]).
91
+ to_return(status: 403)
92
+ stub_request(:get, registry_url + "specs.4.8.gz").
93
+ with(basic_auth: ["SECRET_CODES", ""]).
94
+ to_return(status: 403)
95
+ end
96
+
97
+ it "blows up with a useful error" do
98
+ error_class = Bundler::Fetcher::BadAuthenticationError
99
+ expect { private_registry_versions }.
100
+ to raise_error do |error|
101
+ expect(error).to be_a(error_class)
102
+ expect(error.message).to include("Bad username or password for")
103
+ end
104
+ end
105
+ end
106
+
107
+ context "that bad-requested, but was a private repo" do
108
+ before do
109
+ stub_request(:get, registry_url + "versions").
110
+ with(basic_auth: ["SECRET_CODES", ""]).
111
+ to_return(status: 400)
112
+ stub_request(:get, registry_url + "api/v1/dependencies").
113
+ with(basic_auth: ["SECRET_CODES", ""]).
114
+ to_return(status: 400)
115
+ stub_request(:get, registry_url + "specs.4.8.gz").
116
+ with(basic_auth: ["SECRET_CODES", ""]).
117
+ to_return(status: 400)
118
+ end
119
+
120
+ it "blows up with a useful error" do
121
+ expect { private_registry_versions }.
122
+ to raise_error do |error|
123
+ expect(error).to be_a(Bundler::HTTPError)
124
+ expect(error.message).
125
+ to include("Could not fetch specs from")
126
+ end
127
+ end
128
+ end
129
+
130
+ context "that doesn't have details of the gem" do
131
+ before do
132
+ stub_request(:get, gemfury_business_url).
133
+ with(basic_auth: ["SECRET_CODES", ""]).
134
+ to_return(status: 404)
135
+
136
+ # Stub indexes to return details of other gems (but not this one)
137
+ stub_request(:get, registry_url + "specs.4.8.gz").
138
+ to_return(
139
+ status: 200,
140
+ body: fixture("ruby", "contribsys_old_index_response")
141
+ )
142
+ stub_request(:get, registry_url + "prerelease_specs.4.8.gz").
143
+ to_return(
144
+ status: 200,
145
+ body: fixture("ruby", "contribsys_old_index_prerelease_response")
146
+ )
147
+ end
148
+
149
+ it { is_expected.to be_empty }
150
+ end
151
+
152
+ context "that only implements the old Bundler index format..." do
153
+ let(:project_name) { "sidekiq_pro" }
154
+ let(:dependency_name) { "sidekiq-pro" }
155
+ let(:registry_url) { "https://gems.contribsys.com/" }
156
+
157
+ before do
158
+ stub_request(:get, registry_url + "versions").
159
+ with(basic_auth: %w(username password)).
160
+ to_return(status: 404)
161
+ stub_request(:get, registry_url + "api/v1/dependencies").
162
+ with(basic_auth: %w(username password)).
163
+ to_return(status: 404)
164
+ stub_request(:get, registry_url + "specs.4.8.gz").
165
+ with(basic_auth: %w(username password)).
166
+ to_return(
167
+ status: 200,
168
+ body: fixture("ruby", "contribsys_old_index_response")
169
+ )
170
+ stub_request(:get, registry_url + "prerelease_specs.4.8.gz").
171
+ with(basic_auth: %w(username password)).
172
+ to_return(
173
+ status: 200,
174
+ body: fixture("ruby", "contribsys_old_index_prerelease_response")
175
+ )
176
+ end
177
+
178
+ it "returns all versions from the private source" do
179
+ expect(private_registry_versions.length).to eql(70)
180
+ expect(private_registry_versions.min).to eql(Gem::Version.new("1.0.0"))
181
+ expect(private_registry_versions.max).to eql(Gem::Version.new("3.5.2"))
182
+ end
183
+ end
184
+ end
185
+ end