dependabot-common 0.95.1 → 0.95.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/dependabot.rb +4 -0
  3. data/lib/dependabot/clients/bitbucket.rb +105 -0
  4. data/lib/dependabot/clients/github_with_retries.rb +121 -0
  5. data/lib/dependabot/clients/gitlab.rb +72 -0
  6. data/lib/dependabot/dependency.rb +115 -0
  7. data/lib/dependabot/dependency_file.rb +60 -0
  8. data/lib/dependabot/errors.rb +179 -0
  9. data/lib/dependabot/file_fetchers.rb +18 -0
  10. data/lib/dependabot/file_fetchers/README.md +65 -0
  11. data/lib/dependabot/file_fetchers/base.rb +368 -0
  12. data/lib/dependabot/file_parsers.rb +18 -0
  13. data/lib/dependabot/file_parsers/README.md +45 -0
  14. data/lib/dependabot/file_parsers/base.rb +31 -0
  15. data/lib/dependabot/file_parsers/base/dependency_set.rb +77 -0
  16. data/lib/dependabot/file_updaters.rb +18 -0
  17. data/lib/dependabot/file_updaters/README.md +58 -0
  18. data/lib/dependabot/file_updaters/base.rb +52 -0
  19. data/lib/dependabot/git_commit_checker.rb +412 -0
  20. data/lib/dependabot/metadata_finders.rb +18 -0
  21. data/lib/dependabot/metadata_finders/README.md +53 -0
  22. data/lib/dependabot/metadata_finders/base.rb +117 -0
  23. data/lib/dependabot/metadata_finders/base/changelog_finder.rb +321 -0
  24. data/lib/dependabot/metadata_finders/base/changelog_pruner.rb +177 -0
  25. data/lib/dependabot/metadata_finders/base/commits_finder.rb +221 -0
  26. data/lib/dependabot/metadata_finders/base/release_finder.rb +255 -0
  27. data/lib/dependabot/pull_request_creator.rb +155 -0
  28. data/lib/dependabot/pull_request_creator/branch_namer.rb +170 -0
  29. data/lib/dependabot/pull_request_creator/commit_signer.rb +63 -0
  30. data/lib/dependabot/pull_request_creator/github.rb +277 -0
  31. data/lib/dependabot/pull_request_creator/gitlab.rb +162 -0
  32. data/lib/dependabot/pull_request_creator/labeler.rb +373 -0
  33. data/lib/dependabot/pull_request_creator/message_builder.rb +906 -0
  34. data/lib/dependabot/pull_request_updater.rb +43 -0
  35. data/lib/dependabot/pull_request_updater/github.rb +165 -0
  36. data/lib/dependabot/shared_helpers.rb +224 -0
  37. data/lib/dependabot/source.rb +120 -0
  38. data/lib/dependabot/update_checkers.rb +18 -0
  39. data/lib/dependabot/update_checkers/README.md +67 -0
  40. data/lib/dependabot/update_checkers/base.rb +220 -0
  41. data/lib/dependabot/utils.rb +33 -0
  42. data/lib/dependabot/version.rb +5 -0
  43. data/lib/rubygems_version_patch.rb +14 -0
  44. metadata +44 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5b58d3a338e6b53ed32caf20c73246343b473d893d8b9bfdcebec27a4952f28
4
- data.tar.gz: 6f0a68be8b3c8c23580023d0215c98fb459499478420876141cbb1ffb3a6a3d2
3
+ metadata.gz: 9f7232935dc073586ef8ecbb1ef3adfea19541485f74211cefd33c9025d30d01
4
+ data.tar.gz: e1516d883396351cdcc282a93f6b11c30c94128d9b176b698c46067772ca5fc7
5
5
  SHA512:
