dependabot-core 0.78.0 → 0.79.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/helpers/npm/lib/updater.js +11 -5
  4. data/helpers/npm/package.json +2 -2
  5. data/helpers/npm/yarn.lock +26 -28
  6. data/helpers/yarn/lib/replace-lockfile-declaration.js +15 -3
  7. data/helpers/yarn/lib/updater.js +17 -5
  8. data/helpers/yarn/package.json +2 -2
  9. data/helpers/yarn/yarn.lock +24 -31
  10. data/lib/dependabot/file_fetchers.rb +0 -2
  11. data/lib/dependabot/file_parsers.rb +0 -2
  12. data/lib/dependabot/file_updaters.rb +0 -2
  13. data/lib/dependabot/metadata_finders.rb +0 -2
  14. data/lib/dependabot/update_checkers.rb +0 -2
  15. data/lib/dependabot/utils.rb +0 -4
  16. data/lib/dependabot/version.rb +1 -1
  17. metadata +3 -34
  18. data/helpers/python/lib/__init__.py +0 -0
  19. data/helpers/python/lib/hasher.py +0 -23
  20. data/helpers/python/lib/parser.py +0 -130
  21. data/helpers/python/requirements.txt +0 -9
  22. data/helpers/python/run.py +0 -18
  23. data/lib/dependabot/file_fetchers/python/pip.rb +0 -305
  24. data/lib/dependabot/file_parsers/python/pip.rb +0 -223
  25. data/lib/dependabot/file_parsers/python/pip/pipfile_files_parser.rb +0 -154
  26. data/lib/dependabot/file_parsers/python/pip/poetry_files_parser.rb +0 -141
  27. data/lib/dependabot/file_parsers/python/pip/setup_file_parser.rb +0 -164
  28. data/lib/dependabot/file_updaters/python/pip.rb +0 -147
  29. data/lib/dependabot/file_updaters/python/pip/pip_compile_file_updater.rb +0 -363
  30. data/lib/dependabot/file_updaters/python/pip/pipfile_file_updater.rb +0 -397
  31. data/lib/dependabot/file_updaters/python/pip/pipfile_preparer.rb +0 -125
  32. data/lib/dependabot/file_updaters/python/pip/poetry_file_updater.rb +0 -289
  33. data/lib/dependabot/file_updaters/python/pip/pyproject_preparer.rb +0 -105
  34. data/lib/dependabot/file_updaters/python/pip/requirement_file_updater.rb +0 -166
  35. data/lib/dependabot/file_updaters/python/pip/requirement_replacer.rb +0 -95
  36. data/lib/dependabot/file_updaters/python/pip/setup_file_sanitizer.rb +0 -91
  37. data/lib/dependabot/file_updaters/ruby/.DS_Store +0 -0
  38. data/lib/dependabot/metadata_finders/python/pip.rb +0 -120
  39. data/lib/dependabot/update_checkers/python/pip.rb +0 -227
  40. data/lib/dependabot/update_checkers/python/pip/latest_version_finder.rb +0 -252
  41. data/lib/dependabot/update_checkers/python/pip/pip_compile_version_resolver.rb +0 -380
  42. data/lib/dependabot/update_checkers/python/pip/pipfile_version_resolver.rb +0 -559
  43. data/lib/dependabot/update_checkers/python/pip/poetry_version_resolver.rb +0 -300
  44. data/lib/dependabot/update_checkers/python/pip/requirements_updater.rb +0 -367
  45. data/lib/dependabot/utils/python/requirement.rb +0 -130
  46. data/lib/dependabot/utils/python/version.rb +0 -88
  47. data/lib/python_requirement_parser.rb +0 -33
  48. data/lib/python_versions.rb +0 -21
