dependabot-composer 0.110.17 → 0.111.0

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: aba20dfcc6db0b26954d79062abb3f517c63926d96e40ef3f3ce11d7f9e3cd70
4
- data.tar.gz: 8d60270c6815f0ed1b1946f49a1ba11213c6f93b16cdca8fd2f08df7db8c3a40
3
+ metadata.gz: f06dcecf2a4971c408e7350be98a990123a35f9fe33d512c697e9c3d6f21d0f5
4
+ data.tar.gz: 9d8f060c61d3238669906c3e86e18a575a2289eed7349c86a9809f7f1f7ed6d0
5
5
  SHA512:
6
- metadata.gz: e95deb882e6f92bcc856858cdfce82ec4d71483c537964cce3c0372f120f7290732ae5e0d43f944abccb872bf55f796f9817e82d199d3bbb22bd9e812fb4c631
7
- data.tar.gz: e0e54228ce0fba86f578a3e133f29f3aac2da562b4d0d690171d49ab1521b66403bf62439c9c947c41fc836906845fc93fd018c76f6d21911b438a26b2286c99
6
+ metadata.gz: 8aff8af7f6efcacb59b8902664a3c7bd7b276d4132a3f9575d54eb3402cb1b1a16cc1bbedb78902afb6da6d35d2b9cf45d53c4c70fa79a31b0abeb9db0d3520b
7
+ data.tar.gz: f0d31274fa5a099acd6aad2a45eebc6c6af8bba0bbb96b8f0bad745115f1f59cbe14de12ff16bc45b8760fc56030bb140b7ccf71d8927c80ffb8482a9ba71442
@@ -66,19 +66,8 @@ class UpdateChecker
66
66
  ->setWhitelistTransitiveDependencies(true)
67
67
  ->setExecuteOperations(false)
68
68
  ->setDumpAutoloader(false)
69
- ->setRunScripts(false);
70
-
71
- /*
72
- * If a platform is set we assume people know what they are doing and
73
- * we respect the setting.
74
- * If no platform is set we ignore it so that the php we run as doesn't
75
- * interfere with resolution.
76
- */
77
- if ($config->get('platform') === []) {
78
- $install->setIgnorePlatformRequirements(true);
79
- } else {
80
- $install->setIgnorePlatformRequirements(false);
81
- }
69
+ ->setRunScripts(false)
70
+ ->setIgnorePlatformRequirements(false);
82
71
 
83
72
  $install->run();
84
73
 
@@ -74,19 +74,8 @@ class Updater
74
74
  ->setWhitelistTransitiveDependencies(true)
75
75
  ->setExecuteOperations(false)
76
76
  ->setDumpAutoloader(false)
77
- ->setRunScripts(false);
78
-
79
- /*
80
- * If a platform is set we assume people know what they are doing and
81
- * we respect the setting.
82
- * If no platform is set we ignore it so that the php we run as doesn't
83
- * interfere with resolution.
84
- */
85
- if ($config->get('platform') === []) {
86
- $install->setIgnorePlatformRequirements(true);
87
- } else {
88
- $install->setIgnorePlatformRequirements(false);
89
- }
77
+ ->setRunScripts(false)
78
+ ->setIgnorePlatformRequirements(false);
90
79
 
91
80
  $install->run();
92
81
 
@@ -4,35 +4,61 @@ require "dependabot/shared_helpers"
4
4
  require "dependabot/errors"
5
5
  require "dependabot/composer/file_updater"
6
6
  require "dependabot/composer/version"
7
+ require "dependabot/composer/requirement"
7
8
  require "dependabot/composer/native_helpers"
8
9
 
10
+ # rubocop:disable Metrics/ClassLength
9
11
  module Dependabot
10
12
  module Composer
11
13
  class FileUpdater
12
14
  class LockfileUpdater
13
15
  require_relative "manifest_updater"
14
16
 
17
+ class MissingExtensions < StandardError
18
+ attr_reader :extensions
19
+
20
+ def initialize(extensions)
21
+ @extensions = extensions
22
+ super
23
+ end
24
+ end
25
+
15
26
  def initialize(dependencies:, dependency_files:, credentials:)
16
27
  @dependencies = dependencies
17
28
  @dependency_files = dependency_files
18
29
  @credentials = credentials
30
+ @composer_platform_extensions = {}
19
31
  end
20
32
 
21
33
  def updated_lockfile_content
