dependabot-composer 0.287.0 → 0.289.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|