@@ -1,95 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "python_requirement_parser"
4
- require "dependabot/file_updaters/python/pip"
5
- require "dependabot/shared_helpers"
6
-
7
- module Dependabot
8
- module FileUpdaters
9
- module Python
10
- class Pip
11
- class RequirementReplacer
12
- attr_reader :content, :dependency_name, :old_requirement,
13
- :new_requirement
14
-
15
- def initialize(content:, dependency_name:, old_requirement:,
16
- new_requirement:)
17
- @content = content
18
- @dependency_name = dependency_name
19
- @old_requirement = old_requirement
20
- @new_requirement = new_requirement
21
- end
22
-
23
- def updated_content
24
- updated_content =
25
- content.gsub(original_declaration_replacement_regex) do |mtch|
26
- # If the "declaration" is setting an option (e.g., no-binary)
27
- # ignore it, since it isn't actually a declaration
28
- next mtch if Regexp.last_match.pre_match.match?(/--.*\z/)
29
-
30
- updated_dependency_declaration_string(
31
- old_requirement,
32
- new_requirement
33
- )
34
- end
35
-
36
- raise "Expected content to change!" if content == updated_content
37
-
38
- updated_content
39
- end
40
-
41
- private
42
-
43
- def original_dependency_declaration_string(old_req)
44
- matches = []
45
-
46
- dec =
47
- if old_req.nil?
48
- regex = PythonRequirementParser::INSTALL_REQ_WITHOUT_REQUIREMENT
49
- content.scan(regex) { matches << Regexp.last_match }
50
- matches.find { |m| normalise(m[:name]) == dependency_name }
51
- else
52
- regex = PythonRequirementParser::INSTALL_REQ_WITH_REQUIREMENT
53
- content.scan(regex) { matches << Regexp.last_match }
54
- matches.
55
- select { |m| normalise(m[:name]) == dependency_name }.
56
- find { |m| requirements_match(m[:requirements], old_req) }
57
- end
58
-
59
- raise "Declaration not found for #{dependency_name}!" unless dec
60
-
61
- dec.to_s.strip
62
- end
63
-
64
- def updated_dependency_declaration_string(old_req, new_req)
65
- if old_req
66
- original_dependency_declaration_string(old_req).
67
- sub(PythonRequirementParser::REQUIREMENTS, new_req)
68
- else
69
- original_dependency_declaration_string(old_req).
70
- sub(PythonRequirementParser::NAME_WITH_EXTRAS) do |nm|
71
- nm + new_req
72
- end
73
- end
74
- end
75
-
76
- def original_declaration_replacement_regex
77
- original_string =
78
- original_dependency_declaration_string(old_requirement)
79
- /(?<![\-\w\.])#{Regexp.escape(original_string)}(?![\-\w\.])/
80
- end
81
-
82
- # See https://www.python.org/dev/peps/pep-0503/#normalized-names
83
- def normalise(name)
84
- name.downcase.gsub(/[-_.]+/, "-")
85
- end
86
-
87
- def requirements_match(req1, req2)
88
- req1&.split(",")&.map { |r| r.gsub(/\s/, "") }&.sort ==
89
- req2&.split(",")&.map { |r| r.gsub(/\s/, "") }&.sort
90
- end
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,91 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "dependabot/file_updaters/python/pip"
4
- require "dependabot/file_parsers/python/pip/setup_file_parser"
5
-
6
- module Dependabot
7
- module FileUpdaters
8
- module Python
9
- class Pip
10
- # Take a setup.py, parses it (carefully!) and then create a new, clean
11
- # setup.py using only the information which will appear in the lockfile.
12
- class SetupFileSanitizer
13
- def initialize(setup_file:, setup_cfg:)
14
- @setup_file = setup_file
15
- @setup_cfg = setup_cfg
16
- end
17
-
18
- def sanitized_content
19
- # The part of the setup.py that Pipenv cares about appears to be the
20
- # install_requires. A name and version are required by don't end up
21
- # in the lockfile.
22
- content =
23
- "from setuptools import setup\n\n"\
24
- "setup(name=\"sanitized-package\",version=\"0.0.1\","\
25
- "install_requires=#{install_requires_array.to_json},"\
26
- "extras_require=#{extras_require_hash.to_json}"
27
-
28
- content += ',setup_requires=["pbr"],pbr=True' if include_pbr?
29
- content + ")"
30
- end
31
-
32
- private
33
-
34
- attr_reader :setup_file, :setup_cfg
35
-
36
- def include_pbr?
37
- setup_requires_array.any? { |d| d.start_with?("pbr") }
38
- end
39
-
40
- def install_requires_array
41
- @install_requires_array ||=
42
- parsed_setup_file.dependencies.map do |dep|
43
- next unless dep.requirements.first[:groups].
44
- include?("install_requires")
45
-
46
- dep.name + dep.requirements.first[:requirement].to_s
47
- end.compact
48
- end
49
-
50
- def setup_requires_array
51
- @setup_requires_array ||=
52
- parsed_setup_file.dependencies.map do |dep|
53
- next unless dep.requirements.first[:groups].
54
- include?("setup_requires")
55
-
56
- dep.name + dep.requirements.first[:requirement].to_s
57
- end.compact
58
- end
59
-
60
- def extras_require_hash
61
- @extras_require_hash ||=
62
- begin
63
- hash = {}
64
- parsed_setup_file.dependencies.each do |dep|
65
- dep.requirements.first[:groups].each do |group|
66
- next unless group.start_with?("extras_require:")
67
-
68
- hash[group.split(":").last] ||= []
69
- hash[group.split(":").last] <<
70
- dep.name + dep.requirements.first[:requirement].to_s
71
- end
72
- end
73
-
74
- hash
75
- end
76
- end
77
-
78
- def parsed_setup_file
79
- @parsed_setup_file ||=
80
- FileParsers::Python::Pip::SetupFileParser.new(
81
- dependency_files: [
82
- setup_file&.dup&.tap { |f| f.name = "setup.py" },
83
- setup_cfg&.dup&.tap { |f| f.name = "setup.cfg" }
84
- ].compact
85
- ).dependency_set
86
- end
87
- end
88
- end
89
- end
90
- end
91
- end
@@ -1,120 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "excon"
4
- require "dependabot/metadata_finders/base"
5
- require "dependabot/shared_helpers"
6
-
7
- module Dependabot
8
- module MetadataFinders
9
- module Python
10
- class Pip < Dependabot::MetadataFinders::Base
11
- MAIN_PYPI_URL = "https://pypi.org/pypi"
12
-
13
- def homepage_url
14
- pypi_listing.dig("info", "home_page") || super
15
- end
16
-
17
- private
18
-
19
- def look_up_source
20
- potential_source_urls = [
21
- pypi_listing.dig("info", "home_page"),
22
- pypi_listing.dig("info", "bugtrack_url"),
23
- pypi_listing.dig("info", "download_url"),
24
- pypi_listing.dig("info", "docs_url")
25
- ].compact
26
-
27
- source_url = potential_source_urls.find { |url| Source.from_url(url) }
28
- source_url ||= source_from_description
29
- source_url ||= source_from_homepage
30
-
31
- Source.from_url(source_url)
32
- end
33
-
34
- def source_from_description
35
- github_urls = []
36
- desc = pypi_listing.dig("info", "description")
37
- return unless desc
38
-
39
- desc.scan(Source::SOURCE_REGEX) do
40
- github_urls << Regexp.last_match.to_s
41
- end
42
-
43
- github_urls.find do |url|
44
- repo = Source.from_url(url).repo
45
- repo.downcase.end_with?(dependency.name)
46
- end
47
- end
48
-
49
- def source_from_homepage
50
- return unless homepage_body
51
-
52
- github_urls = []
53
- homepage_body.scan(Source::SOURCE_REGEX) do
54
- github_urls << Regexp.last_match.to_s
55
- end
56
-
57
- github_urls.find do |url|
58
- repo = Source.from_url(url).repo
59
- repo.downcase.end_with?(dependency.name)
60
- end
61
- end
62
-
63
- def homepage_body
64
- homepage_url = pypi_listing.dig("info", "home_page")
65
-
66
- return unless homepage_url
67
- return if homepage_url.include?("pypi.python.org")
68
- return if homepage_url.include?("pypi.org")
69
-
70
- @homepage_response ||=
71
- begin
72
- Excon.get(
73
- homepage_url,
74
- idempotent: true,
75
- **SharedHelpers.excon_defaults
76
- )
77
- rescue Excon::Error::Timeout, Excon::Error::Socket, ArgumentError
78
- nil
79
- end
80
-
81
- return unless @homepage_response&.status == 200
82
-
83
- @homepage_response.body
84
- end
85
-
86
- def pypi_listing
87
- return @pypi_listing unless @pypi_listing.nil?
88
- return @pypi_listing = {} if dependency.version.include?("+")
89
-
90
- possible_listing_urls.each do |url|
91
- response = Excon.get(
92
- url,
93
- idempotent: true,
94
- **SharedHelpers.excon_defaults
95
- )
96
- next unless response.status == 200
97
-
98
- @pypi_listing = JSON.parse(response.body)
99
- return @pypi_listing
100
- rescue JSON::ParserError
101
- next
102
- end
103
-
104
- @pypi_listing = {} # No listing found
105
- end
106
-
107
- def possible_listing_urls
108
- credential_urls =
109
- credentials.
110
- select { |cred| cred["type"] == "python_index" }.
111
- map { |cred| cred["index-url"].gsub(%r{/$}, "") }
112
-
113
- (credential_urls + [MAIN_PYPI_URL]).map do |base_url|
114
- base_url + "/#{dependency.name}/json"
115
- end
116
- end
117
- end
118
- end
119
- end
120
- end
@@ -1,227 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "excon"
4
- require "toml-rb"
5
-
6
- require "python_requirement_parser"
7
- require "dependabot/update_checkers/base"
8
- require "dependabot/shared_helpers"
9
- require "dependabot/utils/python/requirement"
10
-
11
- module Dependabot
12
- module UpdateCheckers
13
- module Python
14
- class Pip < Dependabot::UpdateCheckers::Base
15
- require_relative "pip/poetry_version_resolver"
16
- require_relative "pip/pipfile_version_resolver"
17
- require_relative "pip/pip_compile_version_resolver"
18
- require_relative "pip/requirements_updater"
19
- require_relative "pip/latest_version_finder"
20
-
21
- MAIN_PYPI_INDEXES = %w(
22
- https://pypi.python.org/simple/
23
- https://pypi.org/simple/
24
- ).freeze
25
-
26
- def latest_version
27
- @latest_version ||= fetch_latest_version
28
- end
29
-
30
- def latest_resolvable_version
31
- @latest_resolvable_version ||=
32
- case resolver_type
33
- when :pipfile
34
- PipfileVersionResolver.new(
35
- resolver_args.merge(unlock_requirement: true)
36
- ).latest_resolvable_version
37
- when :poetry
38
- PoetryVersionResolver.new(
39
- resolver_args.merge(unlock_requirement: true)
40
- ).latest_resolvable_version
41
- when :pip_compile
42
- PipCompileVersionResolver.new(
43
- resolver_args.merge(unlock_requirement: true)
44
- ).latest_resolvable_version
45
- when :requirements
46
- # pip doesn't (yet) do any dependency resolution, so if we don't
47
- # have a Pipfile or a pip-compile file, we just return the latest
48
- # version.
49
- latest_version
50
- else raise "Unexpected resolver type #{resolver_type}"
51
- end
52
- end
53
-
54
- def latest_resolvable_version_with_no_unlock
55
- @latest_resolvable_version_with_no_unlock ||=
56
- case resolver_type
57
- when :pipfile
58
- PipfileVersionResolver.new(
59
- resolver_args.merge(unlock_requirement: false)
60
- ).latest_resolvable_version
61
- when :poetry
62
- PoetryVersionResolver.new(
63
- resolver_args.merge(unlock_requirement: false)
64
- ).latest_resolvable_version
65
- when :pip_compile
66
- PipCompileVersionResolver.new(
67
- resolver_args.merge(unlock_requirement: false)
68
- ).latest_resolvable_version
69
- when :requirements
70
- latest_pip_version_with_no_unlock
71
- else raise "Unexpected resolver type #{resolver_type}"
72
- end
73
- end
74
-
75
- def updated_requirements
76
- RequirementsUpdater.new(
77
- requirements: dependency.requirements,
78
- latest_version: latest_version&.to_s,
79
- latest_resolvable_version: latest_resolvable_version&.to_s,
80
- update_strategy: requirements_update_strategy,
81
- has_lockfile: !(pipfile_lock || poetry_lock || pyproject_lock).nil?
82
- ).updated_requirements
83
- end
84
-
85
- def requirements_update_strategy
86
- # If passed in as an option (in the base class) honour that option
87
- if @requirements_update_strategy
88
- return @requirements_update_strategy.to_sym
89
- end
90
-
91
- # Otherwise, check if this is a poetry library or not
92
- poetry_library? ? :widen_ranges : :bump_versions
93
- end
94
-
95
- private
96
-
97
- def latest_version_resolvable_with_full_unlock?
98
- # Full unlock checks aren't implemented for pip because they're not
99
- # relevant (pip doesn't have a resolver). This method always returns
100
- # false to ensure `updated_dependencies_after_full_unlock` is never
101
- # called.
102
- false
103
- end
104
-
105
- def updated_dependencies_after_full_unlock
106
- raise NotImplementedError
107
- end
108
-
109
- # rubocop:disable Metrics/PerceivedComplexity
110
- def resolver_type
111
- reqs = dependency.requirements
112
- req_files = reqs.map { |r| r.fetch(:file) }
113
-
114
- # If there are no requirements then this is a sub-dependency. It
115
- # must come from one of Pipenv, Poetry or pip-tools, and can't come
116
- # from the first two unless they have a lockfile.
117
- return subdependency_resolver if reqs.none?
118
-
119
- # Otherwise, this is a top-level dependency, and we can figure out
120
- # which resolver to use based on the filename of its requirements
121
- return :pipfile if req_files.any? { |f| f == "Pipfile" }
122
- return :poetry if req_files.any? { |f| f == "pyproject.toml" }
123
- return :pip_compile if req_files.any? { |f| f.end_with?(".in") }
124
-
125
- if dependency.version && !exact_requirement?(reqs)
126
- subdependency_resolver
127
- else
128
- :requirements
129
- end
130
- end
131
- # rubocop:enable Metrics/PerceivedComplexity
132
-
133
- def subdependency_resolver
134
- return :pipfile if pipfile_lock
135
- return :poetry if poetry_lock || pyproject_lock
136
- return :pip_compile if pip_compile_files.any?
137
-
138
- raise "Claimed to be a sub-dependency, but no lockfile exists!"
139
- end
140
-
141
- def exact_requirement?(reqs)
142
- reqs = reqs.map { |r| r.fetch(:requirement) }
143
- reqs = reqs.compact
144
- reqs = reqs.flat_map { |r| r.split(",").map(&:strip) }
145
- reqs.any? { |r| Utils::Python::Requirement.new(r).exact? }
146
- end
147
-
148
- def resolver_args
149
- {
150
- dependency: dependency,
151
- dependency_files: dependency_files,
152
- credentials: credentials,
153
- latest_allowable_version: latest_version
154
- }
155
- end
156
-
157
- def fetch_latest_version
158
- latest_version_finder.latest_version
159
- end
160
-
161
- def latest_pip_version_with_no_unlock
162
- latest_version_finder.latest_version_with_no_unlock
163
- end
164
-
165
- def latest_version_finder
166
- @latest_version_finder ||= LatestVersionFinder.new(
167
- dependency: dependency,
168
- dependency_files: dependency_files,
169
- credentials: credentials,
170
- ignored_versions: ignored_versions
171
- )
172
- end
173
-
174
- def poetry_library?
175
- return false unless pyproject
176
-
177
- # Hit PyPi and check whether there are details for a library with a
178
- # matching name and description
179
- details = TomlRB.parse(pyproject.content).dig("tool", "poetry")
180
- return false unless details
181
-
182
- index_response = Excon.get(
183
- "https://pypi.org/pypi/#{normalised_name(details['name'])}/json",
184
- idempotent: true,
185
- **SharedHelpers.excon_defaults
186
- )
187
-
188
- return false unless index_response.status == 200
189
-
190
- pypi_info = JSON.parse(index_response.body)["info"] || {}
191
- pypi_info["summary"] == details["description"]
192
- rescue URI::InvalidURIError
193
- false
194
- end
195
-
196
- # See https://www.python.org/dev/peps/pep-0503/#normalized-names
197
- def normalised_name(name)
198
- name.downcase.gsub(/[-_.]+/, "-")
199
- end
200
-
201
- def pipfile
202
- dependency_files.find { |f| f.name == "Pipfile" }
203
- end
204
-
205
- def pipfile_lock
206
- dependency_files.find { |f| f.name == "Pipfile.lock" }
207
- end
208
-
209
- def pyproject
210
- dependency_files.find { |f| f.name == "pyproject.toml" }
211
- end
212
-
213
- def pyproject_lock
214
- dependency_files.find { |f| f.name == "pyproject.lock" }
215
- end
216
-
217
- def poetry_lock
218
- dependency_files.find { |f| f.name == "poetry.lock" }
219
- end
220
-
221
- def pip_compile_files
222
- dependency_files.select { |f| f.name.end_with?(".in") }
223
- end
224
- end
225
- end
226
- end
227
- end