22
- base_directory = dependency_files.first.directory
23
- @updated_lockfile_content ||=
24
- SharedHelpers.in_a_temporary_directory(base_directory) do
25
- write_temporary_dependency_files
34
+ @updated_lockfile_content ||= generate_updated_lockfile_content
35
+ rescue MissingExtensions => e
36
+ previous_extensions = composer_platform_extensions.dup
37
+ update_required_extensions(e.extensions)
38
+ raise if previous_extensions == composer_platform_extensions
26
39
 
27
- updated_content = run_update_helper.fetch("composer.lock")
40
+ retry
41
+ end
28
42
 
29
- updated_content = post_process_lockfile(updated_content)
30
- if lockfile.content == updated_content
31
- raise "Expected content to change!"
32
- end
43
+ private
44
+
45
+ attr_reader :dependencies, :dependency_files, :credentials,
46
+ :composer_platform_extensions
47
+
48
+ def generate_updated_lockfile_content
49
+ base_directory = dependency_files.first.directory
50
+ SharedHelpers.in_a_temporary_directory(base_directory) do
51
+ write_temporary_dependency_files
52
+
53
+ updated_content = run_update_helper.fetch("composer.lock")
33
54
 
34
- updated_content
55
+ updated_content = post_process_lockfile(updated_content)
56
+ if lockfile.content == updated_content
57
+ raise "Expected content to change!"
35
58
  end
59
+
60
+ updated_content
61
+ end
36
62
  rescue SharedHelpers::HelperSubprocessFailed => e
37
63
  retry_count ||= 0
38
64
  retry_count += 1
@@ -40,10 +66,6 @@ module Dependabot
40
66
  handle_composer_errors(e)
41
67
  end
42
68
 
43
- private
44
-
45
- attr_reader :dependencies, :dependency_files, :credentials
46
-
47
69
  def dependency
48
70
  # For now, we'll only ever be updating a single dependency for PHP
49
71
  dependencies.first
@@ -87,6 +109,17 @@ module Dependabot
87
109
  # rubocop:disable Metrics/MethodLength
88
110
  # rubocop:disable Metrics/PerceivedComplexity
89
111
  def handle_composer_errors(error)
112
+ if error.message.include?("package requires php") ||
113
+ error.message.include?("requested PHP extension")
114
+ missing_extensions =
115
+ error.message.scan(/\sext\-.*? .*?\s|(?<=requires )php .*?\s/).
116
+ map do |extension_string|
117
+ name, requirement = extension_string.strip.split(" ")
118
+ { name: name, requirement: requirement }
119
+ end
120
+ raise MissingExtensions, missing_extensions
121
+ end
122
+
90
123
  if error.message.start_with?("Failed to execute git checkout")
91
124
  raise git_dependency_reference_error(error)
92
125
  end
@@ -152,8 +185,8 @@ module Dependabot
152
185
  end
153
186
 
154
187
  def locked_composer_json_content
155
- dependencies.
156
- reduce(updated_composer_json_content) do |content, dep|
188
+ tmp_content =
189
+ dependencies.reduce(updated_composer_json_content) do |content, dep|
157
190
  updated_req = dep.version
158
191
  next content unless Composer::Version.correct?(updated_req)
159
192
 
@@ -174,6 +207,17 @@ module Dependabot
174
207
  declaration.gsub(%("#{old_req}"), %("#{updated_req}"))
175
208
  end
176
209
  end
210
+
211
+ json = JSON.parse(tmp_content)
212
+
213
+ composer_platform_extensions.each do |extension, requirements|
214
+ json["config"] ||= {}
215
+ json["config"]["platform"] ||= {}
216
+ json["config"]["platform"][extension] =
217
+ version_for_reqs(requirements)
218
+ end
219
+
220
+ JSON.dump(json)
177
221
  end
178
222
 
179
223
  def git_dependency_reference_error(error)
@@ -192,7 +236,8 @@ module Dependabot
192
236
 
193
237
  def post_process_lockfile(content)
194
238
  content = replace_patches(content)
195
- replace_content_hash(content)
239
+ content = replace_content_hash(content)
240
+ replace_platform_overrides(content)
196
241
  end
197
242
 
198
243
  def replace_patches(updated_content)
@@ -239,6 +284,52 @@ module Dependabot
239
284
  end
240
285
  end
241
286
 
