dependabot-composer 0.110.17 → 0.111.0

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.
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