6
- metadata.gz: 97ae77d64fe1a218a570484b22d8a959bd83b7a7ae9e6664b3874e84f0f187b6e86af8a67398ffc54833d26d4a6c2f78b1f7608f393bbf1f550088dada571d59
7
- data.tar.gz: e9fba893c1a85c334142e7661c739dc602fc1d3e51352ff2a2443f424bd29e008b043a6ec25ba3f372467b1d06cf20f359b592350102c2397f39f76ca4953dc2
6
+ metadata.gz: 0b529b1074403b82ba80cfb22f99b3ac3e1231a99b96b54b86eb49974cf95e7ee95d3cf12e1d9d2bc6d7536713ec8510a8cb7c29063a0263f73d67687f0e7e9e
7
+ data.tar.gz: a0de54b828ee9958e0d6906a1cd09603f908460303dfb5eec3f55be4af48d44557edd80b666e40bde523687056f1e09a70dadef7bdd9edde354920646f1daacb
data/lib/dependabot.rb ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dependabot
4
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/shared_helpers"
4
+ require "excon"
5
+
6
+ module Dependabot
7
+ module Clients
8
+ class Bitbucket
9
+ class NotFound < StandardError; end
10
+ class Unauthorized < StandardError; end
11
+ class Forbidden < StandardError; end
12
+
13
+ #######################
14
+ # Constructor methods #
15
+ #######################
16
+
17
+ def self.for_bitbucket_dot_org(credentials:)
18
+ credential =
19
+ credentials.
20
+ select { |cred| cred["type"] == "git_source" }.
21
+ find { |cred| cred["host"] == "bitbucket.org" }
22
+
23
+ new(credential)
24
+ end
25
+
26
+ ##########
27
+ # Client #
28
+ ##########
29
+
30
+ def initialize(credentials)
31
+ @credentials = credentials
32
+ end
33
+
34
+ def fetch_commit(repo, branch)
35
+ path = "#{repo}/refs/branches/#{branch}"
36
+ response = get(base_url + path)
37
+
38
+ JSON.parse(response.body).fetch("target").fetch("hash")
39
+ end
40
+
41
+ def fetch_default_branch(repo)
42
+ response = get(base_url + repo)
43
+
44
+ JSON.parse(response.body).fetch("mainbranch").fetch("name")
45
+ end
46
+
47
+ def fetch_repo_contents(repo, commit = nil, path = nil)
48
+ raise "Commit is required if path provided!" if commit.nil? && path
49
+
50
+ api_path = "#{repo}/src"
51
+ api_path += "/#{commit}" if commit
52
+ api_path += "/#{path.gsub(%r{/+$}, '')}" if path
53
+ api_path += "?pagelen=100"
54
+ response = get(base_url + api_path)
55
+
56
+ JSON.parse(response.body).fetch("values")
57
+ end
58
+
59
+ def fetch_file_contents(repo, commit, path)
60
+ path = "#{repo}/src/#{commit}/#{path.gsub(%r{/+$}, '')}"
61
+ response = get(base_url + path)
62
+
63
+ response.body
64
+ end
65
+
66
+ def tags(repo)
67
+ path = "#{repo}/refs/tags?pagelen=100"
68
+ response = get(base_url + path)
69
+
70
+ JSON.parse(response.body).fetch("values")
71
+ end
72
+
73
+ def compare(repo, previous_tag, new_tag)
74
+ path = "#{repo}/commits/?include=#{new_tag}&exclude=#{previous_tag}"
75
+ response = get(base_url + path)
76
+
77
+ JSON.parse(response.body).fetch("values")
78
+ end
79
+
80
+ def get(url)
81
+ response = Excon.get(
82
+ url,
83
+ user: credentials&.fetch("username"),
84
+ password: credentials&.fetch("password"),
85
+ idempotent: true,
86
+ **Dependabot::SharedHelpers.excon_defaults
87
+ )
88
+ raise Unauthorized if response.status == 401
89
+ raise Forbidden if response.status == 403
90
+ raise NotFound if response.status == 404
91
+
92
+ response
93
+ end
94
+
95
+ private
96
+
97
+ attr_reader :credentials
98
+
99
+ def base_url
100
+ # TODO: Make this configurable when we support enterprise Bitbucket
101
+ "https://api.bitbucket.org/2.0/repositories/"
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "octokit"
4
+
5
+ module Dependabot
6
+ module Clients
7
+ class GithubWithRetries
8
+ DEFAULT_CLIENT_ARGS = {
9
+ connection_options: {
10
+ request: {
11
+ open_timeout: 2,
12
+ timeout: 5
13
+ }
14
+ }
15
+ }.freeze
16
+
17
+ RETRYABLE_ERRORS = [
18
+ Faraday::ConnectionFailed,
19
+ Faraday::TimeoutError,
20
+ Octokit::InternalServerError,
21
+ Octokit::BadGateway
22
+ ].freeze
23
+
24
+ #######################
25
+ # Constructor methods #
26
+ #######################
27
+
28
+ def self.for_source(source:, credentials:)
29
+ access_tokens =
30
+ credentials.
31
+ select { |cred| cred["type"] == "git_source" }.
32
+ select { |cred| cred["host"] == source.hostname }.
33
+ map { |cred| cred.fetch("password") }
34
+
35
+ new(
36
+ access_tokens: access_tokens,
37
+ api_endpoint: source.api_endpoint
38
+ )
39
+ end
40
+
41
+ def self.for_github_dot_com(credentials:)
42
+ access_tokens =
43
+ credentials.
44
+ select { |cred| cred["type"] == "git_source" }.
45
+ select { |cred| cred["host"] == "github.com" }.
46
+ map { |cred| cred.fetch("password") }
47
+
48
+ new(access_tokens: access_tokens)
49
+ end
50
+
51
+ #################
52
+ # VCS Interface #
53
+ #################
54
+
55
+ def fetch_commit(repo, branch)
56
+ response = ref(repo, "heads/#{branch}")
57
+
58
+ raise Octokit::NotFound if response.is_a?(Array)
59
+
60
+ response.object.sha
61
+ end
62
+
63
+ def fetch_default_branch(repo)
64
+ repository(repo).default_branch
65
+ end
66
+
67
+ ############
68
+ # Proxying #
69
+ ############
70
+
71
+ def initialize(max_retries: 3, **args)
72
+ args = DEFAULT_CLIENT_ARGS.merge(args)
73
+
74
+ access_tokens = args.delete(:access_tokens) || []
75
+ access_tokens << args[:access_token] if args[:access_token]
76
+ access_tokens << nil if access_tokens.empty?
77
+ access_tokens.uniq!
78
+
79
+ @max_retries = max_retries || 3
80
+ @clients = access_tokens.map do |token|
81
+ Octokit::Client.new(args.merge(access_token: token))
82
+ end
83
+ end
84
+
85
+ def method_missing(method_name, *args, &block)
86
+ untried_clients = @clients.dup
87
+ client = untried_clients.pop
88
+
89
+ begin
90
+ retry_connection_failures do
91
+ if client.respond_to?(method_name)
92
+ mutatable_args = args.map(&:dup)
93
+ client.public_send(method_name, *mutatable_args, &block)
94
+ else
95
+ super
96
+ end
97
+ end
98
+ rescue Octokit::NotFound, Octokit::Unauthorized, Octokit::Forbidden
99
+ raise unless (client = untried_clients.pop)
100
+
101
+ retry
102
+ end
103
+ end
104
+
105
+ def respond_to_missing?(method_name, include_private = false)
106
+ @clients.first.respond_to?(method_name) || super
107
+ end
108
+
109
+ def retry_connection_failures
110
+ retry_attempt = 0
111
+
112
+ begin
113
+ yield
114
+ rescue *RETRYABLE_ERRORS
115
+ retry_attempt += 1
116
+ retry_attempt <= @max_retries ? retry : raise
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gitlab"
4
+
5
+ module Dependabot
6
+ module Clients
7
+ class Gitlab
8
+ #######################
9
+ # Constructor methods #
10
+ #######################
11
+
12
+ def self.for_source(source:, credentials:)
13
+ access_token =
14
+ credentials.
15
+ select { |cred| cred["type"] == "git_source" }.
16
+ find { |cred| cred["host"] == source.hostname }&.
17
+ fetch("password")
18
+
19
+ new(
20
+ endpoint: source.api_endpoint,
21
+ private_token: access_token || ""
22
+ )
23
+ end
24
+
25
+ def self.for_gitlab_dot_com(credentials:)
26
+ access_token =
27
+ credentials.
28
+ select { |cred| cred["type"] == "git_source" }.
29
+ find { |cred| cred["host"] == "gitlab.com" }&.
30
+ fetch("password")
31
+
32
+ new(
33
+ endpoint: "https://gitlab.com/api/v4",
34
+ private_token: access_token || ""
35
+ )
36
+ end
37
+
38
+ #################
39
+ # VCS Interface #
40
+ #################
41
+
42
+ def fetch_commit(repo, branch)
43
+ branch(repo, branch).commit.id
44
+ end
45
+
46
+ def fetch_default_branch(repo)
47
+ project(repo).default_branch
48
+ end
49
+
50
+ ############
51
+ # Proxying #
52
+ ############
53
+
54
+ def initialize(**args)
55
+ @client = ::Gitlab::Client.new(args)
56
+ end
57
+
58
+ def method_missing(method_name, *args, &block)
59
+ if @client.respond_to?(method_name)
60
+ mutatable_args = args.map(&:dup)
61
+ @client.public_send(method_name, *mutatable_args, &block)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ def respond_to_missing?(method_name, include_private = false)
68
+ @client.respond_to?(method_name) || super
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems_version_patch"
4
+
5
+ module Dependabot
6
+ class Dependency
7
+ @production_checks = {}
8
+
9
+ def self.production_check_for_package_manager(package_manager)
10
+ production_check = @production_checks[package_manager]
11
+ return production_check if production_check
12
+
13
+ raise "Unsupported package_manager #{package_manager}"
14
+ end
15
+
16
+ def self.register_production_check(package_manager, production_check)
17
+ @production_checks[package_manager] = production_check
18
+ end
19
+
20
+ attr_reader :name, :version, :requirements, :package_manager,
21
+ :previous_version, :previous_requirements
22
+
23
+ def initialize(name:, requirements:, package_manager:, version: nil,
24
+ previous_version: nil, previous_requirements: nil)
25
+ @name = name
26
+ @version = version
27
+ @requirements = requirements.map { |req| symbolize_keys(req) }
28
+ @previous_version = previous_version
29
+ @previous_requirements =
30
+ previous_requirements&.map { |req| symbolize_keys(req) }
31
+ @package_manager = package_manager
32
+
33
+ check_values
34
+ end
35
+
36
+ def top_level?
37
+ requirements.any?
38
+ end
39
+
40
+ def to_h
41
+ {
42
+ "name" => name,
43
+ "version" => version,
44
+ "requirements" => requirements,
45
+ "previous_version" => previous_version,
46
+ "previous_requirements" => previous_requirements,
47
+ "package_manager" => package_manager
48
+ }
49
+ end
50
+
51
+ def appears_in_lockfile?
52
+ previous_version || (version && previous_requirements.nil?)
53
+ end
54
+
55
+ def production?
56
+ return true unless top_level?
57
+
58
+ groups = requirements.flat_map { |r| r.fetch(:groups).map(&:to_s) }
59
+
60
+ self.class.
61
+ production_check_for_package_manager(package_manager).
62
+ call(groups)
63
+ end
64
+
65
+ def display_name
66
+ return name unless %w(maven gradle).include?(package_manager)
67
+
68
+ name.split(":").last
69
+ end
70
+
71
+ def ==(other)
72
+ other.instance_of?(self.class) && to_h == other.to_h
73
+ end
74
+
75
+ def hash
76
+ to_h.hash
77
+ end
78
+
79
+ def eql?(other)
80
+ self.==(other)
81
+ end
82
+
83
+ private
84
+
85
+ def check_values
86
+ if [version, previous_version].any? { |v| v == "" }
87
+ raise ArgumentError, "blank strings must not be provided as versions"
88
+ end
89
+
90
+ requirement_fields = [requirements, previous_requirements].compact
91
+ unless requirement_fields.all? { |r| r.is_a?(Array) } &&
92
+ requirement_fields.flatten.all? { |r| r.is_a?(Hash) }
93
+ raise ArgumentError, "requirements must be an array of hashes"
94
+ end
95
+
96
+ required_keys = %i(requirement file groups source)
97
+ optional_keys = %i(metadata)
98
+ unless requirement_fields.flatten.
99
+ all? { |r| required_keys.sort == (r.keys - optional_keys).sort }
100
+ raise ArgumentError, "each requirement must have the following "\
101
+ "required keys: #{required_keys.join(', ')}."\
102
+ "Optionally, it may have the following keys: "\
103
+ "#{optional_keys.join(', ')}."
104
+ end
105
+
106
+ return if requirement_fields.flatten.none? { |r| r[:requirement] == "" }
107
+
108
+ raise ArgumentError, "blank strings must not be provided as requirements"
109
+ end
110
+
111
+ def symbolize_keys(hash)
112
+ Hash[hash.keys.map { |k| [k.to_sym, hash[k]] }]
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module Dependabot
6
+ class DependencyFile
7
+ attr_accessor :name, :content, :directory, :type, :support_file
8
+
9
+ def initialize(name:, content:, directory: "/", type: "file",
10
+ support_file: false)
11
+ @name = name
12
+ @content = content
13
+ @directory = clean_directory(directory)
14
+ @support_file = support_file
15
+
16
+ # Type is used *very* sparingly. It lets the git_modules updater know that
17
+ # a "file" is actually a submodule, and lets our Go updaters know which
18
+ # file represents the main.go.
19
+ # New use cases should be avoided if at all possible (and use the
20
+ # support_file flag instead)
21
+ @type = type
22
+ end
23
+
24
+ def to_h
25
+ {
26
+ "name" => name,
27
+ "content" => content,
28
+ "directory" => directory,
29
+ "type" => type
30
+ }
31
+ end
32
+
33
+ def path
34
+ Pathname.new(File.join(directory, name)).cleanpath.to_path
35
+ end
36
+
37
+ def ==(other)
38
+ other.instance_of?(self.class) && to_h == other.to_h
39
+ end
40
+
41
+ def hash
42
+ to_h.hash
43
+ end
44
+
45
+ def eql?(other)
46
+ self.==(other)
47
+ end
48
+
49
+ def support_file?
50
+ @support_file
51
+ end
52
+
53
+ private
54
+
55
+ def clean_directory(directory)
56
+ # Directory should always start with a `/`
57
+ directory.sub(%r{^/*}, "/")
58
+ end
59
+ end
60
+ end