287
+ def replace_platform_overrides(content)
288
+ original_object = JSON.parse(lockfile.content)
289
+ original_overrides = original_object.fetch("platform-overrides", nil)
290
+
291
+ updated_object = JSON.parse(content)
292
+
293
+ if original_object.key?("platform-overrides")
294
+ updated_object["platform-overrides"] = original_overrides
295
+ else
296
+ updated_object.delete("platform-overrides")
297
+ end
298
+
299
+ JSON.pretty_generate(updated_object, indent: " ").
300
+ gsub(/\[\n\n\s*\]/, "[]").
301
+ gsub(/\}\z/, "}\n")
302
+ end
303
+
304
+ def version_for_reqs(requirements)
305
+ req_array = requirements.map { |str| Composer::Requirement.new(str) }
306
+ potential_versions =
307
+ req_array.map do |req|
308
+ op, version = req.requirements.first
309
+ case op
310
+ when ">" then version.bump
311
+ when "<" then Composer::Version.new("0.0.1")
312
+ else version
313
+ end
314
+ end
315
+
316
+ version = potential_versions.
317
+ find { |v| req_array.all? { |r| r.satisfied_by?(v) } }
318
+ raise "No matching version for #{requirements}!" unless version
319
+
320
+ version.to_s
321
+ end
322
+
323
+ def update_required_extensions(additional_extensions)
324
+ additional_extensions.each do |ext|
325
+ composer_platform_extensions[ext.fetch(:name)] ||= []
326
+ composer_platform_extensions[ext.fetch(:name)] +=
327
+ [ext.fetch(:requirement)]
328
+ composer_platform_extensions[ext.fetch(:name)] =
329
+ composer_platform_extensions[ext.fetch(:name)].uniq
330
+ end
331
+ end
332
+
242
333
  def php_helper_path
243
334
  NativeHelpers.composer_helper_path
244
335
  end
@@ -284,3 +375,4 @@ module Dependabot
284
375
  end
285
376
  end
286
377
  end
378
+ # rubocop:enable Metrics/ClassLength
@@ -1,25 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "dependabot/errors"
4
+ require "json"
3
5
  require "dependabot/shared_helpers"
4
6
  require "dependabot/composer/update_checker"
5
7
  require "dependabot/composer/version"
8
+ require "dependabot/composer/requirement"
6
9
  require "dependabot/composer/native_helpers"
7
10
 
8
11
  module Dependabot
9
12
  module Composer
10
13
  class UpdateChecker
11
14
  class VersionResolver
15
+ class MissingExtensions < StandardError
16
+ attr_reader :extensions
17
+
18
+ def initialize(extensions)
19
+ @extensions = extensions
20
+ super
21
+ end
22
+ end
23
+
12
24
  VERSION_REGEX = /[0-9]+(?:\.[A-Za-z0-9\-_]+)*/.freeze
13
25
  SOURCE_TIMED_OUT_REGEX =
14
26
  /The "(?<url>[^"]+packages\.json)".*timed out/.freeze
15
27
 
16
28
  def initialize(credentials:, dependency:, dependency_files:,
17
29
  requirements_to_unlock:, latest_allowable_version:)
18
- @credentials = credentials
19
- @dependency = dependency
20
- @dependency_files = dependency_files
21
- @requirements_to_unlock = requirements_to_unlock
22
- @latest_allowable_version = latest_allowable_version
30
+ @credentials = credentials
31
+ @dependency = dependency
32
+ @dependency_files = dependency_files
33
+ @requirements_to_unlock = requirements_to_unlock
34
+ @latest_allowable_version = latest_allowable_version
35
+ @composer_platform_extensions = {}
23
36
  end
24
37
 
25
38
  def latest_resolvable_version
@@ -29,7 +42,8 @@ module Dependabot
29
42
  private
30
43
 
31
44
  attr_reader :credentials, :dependency, :dependency_files,
32
- :requirements_to_unlock, :latest_allowable_version
45
+ :requirements_to_unlock, :latest_allowable_version,
46
+ :composer_platform_extensions
33
47
 
34
48
  def fetch_latest_resolvable_version
35
49
  version = fetch_latest_resolvable_version_string
@@ -37,6 +51,12 @@ module Dependabot
37
51
  return unless Composer::Version.correct?(version)
38
52
 
39
53
  Composer::Version.new(version)
