dependabot-common 0.95.1 → 0.95.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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