dependabot-python 0.104.0 → 0.104.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93762e9f3a2a0f2f376cc958c13b0d4ec07cd07bb6208d4859566bb38dc8c1bb
4
- data.tar.gz: 1ee335372559b02cd5ea0a12541875eb3c389726a759beb68d3dd22cbc9b9cc6
3
+ metadata.gz: 1704546d9328a26bba38bd159150a5238797253f07538aa5ee731e307d1a5a86
4
+ data.tar.gz: 4e1a258ebb51cedb6cd6c801d2ac4a7f2f191658b163b56046a7ab526c24c287
5
5
  SHA512:
6
- metadata.gz: d4a11b74a94191a3e2e102a937d3ea327e0b46d0fda1b74faafd0f7626578a7bc6d496918c65e44b7895aa0103b92026d0dad323a1d8b872ef0c13634212c116
7
- data.tar.gz: 6517220833b3f29e267f010ec2cc5f94aa2342ee4bc6edc8af83a490667ac3d97b625ab0c8c3d51dcd2da0735c55a29ebd0189c87cf67e78031a96ec4868c7ba
6
+ metadata.gz: 1b994991d16cafe0ece709fdc52f5bbe5f25cb3dd635ad08fab6b87885926afe281231642d28cfe24e26724bccd4427750d99dad27e718882ecc04bf333e8ff5
7
+ data.tar.gz: 37ffc6c72e569f50b2c73999089237512b08742e10473fb02f5b7047751be623ffeb9d9d8298376e1748748274dfe22c64c6a15ee60647de831317f8caf71e33
@@ -0,0 +1,189 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dependabot/python/update_checker"
4
+ require "dependabot/python/authed_url_builder"
5
+ require "dependabot/errors"
6
+
7
+ module Dependabot
8
+ module Python
9
+ class UpdateChecker
10
+ class IndexFinder
11
+ PYPI_BASE_URL = "https://pypi.python.org/simple/"
12
+ ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/.freeze
13
+
14
+ def initialize(dependency_files:, credentials:)
15
+ @dependency_files = dependency_files
16
+ @credentials = credentials
17
+ end
18
+
19
+ def index_urls
20
+ extra_index_urls =
21
+ config_variable_index_urls[:extra] +
22
+ pipfile_index_urls[:extra] +
23
+ requirement_file_index_urls[:extra] +
24
+ pip_conf_index_urls[:extra]
25
+
26
+ extra_index_urls = extra_index_urls.map do |url|
27
+ clean_check_and_remove_environment_variables(url)
28
+ end
29
+
30
+ # URL encode any `@` characters within registry URL creds.
31
+ # TODO: The test that fails if the `map` here is removed is likely a
32
+ # bug in Ruby's URI parser, and should be fixed there.
33
+ [main_index_url, *extra_index_urls].map do |url|
34
+ url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
35
+ end.uniq
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :dependency_files, :credentials
41
+
42
+ def main_index_url
43
+ url =
44
+ config_variable_index_urls[:main] ||
45
+ pipfile_index_urls[:main] ||
46
+ requirement_file_index_urls[:main] ||
47
+ pip_conf_index_urls[:main] ||
48
+ PYPI_BASE_URL
49
+
50
+ return unless url
51
+
52
+ clean_check_and_remove_environment_variables(url)
53
+ end
54
+
55
+ def requirement_file_index_urls
56
+ urls = { main: nil, extra: [] }
57
+
58
+ requirements_files.each do |file|
59
+ if file.content.match?(/^--index-url\s(.+)/)
60
+ urls[:main] =
61
+ file.content.match(/^--index-url\s(.+)/).captures.first
62
+ end
63
+ urls[:extra] += file.content.scan(/^--extra-index-url\s(.+)/).
64
+ flatten
65
+ end
66
+
67
+ urls
68
+ end
69
+
70
+ def pip_conf_index_urls
71
+ urls = { main: nil, extra: [] }
72
+
73
+ return urls unless pip_conf
74
+
75
+ content = pip_conf.content
76
+
77
+ if content.match?(/^index-url\s*=/x)
78
+ urls[:main] = content.match(/^index-url\s*=\s*(.+)/).
79
+ captures.first
80
+ end
81
+ urls[:extra] += content.scan(/^extra-index-url\s*=(.+)/).flatten
82
+
83
+ urls
84
+ end
85
+
86
+ def pipfile_index_urls
87
+ urls = { main: nil, extra: [] }
88
+
89
+ return urls unless pipfile
90
+
91
+ pipfile_object = TomlRB.parse(pipfile.content)
92
+
93
+ urls[:main] = pipfile_object["source"]&.first&.fetch("url", nil)
94
+
95
+ pipfile_object["source"]&.each do |source|
96
+ urls[:extra] << source.fetch("url") if source["url"]
97
+ end
98
+ urls[:extra] = urls[:extra].uniq
99
+
100
+ urls
101
+ rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
102
+ urls
103
+ end
104
+
105
+ def config_variable_index_urls
106
+ urls = { main: nil, extra: [] }
107
+
108
+ index_url_creds = credentials.
109
+ select { |cred| cred["type"] == "python_index" }
110
+
111
+ if (main_cred = index_url_creds.find { |cred| cred["replaces-base"] })
112
+ urls[:main] = AuthedUrlBuilder.authed_url(credential: main_cred)
113
+ end
114
+
115
+ urls[:extra] =
116
+ index_url_creds.
117
+ reject { |cred| cred["replaces-base"] }.
118
+ map { |cred| AuthedUrlBuilder.authed_url(credential: cred) }
119
+
120
+ urls
121
+ end
122
+
123
+ def clean_check_and_remove_environment_variables(url)
124
+ url = url.strip.gsub(%r{/*$}, "") + "/"
125
+
126
+ unless url.match?(ENVIRONMENT_VARIABLE_REGEX)
127
+ return authed_base_url(url)
128
+ end
129
+
130
+ config_variable_urls =
131
+ [
132
+ config_variable_index_urls[:main],
133
+ *config_variable_index_urls[:extra]
134
+ ].
135
+ compact.
136
+ map { |u| u.strip.gsub(%r{/*$}, "") + "/" }
137
+
138
+ regexp = url.split(ENVIRONMENT_VARIABLE_REGEX).
139
+ map { |part| Regexp.quote(part) }.
140
+ join(".+")
141
+ authed_url = config_variable_urls.find { |u| u.match?(regexp) }
142
+ return authed_url if authed_url
143
+
144
+ cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}, "")
145
+ authed_url = authed_base_url(cleaned_url)
146
+ return authed_url if credential_for(cleaned_url)
147
+
148
+ raise PrivateSourceAuthenticationFailure, url
149
+ end
150
+
151
+ def authed_base_url(base_url)
152
+ cred = credential_for(base_url)
153
+ return base_url unless cred
154
+
155
+ AuthedUrlBuilder.authed_url(credential: cred).gsub(%r{/*$}, "") + "/"
156
+ end
157
+
158
+ def credential_for(url)
159
+ credentials.
160
+ select { |c| c["type"] == "python_index" }.
161
+ find do |c|
162
+ cred_url = c.fetch("index-url").gsub(%r{/*$}, "") + "/"
163
+ cred_url.include?(url)
164
+ end
165
+ end
166
+
167
+ def pip_conf
168
+ dependency_files.find { |f| f.name == "pip.conf" }
169
+ end
170
+
171
+ def pipfile
172
+ dependency_files.find { |f| f.name == "Pipfile" }
173
+ end
174
+
175
+ def pyproject
176
+ dependency_files.find { |f| f.name == "pyproject.toml" }
177
+ end
178
+
179
+ def requirements_files
180
+ dependency_files.select { |f| f.name.match?(/requirements/x) }
181
+ end
182
+
183
+ def pip_compile_files
184
+ dependency_files.select { |f| f.name.end_with?(".in") }
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -10,7 +10,7 @@ module Dependabot
10
10
  module Python
11
11
  class UpdateChecker
12
12
  class LatestVersionFinder
13
- ENVIRONMENT_VARIABLE_REGEX = /\$\{.+\}/.freeze
13
+ require_relative "index_finder"
14
14
 
15
15
  def initialize(dependency:, dependency_files:, credentials:,
16
16
  ignored_versions:)
@@ -36,21 +36,37 @@ module Dependabot
36
36
 
37
37
  def fetch_latest_version
38
38
  versions = available_versions
39
- versions.reject! { |v| ignore_reqs.any? { |r| r.satisfied_by?(v) } }
40
- versions.reject!(&:prerelease?) unless wants_prerelease?
39
+ versions = filter_prerelease_versions(versions)
40
+ versions = filter_ignored_versions(versions)
41
41
  versions.max
42
42
  end
43
43
 
44
44
  def fetch_latest_version_with_no_unlock
45
45
  versions = available_versions
46
+ versions = filter_prerelease_versions(versions)
47
+ versions = filter_ignored_versions(versions)
48
+ versions = filter_out_of_range_versions(versions)
49
+ versions.max
50
+ end
51
+
52
+ def filter_prerelease_versions(versions_array)
53
+ return versions_array if wants_prerelease?
54
+
55
+ versions_array.reject(&:prerelease?)
56
+ end
57
+
58
+ def filter_ignored_versions(versions_array)
59
+ versions_array.
60
+ reject { |v| ignore_reqs.any? { |r| r.satisfied_by?(v) } }
61
+ end
62
+
63
+ def filter_out_of_range_versions(versions_array)
46
64
  reqs = dependency.requirements.map do |r|
47
- reqs = (r.fetch(:requirement) || "").split(",").map(&:strip)
48
- requirement_class.new(reqs)
49
- end
50
- versions.reject!(&:prerelease?) unless wants_prerelease?
51
- versions.sort.reverse.
52
- reject { |v| ignore_reqs.any? { |r| r.satisfied_by?(v) } }.
53
- find { |v| reqs.all? { |r| r.satisfied_by?(v) } }
65
+ requirement_class.requirements_array(r.fetch(:requirement))
66
+ end.compact
67
+
68
+ versions_array.
69
+ select { |v| reqs.all? { |r| r.any? { |o| o.satisfied_by?(v) } } }
54
70
  end
55
71
 
56
72
  def wants_prerelease?
@@ -68,65 +84,42 @@ module Dependabot
68
84
  # See https://www.python.org/dev/peps/pep-0503/ for details of the
69
85
  # Simple Repository API we use here.
70
86
  def available_versions
71
- index_urls.flat_map do |index_url|
72
- sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted")
73
- index_response = registry_response_for_dependency(index_url)
87
+ @available_versions ||=
88
+ index_urls.flat_map do |index_url|
89
+ sanitized_url = index_url.gsub(%r{(?<=//).*(?=@)}, "redacted")
90
+ index_response = registry_response_for_dependency(index_url)
91
+
92
+ if [401, 403].include?(index_response.status) &&
93
+ [401, 403].include?(registry_index_response(index_url).status)
94
+ raise PrivateSourceAuthenticationFailure, sanitized_url
95
+ end
96
+
97
+ index_response.body.
98
+ scan(%r{<a\s.*?>(.*?)</a>}m).flatten.
99
+ select { |n| n.match?(name_regex) }.
100
+ map do |filename|
101
+ version =
102
+ filename.
103
+ gsub(/#{name_regex}-/i, "").
104
+ split(/-|\.tar\.|\.zip|\.whl/).
105
+ first
106
+ next unless version_class.correct?(version)
107
+
108
+ version_class.new(version)
109
+ end.compact
110
+ rescue Excon::Error::Timeout, Excon::Error::Socket
111
+ raise if MAIN_PYPI_INDEXES.include?(index_url)
74
112
 
75
- if [401, 403].include?(index_response.status) &&
76
- [401, 403].include?(registry_index_response(index_url).status)
77
113
  raise PrivateSourceAuthenticationFailure, sanitized_url
78
114
  end
79
-
80
- index_response.body.
81
- scan(%r{<a\s.*?>(.*?)</a>}m).flatten.
82
- select { |n| n.match?(name_regex) }.
83
- map do |filename|
84
- version =
85
- filename.
86
- gsub(/#{name_regex}-/i, "").
87
- split(/-|\.tar\.|\.zip|\.whl/).
88
- first
89
- next unless version_class.correct?(version)
90
-
91
- version_class.new(version)
92
- end.compact
93
- rescue Excon::Error::Timeout, Excon::Error::Socket
94
- next if MAIN_PYPI_INDEXES.include?(index_url)
95
-
96
- raise PrivateSourceAuthenticationFailure, sanitized_url
97
- end
98
115
  end
99
116
 
100
117
  def index_urls
101
- extra_index_urls =
102
- config_variable_index_urls[:extra] +
103
- pipfile_index_urls[:extra] +
104
- requirement_file_index_urls[:extra] +
105
- pip_conf_index_urls[:extra]
106
-
107
- extra_index_urls = extra_index_urls.map do |url|
108
- clean_check_and_remove_environment_variables(url)
109
- end
110
-
111
- # URL encode any `@` characters within registry URL creds.
112
- # TODO: The test that fails if the `map` here is removed is likely a
113
- # bug in Ruby's URI parser, and should be fixed there.
114
- [main_index_url, *extra_index_urls].map do |url|
115
- url.rpartition("@").tap { |a| a.first.gsub!("@", "%40") }.join
116
- end.uniq
117
- end
118
-
119
- def main_index_url
120
- url =
121
- config_variable_index_urls[:main] ||
122
- pipfile_index_urls[:main] ||
123
- requirement_file_index_urls[:main] ||
124
- pip_conf_index_urls[:main] ||
125
- "https://pypi.python.org/simple/"
126
-
127
- return unless url
128
-
129
- clean_check_and_remove_environment_variables(url)
118
+ @index_urls ||=
119
+ IndexFinder.new(
120
+ dependency_files: dependency_files,
121
+ credentials: credentials
122
+ ).index_urls
130
123
  end
131
124
 
132
125
  def registry_response_for_dependency(index_url)
@@ -145,118 +138,6 @@ module Dependabot
145
138
  )
146
139
  end
147
140
 
148
- def requirement_file_index_urls
149
- urls = { main: nil, extra: [] }
150
-
151
- requirements_files.each do |file|
152
- if file.content.match?(/^--index-url\s(.+)/)
153
- urls[:main] =
154
- file.content.match(/^--index-url\s(.+)/).captures.first
155
- end
156
- urls[:extra] += file.content.scan(/^--extra-index-url\s(.+)/).
157
- flatten
158
- end
159
-
160
- urls
161
- end
162
-
163
- def pip_conf_index_urls
164
- urls = { main: nil, extra: [] }
165
-
166
- return urls unless pip_conf
167
-
168
- content = pip_conf.content
169
-
170
- if content.match?(/^index-url\s*=/x)
171
- urls[:main] = content.match(/^index-url\s*=\s*(.+)/).
172
- captures.first
173
- end
174
- urls[:extra] += content.scan(/^extra-index-url\s*=(.+)/).flatten
175
-
176
- urls
177
- end
178
-
179
- def pipfile_index_urls
180
- urls = { main: nil, extra: [] }
181
-
182
- return urls unless pipfile
183
-
184
- pipfile_object = TomlRB.parse(pipfile.content)
185
-
186
- urls[:main] = pipfile_object["source"]&.first&.fetch("url", nil)
187
-
188
- pipfile_object["source"]&.each do |source|
189
- urls[:extra] << source.fetch("url") if source["url"]
190
- end
191
- urls[:extra] = urls[:extra].uniq
192
-
193
- urls
194
- rescue TomlRB::ParseError, TomlRB::ValueOverwriteError
195
- urls
196
- end
197
-
198
- def config_variable_index_urls
199
- urls = { main: nil, extra: [] }
200
-
201
- index_url_creds = credentials.
202
- select { |cred| cred["type"] == "python_index" }
203
-
204
- if (main_cred = index_url_creds.find { |cred| cred["replaces-base"] })
205
- urls[:main] = AuthedUrlBuilder.authed_url(credential: main_cred)
206
- end
207
-
208
- urls[:extra] =
209
- index_url_creds.
210
- reject { |cred| cred["replaces-base"] }.
211
- map { |cred| AuthedUrlBuilder.authed_url(credential: cred) }
212
-
213
- urls
214
- end
215
-
216
- def clean_check_and_remove_environment_variables(url)
217
- url = url.strip.gsub(%r{/*$}, "") + "/"
218
-
219
- unless url.match?(ENVIRONMENT_VARIABLE_REGEX)
220
- return authed_base_url(url)
221
- end
222
-
223
- config_variable_urls =
224
- [
225
- config_variable_index_urls[:main],
226
- *config_variable_index_urls[:extra]
227
- ].
228
- compact.
229
- map { |u| u.strip.gsub(%r{/*$}, "") + "/" }
230
-
231
- regexp = url.split(ENVIRONMENT_VARIABLE_REGEX).
232
- map { |part| Regexp.quote(part) }.
233
- join(".+")
234
- authed_url = config_variable_urls.find { |u| u.match?(regexp) }
235
- return authed_url if authed_url
236
-
237
- cleaned_url = url.gsub(%r{#{ENVIRONMENT_VARIABLE_REGEX}/?}, "")
238
- authed_url = authed_base_url(cleaned_url)
239
- return authed_url if credential_for(cleaned_url)
240
-
241
- raise PrivateSourceAuthenticationFailure, url
242
- end
243
-
244
- def authed_base_url(base_url)
245
- cred = credential_for(base_url)
246
- return base_url unless cred
247
-
248
- AuthedUrlBuilder.authed_url(credential: cred).gsub(%r{/*$}, "") + "/"
249
- end
250
-
251
- def credential_for(url)
252
- credentials.
253
- select { |c| c["type"] == "python_index" }.
254
- find do |c|
255
- cred_url = c.fetch("index-url").gsub(%r{/*$}, "") + "/"
256
- cred_url.include?(url)
257
- end
258
- end
259
-
260
141
  def ignore_reqs
261
142
  ignored_versions.map { |req| requirement_class.new(req.split(",")) }
262
143
  end
@@ -271,26 +152,6 @@ module Dependabot
271
152
  /#{parts.join("[\s_.-]")}/i
272
153
  end
273
154
 
274
- def pip_conf
275
- dependency_files.find { |f| f.name == "pip.conf" }
276
- end
277
-
278
- def pipfile
279
- dependency_files.find { |f| f.name == "Pipfile" }
280
- end
281
-
282
- def pyproject
283
- dependency_files.find { |f| f.name == "pyproject.toml" }
284
- end
285
-
286
- def requirements_files
287
- dependency_files.select { |f| f.name.match?(/requirements/x) }
288
- end
289
-
290
- def pip_compile_files
291
- dependency_files.select { |f| f.name.end_with?(".in") }
292
- end
293
-
294
155
  def version_class
295
156
  Utils.version_class_for_package_manager(dependency.package_manager)
296
157
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-python
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.104.0
4
+ version: 0.104.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-16 00:00:00.000000000 Z
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.104.0
19
+ version: 0.104.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.104.0
26
+ version: 0.104.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -172,6 +172,7 @@ files:
172
172
  - lib/dependabot/python/requirement.rb
173
173
  - lib/dependabot/python/requirement_parser.rb
174
174
  - lib/dependabot/python/update_checker.rb
175
+ - lib/dependabot/python/update_checker/index_finder.rb
175
176
  - lib/dependabot/python/update_checker/latest_version_finder.rb
176
177
  - lib/dependabot/python/update_checker/pip_compile_version_resolver.rb
177
178
  - lib/dependabot/python/update_checker/pipfile_version_resolver.rb