54
+ rescue MissingExtensions => e
55
+ previous_extensions = composer_platform_extensions.dup
56
+ update_required_extensions(e.extensions)
57
+ raise if previous_extensions == composer_platform_extensions
58
+
59
+ retry
40
60
  end
41
61
 
42
62
  def fetch_latest_resolvable_version_string
@@ -82,10 +102,21 @@ module Dependabot
82
102
  def prepared_composer_json_content
83
103
  content = composer_file.content
84
104
 
85
- content.gsub(
105
+ content = content.gsub(
86
106
  /"#{Regexp.escape(dependency.name)}"\s*:\s*".*"/,
87
107
  %("#{dependency.name}": "#{updated_version_requirement_string}")
88
108
  )
109
+
110
+ json = JSON.parse(content)
111
+
112
+ composer_platform_extensions.each do |extension, requirements|
113
+ json["config"] ||= {}
114
+ json["config"]["platform"] ||= {}
115
+ json["config"]["platform"][extension] =
116
+ version_for_reqs(requirements)
117
+ end
118
+
119
+ JSON.dump(json)
89
120
  end
90
121
 
91
122
  # rubocop:disable Metrics/AbcSize
@@ -145,17 +176,16 @@ module Dependabot
145
176
  elsif error.message.start_with?("Could not parse version") ||
146
177
  error.message.include?("does not allow connections to http://")
147
178
  raise Dependabot::DependencyFileNotResolvable, sanitized_message
148
- elsif error.message.include?("requested PHP extension")
149
- extensions = error.message.scan(/\sext\-.*?\s/).map(&:strip).uniq
150
- msg = "Dependabot's installed extensions didn't match those "\
151
- "required by your application.\n\n"\
152
- "Please add the following extensions to the platform "\
153
- "config in your composer.json to allow Dependabot to run: "\
154
- "#{extensions.join(', ')}.\n\n"\
155
- "The full error raised was:\n\n#{error.message}"
156
- raise Dependabot::DependencyFileNotResolvable, msg
157
179
  elsif error.message.include?("package requires php") ||
158
- error.message.include?("cannot require itself") ||
180
+ error.message.include?("requested PHP extension")
181
+ missing_extensions =
182
+ error.message.scan(/\sext\-.*? .*?\s|(?<=requires )php .*?\s/).
183
+ map do |extension_string|
184
+ name, requirement = extension_string.strip.split(" ")
185
+ { name: name, requirement: requirement }
186
+ end
187
+ raise MissingExtensions, missing_extensions
188
+ elsif error.message.include?("cannot require itself") ||
159
189
  error.message.include?('packages.json" file could not be down')
160
190
  raise Dependabot::DependencyFileNotResolvable, error.message
161
191
  elsif error.message.include?("No driver found to handle VCS") &&
@@ -208,6 +238,35 @@ module Dependabot
208
238
  # rubocop:enable Metrics/CyclomaticComplexity
209
239
  # rubocop:enable Metrics/MethodLength
210
240
 
241
+ def version_for_reqs(requirements)
242
+ req_array = requirements.map { |str| Composer::Requirement.new(str) }
243
+ potential_versions =
244
+ req_array.map do |req|
245
+ op, version = req.requirements.first
246
+ case op
247
+ when ">" then version.bump
248
+ when "<" then Composer::Version.new("0.0.1")
249
+ else version
250
+ end
251
+ end
252
+
253
+ version = potential_versions.
254
+ find { |v| req_array.all? { |r| r.satisfied_by?(v) } }
255
+ raise "No matching version for #{requirements}!" unless version
256
+
257
+ version.to_s
258
+ end
259
+
260
+ def update_required_extensions(additional_extensions)
261
+ additional_extensions.each do |ext|
262
+ composer_platform_extensions[ext.fetch(:name)] ||= []
263
+ composer_platform_extensions[ext.fetch(:name)] +=
264
+ [ext.fetch(:requirement)]
265
+ composer_platform_extensions[ext.fetch(:name)] =
266
+ composer_platform_extensions[ext.fetch(:name)].uniq
267
+ end
268
+ end
269
+
211
270
  def php_helper_path
212
271
  NativeHelpers.composer_helper_path
213
272
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-composer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.110.17
4
+ version: 0.111.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-04 00:00:00.000000000 Z
11
+ date: 2019-07-05 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.110.17
19
+ version: 0.111.0
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.110.17
26
+ version: 0.111.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement