dependabot-composer 0.288.0 → 0.289.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 +4 -4
- data/lib/dependabot/composer/file_fetcher/path_dependency_builder.rb +1 -1
- data/lib/dependabot/composer/file_fetcher.rb +7 -7
- data/lib/dependabot/composer/file_parser.rb +70 -11
- data/lib/dependabot/composer/file_updater/lockfile_updater.rb +17 -17
- data/lib/dependabot/composer/file_updater.rb +3 -3
- data/lib/dependabot/composer/helpers.rb +77 -4
- data/lib/dependabot/composer/language.rb +38 -0
- data/lib/dependabot/composer/package_manager.rb +22 -9
- data/lib/dependabot/composer/update_checker/latest_version_finder.rb +7 -5
- data/lib/dependabot/composer/update_checker/version_resolver.rb +22 -18
- data/lib/dependabot/composer/update_checker.rb +2 -2
- data/lib/dependabot/composer.rb +1 -0
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90fd9742e112bda23efcab031a487985c2bc19431d081a25e53f23a915edbdc0
|
|
4
|
+
data.tar.gz: 18902e7c4b755fac1bd12fd6ba60a1b2e7baeb97355156120b9591d3b031cb41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcbf0ae77a0f37ae82e66a5fbfe6b040b881c3de3ac0b4be70ff8964bf2d7c0d1883ef4bf239548febec2d457a5d083bc01bb764a3a9cd0b23731c56dec17254
|
|
7
|
+
data.tar.gz: e799650f569f04c2ffb674199bba37a1eb9f2e121183ebd353701ae2b6df948080c45661b3ff7af830a090c771ea9fc60410d90cf94a05991ba60f545c29667d
|
|
@@ -17,7 +17,7 @@ module Dependabot
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def dependency_file
|
|
20
|
-
filename = File.join(path,
|
|
20
|
+
filename = File.join(path, PackageManager::MANIFEST_FILENAME)
|
|
21
21
|
|
|
22
22
|
# Current we just return `nil` if a path dependency can't be built.
|
|
23
23
|
# In future we may wish to change that to a raise. (We'll get errors
|
|
@@ -16,17 +16,17 @@ module Dependabot
|
|
|
16
16
|
require_relative "helpers"
|
|
17
17
|
|
|
18
18
|
def self.required_files_in?(filenames)
|
|
19
|
-
filenames.include?(
|
|
19
|
+
filenames.include?(PackageManager::MANIFEST_FILENAME)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def self.required_files_message
|
|
23
|
-
"Repo must contain a
|
|
23
|
+
"Repo must contain a #{PackageManager::MANIFEST_FILENAME}."
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def ecosystem_versions
|
|
27
27
|
{
|
|
28
28
|
package_managers: {
|
|
29
|
-
|
|
29
|
+
PackageManager::NAME => Helpers.composer_version(parsed_composer_json, parsed_lockfile)
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
end
|
|
@@ -45,20 +45,20 @@ module Dependabot
|
|
|
45
45
|
private
|
|
46
46
|
|
|
47
47
|
def composer_json
|
|
48
|
-
@composer_json ||= fetch_file_from_host(
|
|
48
|
+
@composer_json ||= fetch_file_from_host(PackageManager::MANIFEST_FILENAME)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def composer_lock
|
|
52
52
|
return @composer_lock if defined?(@composer_lock)
|
|
53
53
|
|
|
54
|
-
@composer_lock = fetch_file_if_present(
|
|
54
|
+
@composer_lock = fetch_file_if_present(PackageManager::LOCKFILE_FILENAME)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# NOTE: This is fetched but currently unused
|
|
58
58
|
def auth_json
|
|
59
59
|
return @auth_json if defined?(@auth_json)
|
|
60
60
|
|
|
61
|
-
@auth_json = fetch_support_file(
|
|
61
|
+
@auth_json = fetch_support_file(PackageManager::AUTH_FILENAME)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def artifact_dependencies
|
|
@@ -106,7 +106,7 @@ module Dependabot
|
|
|
106
106
|
directories = path.end_with?("*") ? expand_path(path) : [path]
|
|
107
107
|
|
|
108
108
|
directories.each do |dir|
|
|
109
|
-
file = File.join(dir,
|
|
109
|
+
file = File.join(dir, PackageManager::MANIFEST_FILENAME)
|
|
110
110
|
|
|
111
111
|
begin
|
|
112
112
|
composer_json_files << fetch_file_with_root_fallback(file)
|
|
@@ -11,6 +11,12 @@ require "dependabot/errors"
|
|
|
11
11
|
|
|
12
12
|
module Dependabot
|
|
13
13
|
module Composer
|
|
14
|
+
REQUIREMENT_SEPARATOR = /
|
|
15
|
+
(?<=\S|^) # Positive lookbehind for a non-whitespace character or start of string
|
|
16
|
+
(?:[ \t,]*\|\|?[ \t]*) # Match optional whitespace, a pipe (|| or |), and optional whitespace
|
|
17
|
+
(?=\S|$) # Positive lookahead for a non-whitespace character or end of string
|
|
18
|
+
/x
|
|
19
|
+
|
|
14
20
|
class FileParser < Dependabot::FileParsers::Base
|
|
15
21
|
require "dependabot/file_parsers/base/dependency_set"
|
|
16
22
|
|
|
@@ -40,7 +46,8 @@ module Dependabot
|
|
|
40
46
|
@ecosystem ||= T.let(
|
|
41
47
|
Ecosystem.new(
|
|
42
48
|
name: ECOSYSTEM,
|
|
43
|
-
package_manager: package_manager
|
|
49
|
+
package_manager: package_manager,
|
|
50
|
+
language: language
|
|
44
51
|
),
|
|
45
52
|
T.nilable(Ecosystem)
|
|
46
53
|
)
|
|
@@ -50,7 +57,48 @@ module Dependabot
|
|
|
50
57
|
|
|
51
58
|
sig { returns(Ecosystem::VersionManager) }
|
|
52
59
|
def package_manager
|
|
53
|
-
|
|
60
|
+
raw_composer_version = env_versions[:composer] || composer_version
|
|
61
|
+
PackageManager.new(
|
|
62
|
+
raw_composer_version
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
sig { returns(T.nilable(Ecosystem::VersionManager)) }
|
|
67
|
+
def language
|
|
68
|
+
php_version = env_versions[:php]
|
|
69
|
+
|
|
70
|
+
return unless php_version
|
|
71
|
+
|
|
72
|
+
Language.new(
|
|
73
|
+
php_version,
|
|
74
|
+
requirement: php_requirement
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
sig { returns(T::Hash[Symbol, T.nilable(String)]) }
|
|
79
|
+
def env_versions
|
|
80
|
+
@env_versions ||= T.let(
|
|
81
|
+
Helpers.fetch_composer_and_php_versions,
|
|
82
|
+
T.nilable(T::Hash[Symbol, T.nilable(String)])
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Capture PHP requirement from the composer.json
|
|
87
|
+
sig { returns(T.nilable(Requirement)) }
|
|
88
|
+
def php_requirement
|
|
89
|
+
requirement_string = Helpers.php_constraint(parsed_composer_json)
|
|
90
|
+
|
|
91
|
+
return nil unless requirement_string
|
|
92
|
+
|
|
93
|
+
requirements = requirement_string
|
|
94
|
+
.strip
|
|
95
|
+
.split(REQUIREMENT_SEPARATOR)
|
|
96
|
+
.map(&:strip)
|
|
97
|
+
.reject(&:empty?)
|
|
98
|
+
|
|
99
|
+
return nil unless requirements.any?
|
|
100
|
+
|
|
101
|
+
Requirement.new(requirements)
|
|
54
102
|
end
|
|
55
103
|
|
|
56
104
|
sig { returns(DependencySet) }
|
|
@@ -95,7 +143,7 @@ module Dependabot
|
|
|
95
143
|
version: dependency_version(name: name, type: group),
|
|
96
144
|
requirements: [{
|
|
97
145
|
requirement: req,
|
|
98
|
-
file:
|
|
146
|
+
file: PackageManager::MANIFEST_FILENAME,
|
|
99
147
|
source: dependency_source(
|
|
100
148
|
name: name,
|
|
101
149
|
type: group,
|
|
@@ -103,7 +151,7 @@ module Dependabot
|
|
|
103
151
|
),
|
|
104
152
|
groups: [group]
|
|
105
153
|
}],
|
|
106
|
-
package_manager:
|
|
154
|
+
package_manager: PackageManager::NAME
|
|
107
155
|
)
|
|
108
156
|
end
|
|
109
157
|
|
|
@@ -141,7 +189,7 @@ module Dependabot
|
|
|
141
189
|
name: name,
|
|
142
190
|
version: version,
|
|
143
191
|
requirements: [],
|
|
144
|
-
package_manager:
|
|
192
|
+
package_manager: PackageManager::NAME,
|
|
145
193
|
subdependency_metadata: [{
|
|
146
194
|
production: keys.fetch(:group) != "development"
|
|
147
195
|
}]
|
|
@@ -162,7 +210,8 @@ module Dependabot
|
|
|
162
210
|
end
|
|
163
211
|
|
|
164
212
|
sig do
|
|
165
|
-
params(name: String, type: String,
|
|
213
|
+
params(name: String, type: String,
|
|
214
|
+
requirement: String).returns(T.nilable(T::Hash[Symbol, T.nilable(String)]))
|
|
166
215
|
end
|
|
167
216
|
def dependency_source(name:, type:, requirement:)
|
|
168
217
|
return unless lockfile
|
|
@@ -223,7 +272,7 @@ module Dependabot
|
|
|
223
272
|
|
|
224
273
|
sig { override.void }
|
|
225
274
|
def check_required_files
|
|
226
|
-
raise "No
|
|
275
|
+
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless get_original_file(PackageManager::MANIFEST_FILENAME)
|
|
227
276
|
end
|
|
228
277
|
|
|
229
278
|
sig { returns(T.nilable(T::Hash[String, T.untyped])) }
|
|
@@ -243,7 +292,10 @@ module Dependabot
|
|
|
243
292
|
def parsed_composer_json
|
|
244
293
|
content = composer_json&.content
|
|
245
294
|
|
|
246
|
-
|
|
295
|
+
if content.nil? || content.strip.empty?
|
|
296
|
+
raise Dependabot::DependencyFileNotParseable,
|
|
297
|
+
composer_json&.path || ""
|
|
298
|
+
end
|
|
247
299
|
|
|
248
300
|
@parsed_composer_json ||= T.let(JSON.parse(content), T.nilable(T::Hash[String, T.untyped]))
|
|
249
301
|
rescue JSON::ParserError
|
|
@@ -252,17 +304,24 @@ module Dependabot
|
|
|
252
304
|
|
|
253
305
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
254
306
|
def composer_json
|
|
255
|
-
@composer_json ||= T.let(
|
|
307
|
+
@composer_json ||= T.let(
|
|
308
|
+
get_original_file(PackageManager::MANIFEST_FILENAME),
|
|
309
|
+
T.nilable(Dependabot::DependencyFile)
|
|
310
|
+
)
|
|
256
311
|
end
|
|
257
312
|
|
|
258
313
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
259
314
|
def lockfile
|
|
260
|
-
@lockfile ||= T.let(
|
|
315
|
+
@lockfile ||= T.let(
|
|
316
|
+
get_original_file(PackageManager::LOCKFILE_FILENAME),
|
|
317
|
+
T.nilable(Dependabot::DependencyFile)
|
|
318
|
+
)
|
|
261
319
|
end
|
|
262
320
|
|
|
263
321
|
sig { returns(String) }
|
|
264
322
|
def composer_version
|
|
265
|
-
@composer_version ||= T.let(Helpers.composer_version(parsed_composer_json, parsed_lockfile),
|
|
323
|
+
@composer_version ||= T.let(Helpers.composer_version(parsed_composer_json, parsed_lockfile),
|
|
324
|
+
T.nilable(String))
|
|
266
325
|
end
|
|
267
326
|
end
|
|
268
327
|
end
|
|
@@ -68,7 +68,7 @@ module Dependabot
|
|
|
68
68
|
SharedHelpers.in_a_temporary_directory(base_directory) do
|
|
69
69
|
write_temporary_dependency_files
|
|
70
70
|
|
|
71
|
-
updated_content = run_update_helper.fetch(
|
|
71
|
+
updated_content = run_update_helper.fetch(PackageManager::LOCKFILE_FILENAME)
|
|
72
72
|
|
|
73
73
|
updated_content = post_process_lockfile(updated_content)
|
|
74
74
|
raise "Expected content to change!" if lockfile.content == updated_content
|
|
@@ -256,9 +256,9 @@ module Dependabot
|
|
|
256
256
|
File.write(file.name, file.content)
|
|
257
257
|
end
|
|
258
258
|
|
|
259
|
-
File.write(
|
|
260
|
-
File.write(
|
|
261
|
-
File.write(
|
|
259
|
+
File.write(PackageManager::MANIFEST_FILENAME, locked_composer_json_content)
|
|
260
|
+
File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content)
|
|
261
|
+
File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
|
|
262
262
|
end
|
|
263
263
|
|
|
264
264
|
def locked_composer_json_content
|
|
@@ -288,7 +288,7 @@ module Dependabot
|
|
|
288
288
|
next content unless Composer::Version.correct?(updated_req)
|
|
289
289
|
|
|
290
290
|
old_req =
|
|
291
|
-
dep.requirements.find { |r| r[:file] ==
|
|
291
|
+
dep.requirements.find { |r| r[:file] == PackageManager::MANIFEST_FILENAME }
|
|
292
292
|
&.fetch(:requirement)
|
|
293
293
|
|
|
294
294
|
# When updating a subdep there won't be an old requirement
|
|
@@ -379,11 +379,11 @@ module Dependabot
|
|
|
379
379
|
def replace_content_hash(content)
|
|
380
380
|
existing_hash = JSON.parse(content).fetch("content-hash")
|
|
381
381
|
SharedHelpers.in_a_temporary_directory do
|
|
382
|
-
File.write(
|
|
382
|
+
File.write(PackageManager::MANIFEST_FILENAME, updated_composer_json_content)
|
|
383
383
|
|
|
384
384
|
content_hash =
|
|
385
385
|
SharedHelpers.run_helper_subprocess(
|
|
386
|
-
command: "
|
|
386
|
+
command: "#{Language::NAME} #{php_helper_path}",
|
|
387
387
|
function: "get_content_hash",
|
|
388
388
|
env: credentials_env,
|
|
389
389
|
args: [Dir.pwd]
|
|
@@ -466,25 +466,25 @@ module Dependabot
|
|
|
466
466
|
|
|
467
467
|
def registry_credentials
|
|
468
468
|
credentials
|
|
469
|
-
.select { |cred| cred.fetch("type") ==
|
|
469
|
+
.select { |cred| cred.fetch("type") == PackageManager::REPOSITORY_KEY }
|
|
470
470
|
.select { |cred| cred["password"] }
|
|
471
471
|
end
|
|
472
472
|
|
|
473
473
|
def initial_platform
|
|
474
|
-
platform_php =
|
|
474
|
+
platform_php = Helpers.capture_platform_php(parsed_composer_json)
|
|
475
475
|
|
|
476
476
|
platform = {}
|
|
477
|
-
platform[
|
|
477
|
+
platform[Language::NAME] = [platform_php] if platform_php.is_a?(String) && requirement_valid?(platform_php)
|
|
478
478
|
|
|
479
479
|
# NOTE: We *don't* include the require-dev PHP version in our initial
|
|
480
480
|
# platform. If we fail to resolve with the PHP version specified in
|
|
481
481
|
# `require` then it will be picked up in a subsequent iteration.
|
|
482
|
-
requirement_php =
|
|
482
|
+
requirement_php = Helpers.php_constraint(parsed_composer_json)
|
|
483
483
|
return platform unless requirement_php.is_a?(String)
|
|
484
484
|
return platform unless requirement_valid?(requirement_php)
|
|
485
485
|
|
|
486
|
-
platform[
|
|
487
|
-
platform[
|
|
486
|
+
platform[Language::NAME] ||= []
|
|
487
|
+
platform[Language::NAME] << requirement_php
|
|
488
488
|
platform
|
|
489
489
|
end
|
|
490
490
|
|
|
@@ -505,16 +505,16 @@ module Dependabot
|
|
|
505
505
|
|
|
506
506
|
def composer_json
|
|
507
507
|
@composer_json ||=
|
|
508
|
-
dependency_files.find { |f| f.name ==
|
|
508
|
+
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
|
|
509
509
|
end
|
|
510
510
|
|
|
511
511
|
def lockfile
|
|
512
512
|
@lockfile ||=
|
|
513
|
-
dependency_files.find { |f| f.name ==
|
|
513
|
+
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
|
|
514
514
|
end
|
|
515
515
|
|
|
516
516
|
def auth_json
|
|
517
|
-
@auth_json ||= dependency_files.find { |f| f.name ==
|
|
517
|
+
@auth_json ||= dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
|
|
518
518
|
end
|
|
519
519
|
|
|
520
520
|
def artifact_dependencies
|
|
@@ -524,7 +524,7 @@ module Dependabot
|
|
|
524
524
|
|
|
525
525
|
def path_dependencies
|
|
526
526
|
@path_dependencies ||=
|
|
527
|
-
dependency_files.select { |f| f.name.end_with?("
|
|
527
|
+
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
|
|
528
528
|
end
|
|
529
529
|
end
|
|
530
530
|
end
|
|
@@ -46,7 +46,7 @@ module Dependabot
|
|
|
46
46
|
private
|
|
47
47
|
|
|
48
48
|
def check_required_files
|
|
49
|
-
raise "No
|
|
49
|
+
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless get_original_file(PackageManager::MANIFEST_FILENAME)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def updated_composer_json_content
|
|
@@ -66,11 +66,11 @@ module Dependabot
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def composer_json
|
|
69
|
-
@composer_json ||= get_original_file(
|
|
69
|
+
@composer_json ||= get_original_file(PackageManager::MANIFEST_FILENAME)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def lockfile
|
|
73
|
-
@lockfile ||= get_original_file(
|
|
73
|
+
@lockfile ||= get_original_file(PackageManager::LOCKFILE_FILENAME)
|
|
74
74
|
end
|
|
75
75
|
end
|
|
76
76
|
end
|
|
@@ -45,8 +45,8 @@ module Dependabot
|
|
|
45
45
|
def self.composer_version(composer_json, parsed_lockfile = nil)
|
|
46
46
|
# If the parsed lockfile has a plugin API version, we return either V1 or V2
|
|
47
47
|
# based on the major version of the lockfile.
|
|
48
|
-
if parsed_lockfile && parsed_lockfile[
|
|
49
|
-
version = Composer::Version.new(parsed_lockfile[
|
|
48
|
+
if parsed_lockfile && parsed_lockfile[PackageManager::PLUGIN_API_VERSION_KEY]
|
|
49
|
+
version = Composer::Version.new(parsed_lockfile[PackageManager::PLUGIN_API_VERSION_KEY])
|
|
50
50
|
major_version = version.canonical_segments.first
|
|
51
51
|
|
|
52
52
|
return major_version.nil? || major_version > 1 ? V2 : V1
|
|
@@ -89,11 +89,84 @@ module Dependabot
|
|
|
89
89
|
nil
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
# Run single composer command returning stdout/stderr
|
|
93
|
+
sig { params(command: String, fingerprint: T.nilable(String)).returns(String) }
|
|
94
|
+
def self.package_manager_run_command(command, fingerprint: nil)
|
|
95
|
+
full_command = "composer #{command}"
|
|
96
|
+
|
|
97
|
+
Dependabot.logger.info("Running composer command: #{full_command}")
|
|
98
|
+
|
|
99
|
+
result = Dependabot::SharedHelpers.run_shell_command(
|
|
100
|
+
full_command,
|
|
101
|
+
fingerprint: "composer #{fingerprint || command}"
|
|
102
|
+
).strip
|
|
103
|
+
|
|
104
|
+
Dependabot.logger.info("Command executed successfully: #{full_command}")
|
|
105
|
+
result
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
Dependabot.logger.error("Error running composer command: #{full_command}, Error: #{e.message}")
|
|
108
|
+
raise
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Example output:
|
|
112
|
+
# [dependabot] ~ $ composer --version
|
|
113
|
+
# Composer version 2.7.7 2024-06-10 22:11:12
|
|
114
|
+
# PHP version 7.4.33 (/usr/bin/php7.4)
|
|
115
|
+
# Run the "diagnose" command to get more detailed diagnostics output.
|
|
116
|
+
# Get the version of the composer and php form the command output
|
|
117
|
+
# @return [Hash] with the composer and php version
|
|
118
|
+
# => { composer: "2.7.7", php: "7.4.33" }
|
|
119
|
+
sig { returns(T::Hash[Symbol, T.nilable(String)]) }
|
|
120
|
+
def self.fetch_composer_and_php_versions
|
|
121
|
+
output = package_manager_run_command("--version").strip
|
|
122
|
+
|
|
123
|
+
composer_version = capture_version(output, /Composer version (?<version>\d+\.\d+\.\d+)/)
|
|
124
|
+
php_version = capture_version(output, /PHP version (?<version>\d+\.\d+\.\d+)/)
|
|
125
|
+
|
|
126
|
+
Dependabot.logger.info("Dependabot running with Composer version: #{composer_version}")
|
|
127
|
+
Dependabot.logger.info("Dependabot running with PHP version: #{php_version}")
|
|
128
|
+
|
|
129
|
+
{ composer: composer_version, php: php_version }
|
|
130
|
+
rescue StandardError => e
|
|
131
|
+
Dependabot.logger.error("Error fetching versions for package manager and language #{name}: #{e.message}")
|
|
132
|
+
{}
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
sig { params(output: String, regex: Regexp).returns(T.nilable(String)) }
|
|
136
|
+
def self.capture_version(output, regex)
|
|
137
|
+
match = output.match(regex)
|
|
138
|
+
match&.named_captures&.fetch("version", nil)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Capture the platform PHP version from composer.json
|
|
142
|
+
sig { params(parsed_composer_json: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
|
|
143
|
+
def self.capture_platform_php(parsed_composer_json)
|
|
144
|
+
capture_platform(parsed_composer_json, Language::NAME)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Capture the platform extension from composer.json
|
|
148
|
+
sig { params(parsed_composer_json: T::Hash[String, T.untyped], name: String).returns(T.nilable(String)) }
|
|
149
|
+
def self.capture_platform(parsed_composer_json, name)
|
|
150
|
+
parsed_composer_json.dig(PackageManager::CONFIG_KEY, PackageManager::PLATFORM_KEY, name)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Capture PHP version constraint from composer.json
|
|
154
|
+
sig { params(parsed_composer_json: T::Hash[String, T.untyped]).returns(T.nilable(String)) }
|
|
155
|
+
def self.php_constraint(parsed_composer_json)
|
|
156
|
+
dependency_constraint(parsed_composer_json, Language::NAME)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Capture extension version constraint from composer.json
|
|
160
|
+
sig { params(parsed_composer_json: T::Hash[String, T.untyped], name: String).returns(T.nilable(String)) }
|
|
161
|
+
def self.dependency_constraint(parsed_composer_json, name)
|
|
162
|
+
parsed_composer_json.dig(PackageManager::REQUIRE_KEY, name)
|
|
163
|
+
end
|
|
164
|
+
|
|
92
165
|
sig { params(composer_json: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
93
166
|
def self.invalid_v2_requirement?(composer_json)
|
|
94
|
-
return false unless composer_json.key?(
|
|
167
|
+
return false unless composer_json.key?(PackageManager::REQUIRE_KEY)
|
|
95
168
|
|
|
96
|
-
composer_json[
|
|
169
|
+
composer_json[PackageManager::REQUIRE_KEY].keys.any? do |key|
|
|
97
170
|
key !~ PLATFORM_PACKAGE_REGEX && key !~ COMPOSER_V2_NAME_REGEX
|
|
98
171
|
end
|
|
99
172
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
require "dependabot/ecosystem"
|
|
6
|
+
require "dependabot/composer/requirement"
|
|
7
|
+
require "dependabot/composer/version"
|
|
8
|
+
|
|
9
|
+
module Dependabot
|
|
10
|
+
module Composer
|
|
11
|
+
class Language < Dependabot::Ecosystem::VersionManager
|
|
12
|
+
extend T::Sig
|
|
13
|
+
|
|
14
|
+
NAME = "php"
|
|
15
|
+
|
|
16
|
+
sig { params(raw_version: String, requirement: T.nilable(Requirement)).void }
|
|
17
|
+
def initialize(raw_version, requirement: nil)
|
|
18
|
+
super(
|
|
19
|
+
NAME,
|
|
20
|
+
Version.new(raw_version),
|
|
21
|
+
[],
|
|
22
|
+
[],
|
|
23
|
+
requirement
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Boolean) }
|
|
28
|
+
def deprecated?
|
|
29
|
+
false
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { returns(T::Boolean) }
|
|
33
|
+
def unsupported?
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -8,22 +8,35 @@ require "dependabot/composer/version"
|
|
|
8
8
|
module Dependabot
|
|
9
9
|
module Composer
|
|
10
10
|
ECOSYSTEM = "composer"
|
|
11
|
-
PACKAGE_MANAGER = "composer"
|
|
12
|
-
|
|
13
|
-
# Keep versions in ascending order
|
|
14
|
-
SUPPORTED_COMPOSER_VERSIONS = T.let([Version.new("2")].freeze, T::Array[Dependabot::Version])
|
|
15
|
-
|
|
16
|
-
DEPRECATED_COMPOSER_VERSIONS = T.let([
|
|
17
|
-
Version.new("1")
|
|
18
|
-
].freeze, T::Array[Dependabot::Version])
|
|
19
11
|
|
|
20
12
|
class PackageManager < Dependabot::Ecosystem::VersionManager
|
|
21
13
|
extend T::Sig
|
|
22
14
|
|
|
15
|
+
NAME = "composer"
|
|
16
|
+
MANIFEST_FILENAME = "composer.json"
|
|
17
|
+
LOCKFILE_FILENAME = "composer.lock"
|
|
18
|
+
AUTH_FILENAME = "auth.json"
|
|
19
|
+
DEPENDENCY_NAME = "composer/composer"
|
|
20
|
+
|
|
21
|
+
REQUIRE_KEY = "require"
|
|
22
|
+
CONFIG_KEY = "config"
|
|
23
|
+
PLATFORM_KEY = "platform"
|
|
24
|
+
PLUGIN_API_VERSION_KEY = "plugin-api-version"
|
|
25
|
+
REPOSITORY_KEY = "composer_repository"
|
|
26
|
+
|
|
27
|
+
# Keep versions in ascending order
|
|
28
|
+
SUPPORTED_COMPOSER_VERSIONS = T.let([Version.new("2")].freeze, T::Array[Dependabot::Version])
|
|
29
|
+
|
|
30
|
+
# Currently, we don't support any deprecated versions of Composer
|
|
31
|
+
# When a version is going to be unsupported, it will be added here for a while to give users time to upgrade
|
|
32
|
+
# Example for deprecation:
|
|
33
|
+
# DEPRECATED_COMPOSER_VERSIONS = T.let([Version.new("1")].freeze, T::Array[Dependabot::Version])
|
|
34
|
+
DEPRECATED_COMPOSER_VERSIONS = T.let([].freeze, T::Array[Dependabot::Version])
|
|
35
|
+
|
|
23
36
|
sig { params(raw_version: String).void }
|
|
24
37
|
def initialize(raw_version)
|
|
25
38
|
super(
|
|
26
|
-
|
|
39
|
+
NAME,
|
|
27
40
|
Version.new(raw_version),
|
|
28
41
|
DEPRECATED_COMPOSER_VERSIONS,
|
|
29
42
|
SUPPORTED_COMPOSER_VERSIONS,
|
|
@@ -107,7 +107,7 @@ module Dependabot
|
|
|
107
107
|
.select { |r| r.is_a?(Hash) }
|
|
108
108
|
|
|
109
109
|
urls = repositories
|
|
110
|
-
.select { |h| h["type"] ==
|
|
110
|
+
.select { |h| h["type"] == PackageManager::NAME }
|
|
111
111
|
.filter_map { |h| h["url"] }
|
|
112
112
|
.map { |url| url.gsub(%r{\/$}, "") + "/packages.json" }
|
|
113
113
|
|
|
@@ -170,7 +170,7 @@ module Dependabot
|
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
def registry_credentials
|
|
173
|
-
credentials.select { |cred| cred["type"] ==
|
|
173
|
+
credentials.select { |cred| cred["type"] == PackageManager::REPOSITORY_KEY } +
|
|
174
174
|
auth_json_credentials
|
|
175
175
|
end
|
|
176
176
|
|
|
@@ -191,14 +191,16 @@ module Dependabot
|
|
|
191
191
|
|
|
192
192
|
def composer_file
|
|
193
193
|
composer_file =
|
|
194
|
-
dependency_files.find
|
|
195
|
-
|
|
194
|
+
dependency_files.find do |f|
|
|
195
|
+
f.name == PackageManager::MANIFEST_FILENAME
|
|
196
|
+
end
|
|
197
|
+
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless composer_file
|
|
196
198
|
|
|
197
199
|
composer_file
|
|
198
200
|
end
|
|
199
201
|
|
|
200
202
|
def auth_json
|
|
201
|
-
dependency_files.find { |f| f.name ==
|
|
203
|
+
dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
|
|
202
204
|
end
|
|
203
205
|
|
|
204
206
|
def ignore_requirements
|
|
@@ -108,7 +108,7 @@ module Dependabot
|
|
|
108
108
|
|
|
109
109
|
def write_dependency_file(unlock_requirement:)
|
|
110
110
|
File.write(
|
|
111
|
-
|
|
111
|
+
PackageManager::MANIFEST_FILENAME,
|
|
112
112
|
prepared_composer_json_content(
|
|
113
113
|
unlock_requirement: unlock_requirement
|
|
114
114
|
)
|
|
@@ -123,11 +123,11 @@ module Dependabot
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def write_lockfile
|
|
126
|
-
File.write(
|
|
126
|
+
File.write(PackageManager::LOCKFILE_FILENAME, lockfile.content) if lockfile
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def write_auth_file
|
|
130
|
-
File.write(
|
|
130
|
+
File.write(PackageManager::AUTH_FILENAME, auth_json.content) if auth_json
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def transitory_failure?(error)
|
|
@@ -175,9 +175,9 @@ module Dependabot
|
|
|
175
175
|
composer_platform_extensions.each do |extension, requirements|
|
|
176
176
|
next unless version_for_reqs(requirements)
|
|
177
177
|
|
|
178
|
-
json[
|
|
179
|
-
json[
|
|
180
|
-
json[
|
|
178
|
+
json[PackageManager::CONFIG_KEY] ||= {}
|
|
179
|
+
json[PackageManager::CONFIG_KEY][PackageManager::PLATFORM_KEY] ||= {}
|
|
180
|
+
json[PackageManager::CONFIG_KEY][PackageManager::PLATFORM_KEY][extension] =
|
|
181
181
|
version_for_reqs(requirements)
|
|
182
182
|
end
|
|
183
183
|
|
|
@@ -338,8 +338,10 @@ module Dependabot
|
|
|
338
338
|
# composer.json. In this case we just ignore the dependency.
|
|
339
339
|
nil
|
|
340
340
|
elsif error.message.include?("does not match the expected JSON schema")
|
|
341
|
-
msg = "Composer failed to parse your
|
|
342
|
-
"
|
|
341
|
+
msg = "Composer failed to parse your #{PackageManager::MANIFEST_FILENAME}" \
|
|
342
|
+
"as it does not match the expected JSON schema.\n" \
|
|
343
|
+
"Run `composer validate` to check your #{PackageManager::MANIFEST_FILENAME} " \
|
|
344
|
+
"and #{PackageManager::LOCKFILE_FILENAME} files.\n\n" \
|
|
343
345
|
"See https://getcomposer.org/doc/04-schema.md for details on the schema."
|
|
344
346
|
raise Dependabot::DependencyFileNotParseable, msg
|
|
345
347
|
else
|
|
@@ -452,20 +454,22 @@ module Dependabot
|
|
|
452
454
|
end
|
|
453
455
|
|
|
454
456
|
def initial_platform
|
|
455
|
-
platform_php =
|
|
457
|
+
platform_php = Helpers.capture_platform_php(parsed_composer_file)
|
|
456
458
|
|
|
457
459
|
platform = {}
|
|
458
|
-
|
|
460
|
+
if platform_php.is_a?(String) && requirement_valid?(platform_php)
|
|
461
|
+
platform[Dependabot::Composer::Language::NAME] = [platform_php]
|
|
462
|
+
end
|
|
459
463
|
|
|
460
464
|
# NOTE: We *don't* include the require-dev PHP version in our initial
|
|
461
465
|
# platform. If we fail to resolve with the PHP version specified in
|
|
462
466
|
# `require` then it will be picked up in a subsequent iteration.
|
|
463
|
-
requirement_php =
|
|
467
|
+
requirement_php = Helpers.php_constraint(parsed_composer_file)
|
|
464
468
|
return platform unless requirement_php.is_a?(String)
|
|
465
469
|
return platform unless requirement_valid?(requirement_php)
|
|
466
470
|
|
|
467
|
-
platform[
|
|
468
|
-
platform[
|
|
471
|
+
platform[Dependabot::Composer::Language::NAME] ||= []
|
|
472
|
+
platform[Dependabot::Composer::Language::NAME] << requirement_php
|
|
469
473
|
platform
|
|
470
474
|
end
|
|
471
475
|
|
|
@@ -479,12 +483,12 @@ module Dependabot
|
|
|
479
483
|
|
|
480
484
|
def composer_file
|
|
481
485
|
@composer_file ||=
|
|
482
|
-
dependency_files.find { |f| f.name ==
|
|
486
|
+
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
|
|
483
487
|
end
|
|
484
488
|
|
|
485
489
|
def path_dependency_files
|
|
486
490
|
@path_dependency_files ||=
|
|
487
|
-
dependency_files.select { |f| f.name.end_with?("
|
|
491
|
+
dependency_files.select { |f| f.name.end_with?("/#{PackageManager::MANIFEST_FILENAME}") }
|
|
488
492
|
end
|
|
489
493
|
|
|
490
494
|
def zipped_path_dependency_files
|
|
@@ -494,11 +498,11 @@ module Dependabot
|
|
|
494
498
|
|
|
495
499
|
def lockfile
|
|
496
500
|
@lockfile ||=
|
|
497
|
-
dependency_files.find { |f| f.name ==
|
|
501
|
+
dependency_files.find { |f| f.name == PackageManager::LOCKFILE_FILENAME }
|
|
498
502
|
end
|
|
499
503
|
|
|
500
504
|
def auth_json
|
|
501
|
-
@auth_json ||= dependency_files.find { |f| f.name ==
|
|
505
|
+
@auth_json ||= dependency_files.find { |f| f.name == PackageManager::AUTH_FILENAME }
|
|
502
506
|
end
|
|
503
507
|
|
|
504
508
|
def requirement_valid?(req_string)
|
|
@@ -516,7 +520,7 @@ module Dependabot
|
|
|
516
520
|
|
|
517
521
|
def registry_credentials
|
|
518
522
|
credentials
|
|
519
|
-
.select { |cred| cred["type"] ==
|
|
523
|
+
.select { |cred| cred["type"] == PackageManager::REPOSITORY_KEY }
|
|
520
524
|
.select { |cred| cred["password"] }
|
|
521
525
|
end
|
|
522
526
|
end
|
|
@@ -139,8 +139,8 @@ module Dependabot
|
|
|
139
139
|
|
|
140
140
|
def composer_file
|
|
141
141
|
composer_file =
|
|
142
|
-
dependency_files.find { |f| f.name ==
|
|
143
|
-
raise "No
|
|
142
|
+
dependency_files.find { |f| f.name == PackageManager::MANIFEST_FILENAME }
|
|
143
|
+
raise "No #{PackageManager::MANIFEST_FILENAME}!" unless composer_file
|
|
144
144
|
|
|
145
145
|
composer_file
|
|
146
146
|
end
|
data/lib/dependabot/composer.rb
CHANGED
|
@@ -12,6 +12,7 @@ require "dependabot/composer/requirement"
|
|
|
12
12
|
require "dependabot/composer/version"
|
|
13
13
|
require "dependabot/composer/helpers"
|
|
14
14
|
require "dependabot/composer/package_manager"
|
|
15
|
+
require "dependabot/composer/language"
|
|
15
16
|
|
|
16
17
|
require "dependabot/pull_request_creator/labeler"
|
|
17
18
|
Dependabot::PullRequestCreator::Labeler
|
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.
|
|
4
|
+
version: 0.289.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-
|
|
11
|
+
date: 2024-12-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.
|
|
19
|
+
version: 0.289.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.
|
|
26
|
+
version: 0.289.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: debug
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -261,6 +261,7 @@ files:
|
|
|
261
261
|
- lib/dependabot/composer/file_updater/lockfile_updater.rb
|
|
262
262
|
- lib/dependabot/composer/file_updater/manifest_updater.rb
|
|
263
263
|
- lib/dependabot/composer/helpers.rb
|
|
264
|
+
- lib/dependabot/composer/language.rb
|
|
264
265
|
- lib/dependabot/composer/metadata_finder.rb
|
|
265
266
|
- lib/dependabot/composer/native_helpers.rb
|
|
266
267
|
- lib/dependabot/composer/package_manager.rb
|
|
@@ -275,7 +276,7 @@ licenses:
|
|
|
275
276
|
- MIT
|
|
276
277
|
metadata:
|
|
277
278
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
278
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
279
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.289.0
|
|
279
280
|
post_install_message:
|
|
280
281
|
rdoc_options: []
|
|
281
282
|
require_paths:
|