dependabot-bundler 0.95.6 → 0.95.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot/bundler/file_fetcher/child_gemfile_finder.rb +68 -0
  3. data/lib/dependabot/bundler/file_fetcher/gemspec_finder.rb +96 -0
  4. data/lib/dependabot/bundler/file_fetcher/path_gemspec_finder.rb +112 -0
  5. data/lib/dependabot/bundler/file_fetcher/require_relative_finder.rb +65 -0
  6. data/lib/dependabot/bundler/file_fetcher.rb +216 -0
  7. data/lib/dependabot/bundler/file_parser/file_preparer.rb +84 -0
  8. data/lib/dependabot/bundler/file_parser/gemfile_checker.rb +46 -0
  9. data/lib/dependabot/bundler/file_parser.rb +297 -0
  10. data/lib/dependabot/bundler/file_updater/gemfile_updater.rb +114 -0
  11. data/lib/dependabot/bundler/file_updater/gemspec_dependency_name_finder.rb +50 -0
  12. data/lib/dependabot/bundler/file_updater/gemspec_sanitizer.rb +298 -0
  13. data/lib/dependabot/bundler/file_updater/gemspec_updater.rb +62 -0
  14. data/lib/dependabot/bundler/file_updater/git_pin_replacer.rb +78 -0
  15. data/lib/dependabot/bundler/file_updater/git_source_remover.rb +100 -0
  16. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +387 -0
  17. data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +221 -0
  18. data/lib/dependabot/bundler/file_updater.rb +125 -0
  19. data/lib/dependabot/bundler/metadata_finder.rb +204 -0
  20. data/lib/dependabot/bundler/requirement.rb +29 -0
  21. data/lib/dependabot/bundler/update_checker/file_preparer.rb +279 -0
  22. data/lib/dependabot/bundler/update_checker/force_updater.rb +259 -0
  23. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +165 -0
  24. data/lib/dependabot/bundler/update_checker/requirements_updater.rb +281 -0
  25. data/lib/dependabot/bundler/update_checker/ruby_requirement_setter.rb +113 -0
  26. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +244 -0
  27. data/lib/dependabot/bundler/update_checker/version_resolver.rb +272 -0
  28. data/lib/dependabot/bundler/update_checker.rb +334 -0
  29. data/lib/dependabot/bundler/version.rb +13 -0
  30. data/lib/dependabot/bundler.rb +27 -0
  31. data/lib/dependabot/monkey_patches/bundler/definition_bundler_version_patch.rb +15 -0
  32. data/lib/dependabot/monkey_patches/bundler/definition_ruby_version_patch.rb +14 -0
  33. data/lib/dependabot/monkey_patches/bundler/git_source_patch.rb +27 -0
  34. metadata +37 -5
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/file_updaters"
4
+ require "dependabot/file_updaters/base"
5
+
6
+ module Dependabot
7
+ module Bundler
8
+ class FileUpdater < Dependabot::FileUpdaters::Base
9
+ require_relative "file_updater/gemfile_updater"
10
+ require_relative "file_updater/gemspec_updater"
11
+ require_relative "file_updater/lockfile_updater"
12
+
13
+ def self.updated_files_regex
14
+ [
15
+ /^Gemfile$/,
16
+ /^Gemfile\.lock$/,
17
+ /^gems\.rb$/,
18
+ /^gems\.locked$/,
19
+ /^*\.gemspec$/
20
+ ]
21
+ end
22
+
23
+ def updated_dependency_files
24
+ updated_files = []
25
+
26
+ if gemfile && file_changed?(gemfile)
27
+ updated_files <<
28
+ updated_file(
29
+ file: gemfile,
30
+ content: updated_gemfile_content(gemfile)
31
+ )
32
+ end
33
+
34
+ if lockfile && dependencies.any?(&:appears_in_lockfile?)
35
+ updated_files <<
36
+ updated_file(file: lockfile, content: updated_lockfile_content)
37
+ end
38
+
39
+ top_level_gemspecs.each do |file|
40
+ next unless file_changed?(file)
41
+
42
+ updated_files <<
43
+ updated_file(file: file, content: updated_gemspec_content(file))
44
+ end
45
+
46
+ evaled_gemfiles.each do |file|
47
+ next unless file_changed?(file)
48
+
49
+ updated_files <<
50
+ updated_file(file: file, content: updated_gemfile_content(file))
51
+ end
52
+
53
+ updated_files
54
+ end
55
+
56
+ private
57
+
58
+ def check_required_files
59
+ file_names = dependency_files.map(&:name)
60
+
61
+ if lockfile && !gemfile
62
+ raise "A Gemfile must be provided if a lockfile is!"
63
+ end
64
+
65
+ return if file_names.any? { |name| name.match?(%r{^[^/]*\.gemspec$}) }
66
+ return if gemfile
67
+
68
+ raise "A gemspec or Gemfile must be provided!"
69
+ end
70
+
71
+ def gemfile
72
+ @gemfile ||= get_original_file("Gemfile") ||
73
+ get_original_file("gems.rb")
74
+ end
75
+
76
+ def lockfile
77
+ @lockfile ||= get_original_file("Gemfile.lock") ||
78
+ get_original_file("gems.locked")
79
+ end
80
+
81
+ def evaled_gemfiles
82
+ @evaled_gemfiles ||=
83
+ dependency_files.
84
+ reject { |f| f.name.end_with?(".gemspec") }.
85
+ reject { |f| f.name.end_with?(".lock") }.
86
+ reject { |f| f.name.end_with?(".ruby-version") }.
87
+ reject { |f| f.name == "Gemfile" }.
88
+ reject { |f| f.name == "gems.rb" }.
89
+ reject { |f| f.name == "gems.locked" }
90
+ end
91
+
92
+ def updated_gemfile_content(file)
93
+ GemfileUpdater.new(
94
+ dependencies: dependencies,
95
+ gemfile: file
96
+ ).updated_gemfile_content
97
+ end
98
+
99
+ def updated_gemspec_content(gemspec)
100
+ GemspecUpdater.new(
101
+ dependencies: dependencies,
102
+ gemspec: gemspec
103
+ ).updated_gemspec_content
104
+ end
105
+
106
+ def updated_lockfile_content
107
+ @updated_lockfile_content ||=
108
+ LockfileUpdater.new(
109
+ dependencies: dependencies,
110
+ dependency_files: dependency_files,
111
+ credentials: credentials
112
+ ).updated_lockfile_content
113
+ end
114
+
115
+ def top_level_gemspecs
116
+ dependency_files.
117
+ select { |file| file.name.end_with?(".gemspec") }.
118
+ reject(&:support_file?)
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ Dependabot::FileUpdaters.
125
+ register("bundler", Dependabot::Bundler::FileUpdater)
@@ -0,0 +1,204 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "excon"
4
+ require "dependabot/metadata_finders"
5
+ require "dependabot/metadata_finders/base"
6
+
7
+ module Dependabot
8
+ module Bundler
9
+ class MetadataFinder < Dependabot::MetadataFinders::Base
10
+ SOURCE_KEYS = %w(
11
+ source_code_uri
12
+ homepage_uri
13
+ wiki_uri
14
+ bug_tracker_uri
15
+ documentation_uri
16
+ changelog_uri
17
+ mailing_list_uri
18
+ download_uri
19
+ ).freeze
20
+
21
+ def homepage_url
22
+ return super unless %w(default rubygems).include?(new_source_type)
23
+ return super unless rubygems_api_response["homepage_uri"]
24
+
25
+ rubygems_api_response["homepage_uri"]
26
+ end
27
+
28
+ private
29
+
30
+ def look_up_source
31
+ case new_source_type
32
+ when "git" then find_source_from_git_url
33
+ when "default", "rubygems" then find_source_from_rubygems
34
+ else raise "Unexpected source type: #{new_source_type}"
35
+ end
36
+ end
37
+
38
+ def new_source_type
39
+ sources =
40
+ dependency.requirements.map { |r| r.fetch(:source) }.uniq.compact
41
+
42
+ return "default" if sources.empty?
43
+ raise "Multiple sources! #{sources.join(', ')}" if sources.count > 1
44
+
45
+ sources.first[:type] || sources.first.fetch("type")
46
+ end
47
+
48
+ def find_source_from_rubygems
49
+ api_source = find_source_from_rubygems_api_response
50
+ return api_source if api_source || new_source_type == "default"
51
+
52
+ find_source_from_gemspec_download
53
+ end
54
+
55
+ def find_source_from_rubygems_api_response
56
+ source_url = rubygems_api_response.
57
+ values_at(*SOURCE_KEYS).
58
+ compact.
59
+ find { |url| Source.from_url(url) }
60
+
61
+ Source.from_url(source_url)
62
+ end
63
+
64
+ def find_source_from_git_url
65
+ info = dependency.requirements.map { |r| r[:source] }.compact.first
66
+
67
+ url = info[:url] || info.fetch("url")
68
+ Source.from_url(url)
69
+ end
70
+
71
+ def find_source_from_gemspec_download
72
+ github_urls = []
73
+ return unless rubygems_marshalled_gemspec_response
74
+
75
+ rubygems_marshalled_gemspec_response.scan(Source::SOURCE_REGEX) do
76
+ github_urls << Regexp.last_match.to_s
77
+ end
78
+
79
+ source_url = github_urls.find do |url|
80
+ repo = Source.from_url(url).repo
81
+ repo.downcase.end_with?(dependency.name)
82
+ end
83
+ return unless source_url
84
+
85
+ Source.from_url(source_url)
86
+ end
87
+
88
+ # Note: This response MUST NOT be unmarshalled
89
+ # (as calling Marshal.load is unsafe)
90
+ def rubygems_marshalled_gemspec_response
91
+ if defined?(@rubygems_marshalled_gemspec_response)
92
+ return @rubygems_marshalled_gemspec_response
93
+ end
94
+
95
+ gemspec_uri =
96
+ "#{registry_url}quick/Marshal.4.8/"\
97
+ "#{dependency.name}-#{dependency.version}.gemspec.rz"
98
+
99
+ response =
100
+ Excon.get(
101
+ gemspec_uri,
102
+ headers: registry_auth_headers,
103
+ idempotent: true,
104
+ **SharedHelpers.excon_defaults
105
+ )
106
+
107
+ if response.status >= 400
108
+ return @rubygems_marshalled_gemspec_response = nil
109
+ end
110
+
111
+ @rubygems_marshalled_gemspec_response =
112
+ Zlib::Inflate.inflate(response.body)
113
+ rescue Zlib::DataError
114
+ @rubygems_marshalled_gemspec_response = nil
115
+ end
116
+
117
+ def rubygems_api_response
118
+ return @rubygems_api_response if defined?(@rubygems_api_response)
119
+
120
+ response =
121
+ Excon.get(
122
+ "#{registry_url}api/v1/gems/#{dependency.name}.json",
123
+ headers: registry_auth_headers,
124
+ idempotent: true,
125
+ **SharedHelpers.excon_defaults
126
+ )
127
+ return @rubygems_api_response = {} if response.status >= 400
128
+
129
+ response_body = response.body
130
+ response_body = augment_private_response_if_appropriate(response_body)
131
+
132
+ @rubygems_api_response = JSON.parse(response_body)
133
+ append_slash_to_source_code_uri(@rubygems_api_response)
134
+ rescue JSON::ParserError, Excon::Error::Timeout
135
+ @rubygems_api_response = {}
136
+ end
137
+
138
+ def append_slash_to_source_code_uri(listing)
139
+ # We have to do this so that `Source.from_url(...)` doesn't prune the
140
+ # last line off of the directory.
141
+ return listing unless listing&.fetch("source_code_uri", nil)
142
+ return listing if listing.fetch("source_code_uri").end_with?("/")
143
+
144
+ listing["source_code_uri"] = listing["source_code_uri"] + "/"
145
+ listing
146
+ end
147
+
148
+ def augment_private_response_if_appropriate(response_body)
149
+ return response_body if new_source_type == "default"
150
+
151
+ parsed_body = JSON.parse(response_body)
152
+ return response_body if (SOURCE_KEYS - parsed_body.keys).none?
153
+
154
+ digest = parsed_body.values_at("version", "authors", "info").hash
155
+
156
+ source_url = parsed_body.
157
+ values_at(*SOURCE_KEYS).
158
+ compact.
159
+ find { |url| Source.from_url(url) }
160
+ return response_body if source_url
161
+
162
+ rubygems_response =
163
+ Excon.get(
164
+ "https://rubygems.org/api/v1/gems/#{dependency.name}.json",
165
+ idempotent: true,
166
+ **SharedHelpers.excon_defaults
167
+ )
168
+ parsed_rubygems_body = JSON.parse(rubygems_response.body)
169
+ rubygems_digest =
170
+ parsed_rubygems_body.values_at("version", "authors", "info").hash
171
+
172
+ digest == rubygems_digest ? rubygems_response.body : response_body
173
+ rescue JSON::ParserError, Excon::Error::Socket, Excon::Error::Timeout
174
+ response_body
175
+ end
176
+
177
+ def registry_url
178
+ return "https://rubygems.org/" if new_source_type == "default"
179
+
180
+ info = dependency.requirements.map { |r| r[:source] }.compact.first
181
+ info[:url] || info.fetch("url")
182
+ end
183
+
184
+ def registry_auth_headers
185
+ return {} unless new_source_type == "rubygems"
186
+
187
+ token =
188
+ credentials.
189
+ select { |cred| cred["type"] == "rubygems_server" }.
190
+ find { |cred| registry_url.include?(cred["host"]) }&.
191
+ fetch("token")
192
+
193
+ return {} unless token
194
+
195
+ token += ":" unless token.include?(":")
196
+ encoded_token = Base64.encode64(token).delete("\n")
197
+ { "Authorization" => "Basic #{encoded_token}" }
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ Dependabot::MetadataFinders.
204
+ register("bundler", Dependabot::Bundler::MetadataFinder)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/utils"
4
+
5
+ module Dependabot
6
+ module Bundler
7
+ class Requirement < Gem::Requirement
8
+ # For consistency with other langauges, we define a requirements array.
9
+ # Ruby doesn't have an `OR` separator for requirements, so it always
10
+ # contains a single element.
11
+ def self.requirements_array(requirement_string)
12
+ [new(requirement_string)]
13
+ end
14
+
15
+ # Patches Gem::Requirement to make it accept requirement strings like
16
+ # "~> 4.2.5, >= 4.2.5.1" without first needing to split them.
17
+ def initialize(*requirements)
18
+ requirements = requirements.flatten.flat_map do |req_string|
19
+ req_string.split(",")
20
+ end
21
+
22
+ super(requirements)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ Dependabot::Utils.
29
+ register_requirement_class("bundler", Dependabot::Bundler::Requirement)
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/dependency_file"
4
+ require "dependabot/bundler/update_checker"
5
+ require "dependabot/bundler/file_updater/gemspec_sanitizer"
6
+ require "dependabot/bundler/file_updater/git_pin_replacer"
7
+ require "dependabot/bundler/file_updater/git_source_remover"
8
+ require "dependabot/bundler/file_updater/requirement_replacer"
9
+ require "dependabot/bundler/file_updater/gemspec_dependency_name_finder"
10
+ require "dependabot/bundler/file_updater/lockfile_updater"
11
+ require "dependabot/bundler/update_checker/ruby_requirement_setter"
12
+
13
+ module Dependabot
14
+ module Bundler
15
+ class UpdateChecker
16
+ # This class takes a set of dependency files and sanitizes them for use
17
+ # in UpdateCheckers::Ruby::Bundler. In particular, it:
18
+ # - Removes any version requirement on the dependency being updated
19
+ # (in the Gemfile)
20
+ # - Sanitizes any provided gemspecs to remove file imports etc. (since
21
+ # Dependabot doesn't pull down the entire repo). This process is
22
+ # imperfect - an alternative would be to clone the repo
23
+ # - Sets the ruby version in the Gemfile to be the lowest possible
24
+ # version allowed by the gemspec, if the gemspec has a required ruby
25
+ # version range
26
+ class FilePreparer
27
+ VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
28
+
29
+ # Can't be a constant because some of these don't exist in bundler
30
+ # 1.15, which Heroku uses, which causes an exception on boot.
31
+ def gemspec_sources
32
+ [
33
+ ::Bundler::Source::Path,
34
+ ::Bundler::Source::Gemspec
35
+ ]
36
+ end
37
+
38
+ def initialize(dependency_files:, dependency:,
39
+ remove_git_source: false,
40
+ unlock_requirement: true,
41
+ replacement_git_pin: nil,
42
+ latest_allowable_version: nil,
43
+ lock_ruby_version: true)
44
+ @dependency_files = dependency_files
45
+ @dependency = dependency
46
+ @remove_git_source = remove_git_source
47
+ @unlock_requirement = unlock_requirement
48
+ @replacement_git_pin = replacement_git_pin
49
+ @latest_allowable_version = latest_allowable_version
50
+ @lock_ruby_version = lock_ruby_version
51
+ end
52
+
53
+ # rubocop:disable Metrics/AbcSize
54
+ # rubocop:disable Metrics/MethodLength
55
+ def prepared_dependency_files
56
+ files = []
57
+
58
+ if gemfile
59
+ files << DependencyFile.new(
60
+ name: gemfile.name,
61
+ content: gemfile_content_for_update_check(gemfile),
62
+ directory: gemfile.directory
63
+ )
64
+ end
65
+
66
+ top_level_gemspecs.each do |gemspec|
67
+ files << DependencyFile.new(
68
+ name: gemspec.name,
69
+ content: gemspec_content_for_update_check(gemspec),
70
+ directory: gemspec.directory
71
+ )
72
+ end
73
+
74
+ path_gemspecs.each do |file|
75
+ files << DependencyFile.new(
76
+ name: file.name,
77
+ content: sanitize_gemspec_content(file.content),
78
+ directory: file.directory,
79
+ support_file: file.support_file?
80
+ )
81
+ end
82
+
83
+ evaled_gemfiles.each do |file|
84
+ files << DependencyFile.new(
85
+ name: file.name,
86
+ content: gemfile_content_for_update_check(file),
87
+ directory: file.directory
88
+ )
89
+ end
90
+
91
+ # No editing required for lockfile or Ruby version file
92
+ files += [lockfile, ruby_version_file, *imported_ruby_files].compact
93
+ end
94
+ # rubocop:enable Metrics/AbcSize
95
+ # rubocop:enable Metrics/MethodLength
96
+
97
+ private
98
+
99
+ attr_reader :dependency_files, :dependency, :replacement_git_pin,
100
+ :latest_allowable_version
101
+
102
+ def remove_git_source?
103
+ @remove_git_source
104
+ end
105
+
106
+ def unlock_requirement?
107
+ @unlock_requirement
108
+ end
109
+
110
+ def replace_git_pin?
111
+ !replacement_git_pin.nil?
112
+ end
113
+
114
+ def gemfile
115
+ dependency_files.find { |f| f.name == "Gemfile" } ||
116
+ dependency_files.find { |f| f.name == "gems.rb" }
117
+ end
118
+
119
+ def evaled_gemfiles
120
+ dependency_files.
121
+ reject { |f| f.name.end_with?(".gemspec") }.
122
+ reject { |f| f.name.end_with?(".lock") }.
123
+ reject { |f| f.name.end_with?(".ruby-version") }.
124
+ reject { |f| f.name == "Gemfile" }.
125
+ reject { |f| f.name == "gems.rb" }.
126
+ reject { |f| f.name == "gems.locked" }
127
+ end
128
+
129
+ def lockfile
130
+ dependency_files.find { |f| f.name == "Gemfile.lock" } ||
131
+ dependency_files.find { |f| f.name == "gems.locked" }
132
+ end
133
+
134
+ def top_level_gemspecs
135
+ dependency_files.
136
+ select { |f| f.name.end_with?(".gemspec") }.
137
+ reject(&:support_file?)
138
+ end
139
+
140
+ def ruby_version_file
141
+ dependency_files.find { |f| f.name == ".ruby-version" }
142
+ end
143
+
144
+ def path_gemspecs
145
+ all = dependency_files.select { |f| f.name.end_with?(".gemspec") }
146
+ all - top_level_gemspecs
147
+ end
148
+
149
+ def imported_ruby_files
150
+ dependency_files.
151
+ select { |f| f.name.end_with?(".rb") }.
152
+ reject { |f| f.name == "gems.rb" }
153
+ end
154
+
155
+ def gemfile_content_for_update_check(file)
156
+ content = file.content
157
+ content = replace_gemfile_constraint(content, file.name)
158
+ content = remove_git_source(content) if remove_git_source?
159
+ content = replace_git_pin(content) if replace_git_pin?
160
+ content = lock_ruby_version(content) if lock_ruby_version?(file)
161
+ content
162
+ end
163
+
164
+ def gemspec_content_for_update_check(gemspec)
165
+ content = gemspec.content
166
+ content = replace_gemspec_constraint(content, gemspec.name)
167
+ sanitize_gemspec_content(content)
168
+ end
169
+
170
+ def replace_gemfile_constraint(content, filename)
171
+ FileUpdater::RequirementReplacer.new(
172
+ dependency: dependency,
173
+ file_type: :gemfile,
174
+ updated_requirement: updated_version_requirement_string(filename),
175
+ insert_if_bare: true
176
+ ).rewrite(content)
177
+ end
178
+
179
+ def replace_gemspec_constraint(content, filename)
180
+ FileUpdater::RequirementReplacer.new(
181
+ dependency: dependency,
182
+ file_type: :gemspec,
183
+ updated_requirement: updated_version_requirement_string(filename),
184
+ insert_if_bare: true
185
+ ).rewrite(content)
186
+ end
187
+
188
+ def sanitize_gemspec_content(gemspec_content)
189
+ new_version = replacement_version_for_gemspec(gemspec_content)
190
+
191
+ FileUpdater::GemspecSanitizer.
192
+ new(replacement_version: new_version).
193
+ rewrite(gemspec_content)
194
+ end
195
+
196
+ def updated_version_requirement_string(filename)
197
+ lower_bound_req = updated_version_req_lower_bound(filename)
198
+
199
+ return lower_bound_req if latest_allowable_version.nil?
200
+ unless Gem::Version.correct?(latest_allowable_version)
201
+ return lower_bound_req
202
+ end
203
+
204
+ lower_bound_req + ", <= #{latest_allowable_version}"
205
+ end
206
+
207
+ def updated_version_req_lower_bound(filename)
208
+ original_req = dependency.requirements.
209
+ find { |r| r.fetch(:file) == filename }&.
210
+ fetch(:requirement)
211
+
212
+ if original_req && !unlock_requirement? then original_req
213
+ elsif dependency.version&.match?(/^[0-9a-f]{40}$/) then ">= 0"
214
+ elsif dependency.version then ">= #{dependency.version}"
215
+ else
216
+ version_for_requirement =
217
+ dependency.requirements.map { |r| r[:requirement] }.
218
+ reject { |req_string| req_string.start_with?("<") }.
219
+ select { |req_string| req_string.match?(VERSION_REGEX) }.
220
+ map { |req_string| req_string.match(VERSION_REGEX) }.
221
+ select { |version| Gem::Version.correct?(version) }.
222
+ max_by { |version| Gem::Version.new(version) }
223
+
224
+ ">= #{version_for_requirement || 0}"
225
+ end
226
+ end
227
+
228
+ def remove_git_source(content)
229
+ FileUpdater::GitSourceRemover.new(
230
+ dependency: dependency
231
+ ).rewrite(content)
232
+ end
233
+
234
+ def replace_git_pin(content)
235
+ FileUpdater::GitPinReplacer.new(
236
+ dependency: dependency,
237
+ new_pin: replacement_git_pin
238
+ ).rewrite(content)
239
+ end
240
+
241
+ def lock_ruby_version(gemfile_content)
242
+ top_level_gemspecs.each do |gs|
243
+ gemfile_content =
244
+ RubyRequirementSetter.new(gemspec: gs).rewrite(gemfile_content)
245
+ end
246
+
247
+ gemfile_content
248
+ end
249
+
250
+ def lock_ruby_version?(file)
251
+ @lock_ruby_version && file == gemfile
252
+ end
253
+
254
+ def replacement_version_for_gemspec(gemspec_content)
255
+ return "0.0.1" unless lockfile
256
+
257
+ gemspec_specs =
258
+ ::Bundler::LockfileParser.new(sanitized_lockfile_content).specs.
259
+ select { |s| gemspec_sources.include?(s.source.class) }
260
+
261
+ gem_name =
262
+ FileUpdater::GemspecDependencyNameFinder.
263
+ new(gemspec_content: gemspec_content).
264
+ dependency_name
265
+
266
+ return gemspec_specs.first&.version || "0.0.1" unless gem_name
267
+
268
+ spec = gemspec_specs.find { |s| s.name == gem_name }
269
+ spec&.version || gemspec_specs.first&.version || "0.0.1"
270
+ end
271
+
272
+ def sanitized_lockfile_content
273
+ re = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
274
+ lockfile.content.gsub(re, "")
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end