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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f9e379564416aff2841e1bccf1b420dd167eb77fb5647b3a1741d49a8d53a67
4
- data.tar.gz: 3dbbaae582caf3e6380842c6b4f893002eb5062819cd249c3a24e22dc5749b51
3
+ metadata.gz: 7c6c9dbd8b8a661f9e5ea65669d1e7b62c500962eca6f678694aaf453097a129
4
+ data.tar.gz: 1232449ea7f3a20430eed31c086c4f682f269405f7f3ed04ed7f49f387e3393e
5
5
  SHA512:
6
- metadata.gz: d5881f87654a931b002b2db3a1a128f43fd2f378acbaff69e77710cb81ed9133b5711bc98ee2bc27c2e56feb3eb71b3dc16e2b4fdd269c41c977734239522e95
7
- data.tar.gz: eb4c477200b043f59e4de9c6e468d71be947468ac7cf980d62c3828c1a528e1f25f4559c1f6fdf153fdf04208fe0c855c0cad5ed664d388b7eef3dcdaba3a9b0
6
+ metadata.gz: 4711f733fd5af8bce8ed4ea4c437b891f4bafd16eec7b1d5d4402a50cad9710feb052ef9afe88edf747c505f65a52f95bb5cc017a96b4796e25c270bec98704a
7
+ data.tar.gz: 7f16f6ebb4fc3712b8dcc91969986016f7dac9ccea85e21aef7b95396dbe19077ab2f55d565f25a89da67f5a9439fc7e058e2a2951e2f36a0252a50bdf20682e
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "parser/current"
5
+ require "dependabot/bundler/file_fetcher"
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Bundler
10
+ class FileFetcher
11
+ # Finds the paths of any Gemfiles declared using `eval_gemfile` in the
12
+ # passed Gemfile.
13
+ class ChildGemfileFinder
14
+ def initialize(gemfile:)
15
+ @gemfile = gemfile
16
+ end
17
+
18
+ def child_gemfile_paths
19
+ ast = Parser::CurrentRuby.parse(gemfile.content)
20
+ find_child_gemfile_paths(ast)
21
+ rescue Parser::SyntaxError
22
+ raise Dependabot::DependencyFileNotParseable, gemfile.path
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :gemfile
28
+
29
+ # rubocop:disable Security/Eval
30
+ def find_child_gemfile_paths(node)
31
+ return [] unless node.is_a?(Parser::AST::Node)
32
+
33
+ if declares_eval_gemfile?(node)
34
+ # We use eval here, but we know what we're doing. The FileFetchers
35
+ # helper method should only ever be run in an isolated environment
36
+ source = node.children[2].loc.expression.source
37
+ begin
38
+ path = eval(source)
39
+ rescue StandardError
40
+ return []
41
+ end
42
+ if Pathname.new(path).absolute?
43
+ base_path = Pathname.new(File.expand_path(Dir.pwd))
44
+ path = Pathname.new(path).relative_path_from(base_path).to_s
45
+ end
46
+ path = File.join(current_dir, path) unless current_dir.nil?
47
+ return [Pathname.new(path).cleanpath.to_path]
48
+ end
49
+
50
+ node.children.flat_map do |child_node|
51
+ find_child_gemfile_paths(child_node)
52
+ end
53
+ end
54
+ # rubocop:enable Security/Eval
55
+
56
+ def current_dir
57
+ @current_dir ||= gemfile.name.split("/")[0..-2].last
58
+ end
59
+
60
+ def declares_eval_gemfile?(node)
61
+ return false unless node.is_a?(Parser::AST::Node)
62
+
63
+ node.children[1] == :eval_gemfile
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "parser/current"
5
+ require "dependabot/bundler/file_fetcher"
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Bundler
10
+ class FileFetcher
11
+ # Finds the directories of any gemspecs declared using `gemspec` in the
12
+ # passed Gemfile.
13
+ class GemspecFinder
14
+ def initialize(gemfile:)
15
+ @gemfile = gemfile
16
+ end
17
+
18
+ def gemspec_directories
19
+ ast = Parser::CurrentRuby.parse(gemfile.content)
20
+ find_gemspec_paths(ast)
21
+ rescue Parser::SyntaxError
22
+ raise Dependabot::DependencyFileNotParseable, gemfile.path
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :gemfile
28
+
29
+ # rubocop:disable Security/Eval
30
+ def find_gemspec_paths(node)
31
+ return [] unless node.is_a?(Parser::AST::Node)
32
+
33
+ if declares_gemspec_dependency?(node)
34
+ path_node = path_node_for_gem_declaration(node)
35
+ return [clean_path(".")] unless path_node
36
+
37
+ begin
38
+ # We use eval here, but we know what we're doing. The
39
+ # FileFetchers helper method should only ever be run in an
40
+ # isolated environment
41
+ path = eval(path_node.loc.expression.source)
42
+ rescue StandardError
43
+ return []
44
+ end
45
+ return [clean_path(path)]
46
+ end
47
+
48
+ node.children.flat_map do |child_node|
49
+ find_gemspec_paths(child_node)
50
+ end
51
+ end
52
+ # rubocop:enable Security/Eval
53
+
54
+ def current_dir
55
+ @current_dir ||= gemfile.name.rpartition("/").first
56
+ @current_dir = nil if @current_dir == ""
57
+ @current_dir
58
+ end
59
+
60
+ def declares_gemspec_dependency?(node)
61
+ return false unless node.is_a?(Parser::AST::Node)
62
+
63
+ node.children[1] == :gemspec
64
+ end
65
+
66
+ def clean_path(path)
67
+ if Pathname.new(path).absolute?
68
+ base_path = Pathname.new(File.expand_path(Dir.pwd))
69
+ path = Pathname.new(path).relative_path_from(base_path).to_s
70
+ end
71
+ path = File.join(current_dir, path) unless current_dir.nil?
72
+ Pathname.new(path).cleanpath
73
+ end
74
+
75
+ def path_node_for_gem_declaration(node)
76
+ return unless node.children.last.is_a?(Parser::AST::Node)
77
+ return unless node.children.last.type == :hash
78
+
79
+ kwargs_node = node.children.last
80
+
81
+ path_hash_pair =
82
+ kwargs_node.children.
83
+ find { |hash_pair| key_from_hash_pair(hash_pair) == :path }
84
+
85
+ return unless path_hash_pair
86
+
87
+ path_hash_pair.children.last
88
+ end
89
+
90
+ def key_from_hash_pair(node)
91
+ node.children.first.children.first.to_sym
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "parser/current"
5
+ require "dependabot/bundler/file_fetcher"
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Bundler
10
+ class FileFetcher
11
+ # Finds the paths of any gemspecs declared using `path: ` in the
12
+ # passed Gemfile.
13
+ class PathGemspecFinder
14
+ def initialize(gemfile:)
15
+ @gemfile = gemfile
16
+ end
17
+
18
+ def path_gemspec_paths
19
+ ast = Parser::CurrentRuby.parse(gemfile.content)
20
+ find_path_gemspec_paths(ast)
21
+ rescue Parser::SyntaxError
22
+ raise Dependabot::DependencyFileNotParseable, gemfile.path
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :gemfile
28
+
29
+ # rubocop:disable Security/Eval
30
+ def find_path_gemspec_paths(node)
31
+ return [] unless node.is_a?(Parser::AST::Node)
32
+
33
+ if declares_path_dependency?(node)
34
+ path_node = path_node_for_gem_declaration(node)
35
+
36
+ begin
37
+ # We use eval here, but we know what we're doing. The
38
+ # FileFetchers helper method should only ever be run in an
39
+ # isolated environment
40
+ path = eval(path_node.loc.expression.source)
41
+ rescue StandardError
42
+ return []
43
+ end
44
+ return [clean_path(path)]
45
+ end
46
+
47
+ relevant_child_nodes(node).flat_map do |child_node|
48
+ find_path_gemspec_paths(child_node)
49
+ end
50
+ end
51
+ # rubocop:enable Security/Eval
52
+
53
+ def current_dir
54
+ @current_dir ||= gemfile.name.rpartition("/").first
55
+ @current_dir = nil if @current_dir == ""
56
+ @current_dir
57
+ end
58
+
59
+ def declares_path_dependency?(node)
60
+ return false unless node.is_a?(Parser::AST::Node)
61
+ return false unless node.children[1] == :gem
62
+
63
+ !path_node_for_gem_declaration(node).nil?
64
+ end
65
+
66
+ def clean_path(path)
67
+ if Pathname.new(path).absolute?
68
+ base_path = Pathname.new(File.expand_path(Dir.pwd))
69
+ path = Pathname.new(path).relative_path_from(base_path).to_s
70
+ end
71
+ path = File.join(current_dir, path) unless current_dir.nil?
72
+ Pathname.new(path).cleanpath
73
+ end
74
+
75
+ # rubocop:disable Security/Eval
76
+ def relevant_child_nodes(node)
77
+ return [] unless node.is_a?(Parser::AST::Node)
78
+ return node.children unless node.type == :if
79
+
80
+ begin
81
+ if eval(node.children.first.loc.expression.source)
82
+ [node.children[1]]
83
+ else
84
+ [node.children[2]]
85
+ end
86
+ rescue StandardError
87
+ return node.children
88
+ end
89
+ end
90
+ # rubocop:enable Security/Eval
91
+
92
+ def path_node_for_gem_declaration(node)
93
+ return unless node.children.last.type == :hash
94
+
95
+ kwargs_node = node.children.last
96
+
97
+ path_hash_pair =
98
+ kwargs_node.children.
99
+ find { |hash_pair| key_from_hash_pair(hash_pair) == :path }
100
+
101
+ return unless path_hash_pair
102
+
103
+ path_hash_pair.children.last
104
+ end
105
+
106
+ def key_from_hash_pair(node)
107
+ node.children.first.children.first.to_sym
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "parser/current"
5
+ require "dependabot/bundler/file_fetcher"
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Bundler
10
+ class FileFetcher
11
+ # Finds the paths of any files included using `require_relative` in the
12
+ # passed file.
13
+ class RequireRelativeFinder
14
+ def initialize(file:)
15
+ @file = file
16
+ end
17
+
18
+ def require_relative_paths
19
+ ast = Parser::CurrentRuby.parse(file.content)
20
+ find_require_relative_paths(ast)
21
+ rescue Parser::SyntaxError
22
+ raise Dependabot::DependencyFileNotParseable, file.path
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :file
28
+
29
+ # rubocop:disable Security/Eval
30
+ def find_require_relative_paths(node)
31
+ return [] unless node.is_a?(Parser::AST::Node)
32
+
33
+ if declares_require_relative?(node)
34
+ # We use eval here, but we know what we're doing. The FileFetchers
35
+ # helper method should only ever be run in an isolated environment
36
+ source = node.children[2].loc.expression.source
37
+ begin
38
+ path = eval(source)
39
+ rescue StandardError
40
+ return []
41
+ end
42
+
43
+ path = File.join(current_dir, path) unless current_dir.nil?
44
+ return [Pathname.new(path + ".rb").cleanpath.to_path]
45
+ end
46
+
47
+ node.children.flat_map do |child_node|
48
+ find_require_relative_paths(child_node)
49
+ end
50
+ end
51
+ # rubocop:enable Security/Eval
52
+
53
+ def current_dir
54
+ @current_dir ||= file.name.split("/")[0..-2].last
55
+ end
56
+
57
+ def declares_require_relative?(node)
58
+ return false unless node.is_a?(Parser::AST::Node)
59
+
60
+ node.children[1] == :require_relative
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/file_fetchers"
4
+ require "dependabot/file_fetchers/base"
5
+ require "dependabot/bundler/file_updater/lockfile_updater"
6
+ require "dependabot/errors"
7
+
8
+ module Dependabot
9
+ module Bundler
10
+ class FileFetcher < Dependabot::FileFetchers::Base
11
+ require "dependabot/bundler/file_fetcher/gemspec_finder"
12
+ require "dependabot/bundler/file_fetcher/path_gemspec_finder"
13
+ require "dependabot/bundler/file_fetcher/child_gemfile_finder"
14
+ require "dependabot/bundler/file_fetcher/require_relative_finder"
15
+
16
+ def self.required_files_in?(filenames)
17
+ if filenames.any? { |name| name.match?(%r{^[^/]*\.gemspec$}) }
18
+ return true
19
+ end
20
+
21
+ filenames.include?("Gemfile") || filenames.include?("gems.rb")
22
+ end
23
+
24
+ def self.required_files_message
25
+ "Repo must contain either a Gemfile, a gemspec, or a gems.rb."
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_files
31
+ fetched_files = []
32
+ fetched_files << gemfile if gemfile
33
+ fetched_files << lockfile if gemfile && lockfile
34
+ fetched_files += child_gemfiles
35
+ fetched_files += gemspecs
36
+ fetched_files << ruby_version_file if ruby_version_file
37
+ fetched_files += path_gemspecs
38
+ fetched_files += require_relative_files(fetched_files)
39
+
40
+ fetched_files = uniq_files(fetched_files)
41
+
42
+ check_required_files_present
43
+
44
+ unless self.class.required_files_in?(fetched_files.map(&:name))
45
+ raise "Invalid set of files: #{fetched_files.map(&:name)}"
46
+ end
47
+
48
+ fetched_files
49
+ end
50
+
51
+ def uniq_files(fetched_files)
52
+ uniq_files = fetched_files.reject(&:support_file?).uniq
53
+ uniq_files += fetched_files.
54
+ reject { |f| uniq_files.map(&:name).include?(f.name) }
55
+ end
56
+
57
+ def check_required_files_present
58
+ return if gemfile || gemspecs.any?
59
+
60
+ path = Pathname.new(File.join(directory, "Gemfile")).
61
+ cleanpath.to_path
62
+ raise Dependabot::DependencyFileNotFound, path
63
+ end
64
+
65
+ def gemfile
66
+ @gemfile ||= fetch_file_if_present("gems.rb") ||
67
+ fetch_file_if_present("Gemfile")
68
+ end
69
+
70
+ def lockfile
71
+ @lockfile ||= fetch_file_if_present("gems.locked") ||
72
+ fetch_file_if_present("Gemfile.lock")
73
+ end
74
+
75
+ def gemspecs
76
+ return @gemspecs if defined?(@gemspecs)
77
+
78
+ gemspecs_paths =
79
+ gemspec_directories.
80
+ flat_map do |d|
81
+ repo_contents(dir: d).
82
+ select { |f| f.name.end_with?(".gemspec") }.
83
+ map { |f| File.join(d, f.name) }
84
+ end
85
+
86
+ @gemspecs = gemspecs_paths.map { |n| fetch_file_from_host(n) }
87
+ rescue Octokit::NotFound
88
+ []
89
+ end
90
+
91
+ def gemspec_directories
92
+ gemfiles = ([gemfile] + child_gemfiles).compact
93
+ directories =
94
+ gemfiles.flat_map do |file|
95
+ GemspecFinder.new(gemfile: file).gemspec_directories
96
+ end.uniq
97
+
98
+ directories.empty? ? ["."] : directories
99
+ end
100
+
101
+ def ruby_version_file
102
+ return unless gemfile
103
+ return unless gemfile.content.include?(".ruby-version")
104
+
105
+ @ruby_version_file ||=
106
+ fetch_file_if_present(".ruby-version")&.
107
+ tap { |f| f.support_file = true }
108
+ end
109
+
110
+ def path_gemspecs
111
+ gemspec_files = []
112
+ unfetchable_gems = []
113
+
114
+ path_gemspec_paths.each do |path|
115
+ # Get any gemspecs at the path itself
116
+ gemspecs_at_path = fetch_gemspecs_from_directory(path)
117
+
118
+ # Get any gemspecs nested one level deeper
119
+ nested_directories =
120
+ repo_contents(dir: path).
121
+ select { |f| f.type == "dir" }
122
+
123
+ nested_directories.each do |dir|
124
+ dir_path = File.join(path, dir.name)
125
+ gemspecs_at_path += fetch_gemspecs_from_directory(dir_path)
126
+ end
127
+
128
+ # Add the fetched gemspecs to the main array, and note an error if
129
+ # none were found for this path
130
+ gemspec_files += gemspecs_at_path
131
+ unfetchable_gems << path.basename.to_s if gemspecs_at_path.empty?
132
+ rescue Octokit::NotFound, Gitlab::Error::NotFound
133
+ unfetchable_gems << path.basename.to_s
134
+ end
135
+
136
+ if unfetchable_gems.any?
137
+ raise Dependabot::PathDependenciesNotReachable, unfetchable_gems
138
+ end
139
+
140
+ gemspec_files.tap { |ar| ar.each { |f| f.support_file = true } }
141
+ end
142
+
143
+ def path_gemspec_paths
144
+ fetch_path_gemspec_paths.map { |path| Pathname.new(path) }
145
+ end
146
+
147
+ def require_relative_files(files)
148
+ ruby_files =
149
+ files.select { |f| f.name.end_with?(".rb", "Gemfile", ".gemspec") }
150
+
151
+ paths = ruby_files.flat_map do |file|
152
+ RequireRelativeFinder.new(file: file).require_relative_paths
153
+ end
154
+
155
+ @require_relative_files ||=
156
+ paths.map { |path| fetch_file_from_host(path) }.
157
+ tap { |req_files| req_files.each { |f| f.support_file = true } }
158
+ end
159
+
160
+ def fetch_gemspecs_from_directory(dir_path)
161
+ repo_contents(dir: dir_path, fetch_submodules: true).
162
+ select { |f| f.name.end_with?(".gemspec") }.
163
+ map { |f| File.join(dir_path, f.name) }.
164
+ map { |fp| fetch_file_from_host(fp, fetch_submodules: true) }
165
+ end
166
+
167
+ def fetch_path_gemspec_paths
168
+ if lockfile
169
+ parsed_lockfile = ::Bundler::LockfileParser.new(
170
+ sanitized_lockfile_content
171
+ )
172
+ parsed_lockfile.specs.
173
+ select { |s| s.source.instance_of?(::Bundler::Source::Path) }.
174
+ map { |s| s.source.path }.uniq
175
+ else
176
+ gemfiles = ([gemfile] + child_gemfiles).compact
177
+ gemfiles.flat_map do |file|
178
+ PathGemspecFinder.new(gemfile: file).path_gemspec_paths
179
+ end.uniq
180
+ end
181
+ rescue ::Bundler::LockfileError
182
+ raise Dependabot::DependencyFileNotParseable, lockfile.path
183
+ end
184
+
185
+ def child_gemfiles
186
+ return [] unless gemfile
187
+
188
+ @child_gemfiles ||=
189
+ fetch_child_gemfiles(file: gemfile, previously_fetched_files: [])
190
+ end
191
+
192
+ def sanitized_lockfile_content
193
+ regex = FileUpdater::LockfileUpdater::LOCKFILE_ENDING
194
+ lockfile.content.gsub(regex, "")
195
+ end
196
+
197
+ def fetch_child_gemfiles(file:, previously_fetched_files:)
198
+ paths = ChildGemfileFinder.new(gemfile: file).child_gemfile_paths
199
+
200
+ paths.flat_map do |path|
201
+ next if previously_fetched_files.map(&:name).include?(path)
202
+ next if file.name == path
203
+
204
+ fetched_file = fetch_file_from_host(path)
205
+ grandchild_gemfiles = fetch_child_gemfiles(
206
+ file: fetched_file,
207
+ previously_fetched_files: previously_fetched_files + [file]
208
+ )
209
+ [fetched_file, *grandchild_gemfiles]
210
+ end.compact
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ Dependabot::FileFetchers.register("bundler", Dependabot::Bundler::FileFetcher)
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/dependency_file"
4
+ require "dependabot/bundler/file_parser"
5
+ require "dependabot/bundler/file_updater/gemspec_sanitizer"
6
+
7
+ module Dependabot
8
+ module Bundler
9
+ class FileParser
10
+ class FilePreparer
11
+ def initialize(dependency_files:)
12
+ @dependency_files = dependency_files
13
+ end
14
+
15
+ def prepared_dependency_files
16
+ files = []
17
+
18
+ gemspecs.compact.each do |file|
19
+ files << DependencyFile.new(
20
+ name: file.name,
21
+ content: sanitize_gemspec_content(file.content),
22
+ directory: file.directory,
23
+ support_file: file.support_file?
24
+ )
25
+ end
26
+
27
+ files += [
28
+ gemfile,
29
+ *evaled_gemfiles,
30
+ lockfile,
31
+ ruby_version_file,
32
+ *imported_ruby_files
33
+ ].compact
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :dependency_files
39
+
40
+ def gemfile
41
+ dependency_files.find { |f| f.name == "Gemfile" } ||
42
+ dependency_files.find { |f| f.name == "gems.rb" }
43
+ end
44
+
45
+ def evaled_gemfiles
46
+ dependency_files.
47
+ reject { |f| f.name.end_with?(".gemspec") }.
48
+ reject { |f| f.name.end_with?(".lock") }.
49
+ reject { |f| f.name.end_with?(".ruby-version") }.
50
+ reject { |f| f.name == "Gemfile" }.
51
+ reject { |f| f.name == "gems.rb" }.
52
+ reject { |f| f.name == "gems.locked" }
53
+ end
54
+
55
+ def lockfile
56
+ dependency_files.find { |f| f.name == "Gemfile.lock" } ||
57
+ dependency_files.find { |f| f.name == "gems.locked" }
58
+ end
59
+
60
+ def gemspecs
61
+ dependency_files.select { |f| f.name.end_with?(".gemspec") }
62
+ end
63
+
64
+ def ruby_version_file
65
+ dependency_files.find { |f| f.name == ".ruby-version" }
66
+ end
67
+
68
+ def imported_ruby_files
69
+ dependency_files.
70
+ select { |f| f.name.end_with?(".rb") }.
71
+ reject { |f| f.name == "gems.rb" }
72
+ end
73
+
74
+ def sanitize_gemspec_content(gemspec_content)
75
+ # No need to set the version correctly - this is just an update
76
+ # check so we're not going to persist any changes to the lockfile.
77
+ FileUpdater::GemspecSanitizer.
78
+ new(replacement_version: "0.0.1").
79
+ rewrite(gemspec_content)
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parser/current"
4
+ require "dependabot/bundler/file_parser"
5
+
6
+ module Dependabot
7
+ module Bundler
8
+ class FileParser
9
+ # Checks whether a dependency is declared in a Gemfile
10
+ class GemfileChecker
11
+ def initialize(dependency:, gemfile:)
12
+ @dependency = dependency
13
+ @gemfile = gemfile
14
+ end
15
+
16
+ def includes_dependency?
17
+ return false unless Parser::CurrentRuby.parse(gemfile.content)
18
+
19
+ Parser::CurrentRuby.parse(gemfile.content).children.any? do |node|
20
+ deep_check_for_gem(node)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :dependency, :gemfile
27
+
28
+ def deep_check_for_gem(node)
29
+ return true if declares_targeted_gem?(node)
30
+ return false unless node.is_a?(Parser::AST::Node)
31
+
32
+ node.children.any? do |child_node|
33
+ deep_check_for_gem(child_node)
34
+ end
35
+ end
36
+
37
+ def declares_targeted_gem?(node)
38
+ return false unless node.is_a?(Parser::AST::Node)
39
+ return false unless node.children[1] == :gem
40
+
41
+ node.children[2].children.first == dependency.name
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end