dependabot-bundler 0.94.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/helpers/Makefile +9 -0
  3. data/helpers/build +26 -0
  4. data/lib/dependabot/bundler.rb +27 -0
  5. data/lib/dependabot/bundler/file_fetcher.rb +216 -0
  6. data/lib/dependabot/bundler/file_fetcher/child_gemfile_finder.rb +68 -0
  7. data/lib/dependabot/bundler/file_fetcher/gemspec_finder.rb +96 -0
  8. data/lib/dependabot/bundler/file_fetcher/path_gemspec_finder.rb +112 -0
  9. data/lib/dependabot/bundler/file_fetcher/require_relative_finder.rb +65 -0
  10. data/lib/dependabot/bundler/file_parser.rb +297 -0
  11. data/lib/dependabot/bundler/file_parser/file_preparer.rb +84 -0
  12. data/lib/dependabot/bundler/file_parser/gemfile_checker.rb +46 -0
  13. data/lib/dependabot/bundler/file_updater.rb +125 -0
  14. data/lib/dependabot/bundler/file_updater/gemfile_updater.rb +114 -0
  15. data/lib/dependabot/bundler/file_updater/gemspec_dependency_name_finder.rb +50 -0
  16. data/lib/dependabot/bundler/file_updater/gemspec_sanitizer.rb +296 -0
  17. data/lib/dependabot/bundler/file_updater/gemspec_updater.rb +62 -0
  18. data/lib/dependabot/bundler/file_updater/git_pin_replacer.rb +78 -0
  19. data/lib/dependabot/bundler/file_updater/git_source_remover.rb +100 -0
  20. data/lib/dependabot/bundler/file_updater/lockfile_updater.rb +387 -0
  21. data/lib/dependabot/bundler/file_updater/requirement_replacer.rb +221 -0
  22. data/lib/dependabot/bundler/metadata_finder.rb +204 -0
  23. data/lib/dependabot/bundler/requirement.rb +29 -0
  24. data/lib/dependabot/bundler/update_checker.rb +334 -0
  25. data/lib/dependabot/bundler/update_checker/file_preparer.rb +279 -0
  26. data/lib/dependabot/bundler/update_checker/force_updater.rb +259 -0
  27. data/lib/dependabot/bundler/update_checker/latest_version_finder.rb +165 -0
  28. data/lib/dependabot/bundler/update_checker/requirements_updater.rb +281 -0
  29. data/lib/dependabot/bundler/update_checker/ruby_requirement_setter.rb +113 -0
  30. data/lib/dependabot/bundler/update_checker/shared_bundler_helpers.rb +244 -0
  31. data/lib/dependabot/bundler/update_checker/version_resolver.rb +272 -0
  32. data/lib/dependabot/bundler/version.rb +13 -0
  33. metadata +200 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c2ec2bbf57a168323e957e8bcc8cee48ce9ad4bd05329e645d4e016483fd01f6
4
+ data.tar.gz: e80762d26ebbc3afc933fe46ab7a64fb1f88fd49822d9624d53a6bfca58bdee8
5
+ SHA512:
6
+ metadata.gz: 26a535914e74d584d32f7089c8c17d2aed2bf4dcf5ad3ebf29dcb3713fae24deb7e767b55ccd47caa7e9ab554c2e47ba1c22ccc77de4a70f76e413264ab107e8
7
+ data.tar.gz: 64e1dc779eb2a0ce63625d1462587e0cbc7d8d18f1e3012fefff304015806788176d789f692e576ff7165dfb410e2910ae9d5fa0dd1cd17dd118c40751442289
data/helpers/Makefile ADDED
@@ -0,0 +1,9 @@
1
+ .PHONY = all
2
+
3
+ all: darwin linux
4
+
5
+ darwin:
6
+ GO111MODULE=on GOOS=darwin GOARCH=amd64 go build -o go-helpers.darwin64 .
7
+
8
+ linux:
9
+ GO111MODULE=on GOOS=linux GOARCH=amd64 go build -o go-helpers.linux64 .
data/helpers/build ADDED
@@ -0,0 +1,26 @@
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
+ if ! [[ "$install_dir" =~ ^/ ]]; then
12
+ echo "$install_dir must be an absolute path"
13
+ exit 1
14
+ fi
15
+
16
+ if [ ! -d "$install_dir/bin" ]; then
17
+ mkdir -p "$install_dir/bin"
18
+ fi
19
+
20
+ helpers_dir="$(dirname "${BASH_SOURCE[0]}")"
21
+ cd $helpers_dir
22
+
23
+ os="$(uname -s | tr '[:upper:]' '[:lower:]')"
24
+ echo "building $install_dir/bin/helper"
25
+
26
+ GO111MODULE=on GOOS="$os" GOARCH=amd64 go build -o "$install_dir/bin/helper" .
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # These all need to be required so the various classes can be registered in a
4
+ # lookup table of package manager names to concrete classes.
5
+ require "dependabot/bundler/file_fetcher"
6
+ require "dependabot/bundler/file_parser"
7
+ require "dependabot/bundler/update_checker"
8
+ require "dependabot/bundler/file_updater"
9
+ require "dependabot/bundler/metadata_finder"
10
+ require "dependabot/bundler/requirement"
11
+ require "dependabot/bundler/version"
12
+
13
+ require "dependabot/pull_request_creator/labeler"
14
+ Dependabot::PullRequestCreator::Labeler.
15
+ register_label_details("bundler", name: "ruby", colour: "ce2d2d")
16
+
17
+ require "dependabot/dependency"
18
+ Dependabot::Dependency.register_production_check(
19
+ "bundler",
20
+ lambda do |groups|
21
+ return true if groups.empty?
22
+ return true if groups.include?("runtime")
23
+ return true if groups.include?("default")
24
+
25
+ groups.any? { |g| g.include?("prod") }
26
+ end
27
+ )
@@ -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).
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) }
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,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