dependabot-common 0.290.0 → 0.292.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/command_helpers.rb +226 -0
- data/lib/dependabot/config/update_config.rb +17 -0
- data/lib/dependabot/ecosystem.rb +49 -13
- data/lib/dependabot/errors.rb +27 -0
- data/lib/dependabot/file_fetchers/base.rb +1 -1
- data/lib/dependabot/notices.rb +23 -15
- data/lib/dependabot/shared_helpers.rb +35 -15
- data/lib/dependabot/workspace/git.rb +1 -1
- data/lib/dependabot.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc0d7a7acc0f4dcb2e25a622e816fd82a11a1553eecf85e6ae1e442ce5750ffb
|
4
|
+
data.tar.gz: 29e3f86968cb122e49a26f2866ee3554cb07ddbbda305e17d02ee4cb10099282
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14e6659eaa880f07f1d2562d89ba71a5f581bcf431edcc49983bf7b6819be8567e0bd252606daa744e0c7d3523d2fd1970e22699b1053179f648009517ca332f
|
7
|
+
data.tar.gz: 47e39f274165302b4a2da440f242ffb7fd2e635c535666d3c09dc968090fbf7187c614ce1ed642f3ea472093820918675bb7ae465d4cad48a8c644d7ef5d6db8
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "open3"
|
5
|
+
require "timeout"
|
6
|
+
require "sorbet-runtime"
|
7
|
+
require "shellwords"
|
8
|
+
|
9
|
+
module Dependabot
|
10
|
+
module CommandHelpers
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
module TIMEOUTS
|
14
|
+
NO_TIME_OUT = -1 # No timeout
|
15
|
+
LOCAL = 30 # 30 seconds
|
16
|
+
NETWORK = 120 # 2 minutes
|
17
|
+
LONG_RUNNING = 300 # 5 minutes
|
18
|
+
DEFAULT = 900 # 15 minutes
|
19
|
+
end
|
20
|
+
|
21
|
+
class ProcessStatus
|
22
|
+
extend T::Sig
|
23
|
+
|
24
|
+
sig { params(process_status: Process::Status, custom_exitstatus: T.nilable(Integer)).void }
|
25
|
+
def initialize(process_status, custom_exitstatus = nil)
|
26
|
+
@process_status = process_status
|
27
|
+
@custom_exitstatus = custom_exitstatus
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the exit status, either from the process status or the custom one
|
31
|
+
sig { returns(Integer) }
|
32
|
+
def exitstatus
|
33
|
+
@custom_exitstatus || @process_status.exitstatus || 0
|
34
|
+
end
|
35
|
+
|
36
|
+
# Determine if the process was successful
|
37
|
+
sig { returns(T::Boolean) }
|
38
|
+
def success?
|
39
|
+
@custom_exitstatus.nil? ? @process_status.success? || false : @custom_exitstatus.zero?
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the PID of the process (if available)
|
43
|
+
sig { returns(T.nilable(Integer)) }
|
44
|
+
def pid
|
45
|
+
@process_status.pid
|
46
|
+
end
|
47
|
+
|
48
|
+
sig { returns(T.nilable(Integer)) }
|
49
|
+
def termsig
|
50
|
+
@process_status.termsig
|
51
|
+
end
|
52
|
+
|
53
|
+
# String representation of the status
|
54
|
+
sig { returns(String) }
|
55
|
+
def to_s
|
56
|
+
if @custom_exitstatus
|
57
|
+
"pid #{pid || 'unknown'}: exit #{@custom_exitstatus} (custom status)"
|
58
|
+
else
|
59
|
+
@process_status.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# rubocop:disable Metrics/AbcSize
|
65
|
+
# rubocop:disable Metrics/MethodLength
|
66
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
67
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
68
|
+
sig do
|
69
|
+
params(
|
70
|
+
env_cmd: T::Array[T.any(T::Hash[String, String], String)],
|
71
|
+
stdin_data: T.nilable(String),
|
72
|
+
stderr_to_stdout: T::Boolean,
|
73
|
+
timeout: Integer
|
74
|
+
).returns([T.nilable(String), T.nilable(String), T.nilable(ProcessStatus), Float])
|
75
|
+
end
|
76
|
+
def self.capture3_with_timeout(
|
77
|
+
env_cmd,
|
78
|
+
stdin_data: nil,
|
79
|
+
stderr_to_stdout: false,
|
80
|
+
timeout: TIMEOUTS::DEFAULT
|
81
|
+
)
|
82
|
+
|
83
|
+
stdout = T.let("", String)
|
84
|
+
stderr = T.let("", String)
|
85
|
+
status = T.let(nil, T.nilable(ProcessStatus))
|
86
|
+
pid = T.let(nil, T.untyped)
|
87
|
+
start_time = Time.now
|
88
|
+
|
89
|
+
begin
|
90
|
+
T.unsafe(Open3).popen3(*env_cmd) do |stdin, stdout_io, stderr_io, wait_thr| # rubocop:disable Metrics/BlockLength
|
91
|
+
pid = wait_thr.pid
|
92
|
+
Dependabot.logger.info("Started process PID: #{pid} with command: #{env_cmd.join(' ')}")
|
93
|
+
|
94
|
+
# Write to stdin if input data is provided
|
95
|
+
stdin&.write(stdin_data) if stdin_data
|
96
|
+
stdin&.close
|
97
|
+
|
98
|
+
stdout_io.sync = true
|
99
|
+
stderr_io.sync = true
|
100
|
+
|
101
|
+
# Array to monitor both stdout and stderr
|
102
|
+
ios = [stdout_io, stderr_io]
|
103
|
+
|
104
|
+
last_output_time = Time.now # Track the last time output was received
|
105
|
+
|
106
|
+
until ios.empty?
|
107
|
+
if timeout.positive?
|
108
|
+
# Calculate remaining timeout dynamically
|
109
|
+
remaining_timeout = timeout - (Time.now - last_output_time)
|
110
|
+
|
111
|
+
# Raise an error if timeout is exceeded
|
112
|
+
if remaining_timeout <= 0
|
113
|
+
Dependabot.logger.warn("Process PID: #{pid} timed out after #{timeout}s. Terminating...")
|
114
|
+
terminate_process(pid)
|
115
|
+
status = ProcessStatus.new(wait_thr.value, 124)
|
116
|
+
raise Timeout::Error, "Timed out due to inactivity after #{timeout} seconds"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Use IO.select with a dynamically calculated short timeout
|
121
|
+
ready_ios = IO.select(ios, nil, nil, 0)
|
122
|
+
|
123
|
+
# Process ready IO streams
|
124
|
+
ready_ios&.first&.each do |io|
|
125
|
+
# 1. Read data from the stream
|
126
|
+
io.set_encoding("BINARY")
|
127
|
+
data = io.read_nonblock(1024)
|
128
|
+
|
129
|
+
# 2. Force encoding to UTF-8 (for proper conversion)
|
130
|
+
data.force_encoding("UTF-8")
|
131
|
+
|
132
|
+
# 3. Convert to UTF-8 safely, handling invalid/undefined bytes
|
133
|
+
data = data.encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")
|
134
|
+
|
135
|
+
# Reset the timeout if data is received
|
136
|
+
last_output_time = Time.now unless data.empty?
|
137
|
+
|
138
|
+
# 4. Append data to the appropriate stream
|
139
|
+
if io == stdout_io
|
140
|
+
stdout += data
|
141
|
+
else
|
142
|
+
stderr += data unless stderr_to_stdout
|
143
|
+
stdout += data if stderr_to_stdout
|
144
|
+
end
|
145
|
+
rescue EOFError
|
146
|
+
# Remove the stream when EOF is reached
|
147
|
+
ios.delete(io)
|
148
|
+
rescue IO::WaitReadable
|
149
|
+
# Continue when IO is not ready yet
|
150
|
+
next
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
status = ProcessStatus.new(wait_thr.value)
|
155
|
+
Dependabot.logger.info("Process PID: #{pid} completed with status: #{status}")
|
156
|
+
end
|
157
|
+
rescue Timeout::Error => e
|
158
|
+
Dependabot.logger.error("Process PID: #{pid} failed due to timeout: #{e.message}")
|
159
|
+
terminate_process(pid)
|
160
|
+
|
161
|
+
# Append timeout message only to stderr without interfering with stdout
|
162
|
+
stderr += "\n#{e.message}" unless stderr_to_stdout
|
163
|
+
stdout += "\n#{e.message}" if stderr_to_stdout
|
164
|
+
rescue Errno::ENOENT => e
|
165
|
+
Dependabot.logger.error("Command failed: #{e.message}")
|
166
|
+
stderr += e.message unless stderr_to_stdout
|
167
|
+
stdout += e.message if stderr_to_stdout
|
168
|
+
end
|
169
|
+
|
170
|
+
elapsed_time = Time.now - start_time
|
171
|
+
Dependabot.logger.info("Total execution time: #{elapsed_time.round(2)} seconds")
|
172
|
+
[stdout, stderr, status, elapsed_time]
|
173
|
+
end
|
174
|
+
# rubocop:enable Metrics/AbcSize
|
175
|
+
# rubocop:enable Metrics/MethodLength
|
176
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
177
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
178
|
+
|
179
|
+
# Terminate a process by PID
|
180
|
+
sig { params(pid: T.nilable(Integer)).void }
|
181
|
+
def self.terminate_process(pid)
|
182
|
+
return unless pid
|
183
|
+
|
184
|
+
begin
|
185
|
+
if process_alive?(pid)
|
186
|
+
Process.kill("TERM", pid) # Attempt graceful termination
|
187
|
+
sleep(0.5) # Allow process to terminate
|
188
|
+
end
|
189
|
+
if process_alive?(pid)
|
190
|
+
Process.kill("KILL", pid) # Forcefully kill if still running
|
191
|
+
end
|
192
|
+
rescue Errno::EPERM
|
193
|
+
Dependabot.logger.error("Insufficient permissions to terminate process: #{pid}")
|
194
|
+
ensure
|
195
|
+
begin
|
196
|
+
Process.waitpid(pid)
|
197
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
198
|
+
# Process has already exited
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Check if the process is still alive
|
204
|
+
sig { params(pid: T.nilable(Integer)).returns(T::Boolean) }
|
205
|
+
def self.process_alive?(pid)
|
206
|
+
return false if pid.nil?
|
207
|
+
|
208
|
+
begin
|
209
|
+
Process.kill(0, pid) # Check if the process exists
|
210
|
+
true
|
211
|
+
rescue Errno::ESRCH
|
212
|
+
false
|
213
|
+
rescue Errno::EPERM
|
214
|
+
Dependabot.logger.error("Insufficient permissions to check process: #{pid}")
|
215
|
+
false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Escape shell commands to ensure safe execution
|
220
|
+
sig { params(command: String).returns(String) }
|
221
|
+
def self.escape_command(command)
|
222
|
+
command_parts = command.split.map(&:strip).reject(&:empty?)
|
223
|
+
Shellwords.join(command_parts)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -32,6 +32,10 @@ module Dependabot
|
|
32
32
|
normalizer = name_normaliser_for(dependency)
|
33
33
|
dep_name = T.must(normalizer).call(dependency.name)
|
34
34
|
|
35
|
+
if dependency.version.nil? && dependency.requirements.any?
|
36
|
+
dependency = extract_base_version_from_requirement(dependency)
|
37
|
+
end
|
38
|
+
|
35
39
|
@ignore_conditions
|
36
40
|
.select { |ic| self.class.wildcard_match?(T.must(normalizer).call(ic.dependency_name), dep_name) }
|
37
41
|
.map { |ic| ic.ignored_versions(dependency, security_updates_only) }
|
@@ -40,6 +44,19 @@ module Dependabot
|
|
40
44
|
.uniq
|
41
45
|
end
|
42
46
|
|
47
|
+
sig { params(dependency: Dependency).returns(Dependency) }
|
48
|
+
def extract_base_version_from_requirement(dependency)
|
49
|
+
requirements = dependency.requirements
|
50
|
+
requirement = T.must(requirements.first)[:requirement]
|
51
|
+
version = requirement&.match(/\d+\.\d+\.\d+/)&.to_s
|
52
|
+
Dependabot::Dependency.new(
|
53
|
+
name: dependency.name,
|
54
|
+
version: version,
|
55
|
+
requirements: dependency.requirements,
|
56
|
+
package_manager: dependency.package_manager
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
43
60
|
sig { params(wildcard_string: T.nilable(String), candidate_string: T.nilable(String)).returns(T::Boolean) }
|
44
61
|
def self.wildcard_match?(wildcard_string, candidate_string)
|
45
62
|
return false unless wildcard_string && candidate_string
|
data/lib/dependabot/ecosystem.rb
CHANGED
@@ -17,30 +17,38 @@ module Dependabot
|
|
17
17
|
abstract!
|
18
18
|
# Initialize version information for a package manager or language.
|
19
19
|
# @param name [String] the name of the package manager or language (e.g., "bundler", "ruby").
|
20
|
-
# @param
|
20
|
+
# @param detected_version [Dependabot::Version] the detected version of the package manager or language.
|
21
|
+
# @param version [Dependabot::Version] the version dependabots run on.
|
21
22
|
# @param deprecated_versions [Array<Dependabot::Version>] an array of deprecated versions.
|
22
23
|
# @param supported_versions [Array<Dependabot::Version>] an array of supported versions.
|
23
24
|
# @param requirement [Dependabot::Requirement] an array of requirements.
|
24
25
|
# @example
|
25
|
-
# VersionManager.new(
|
26
|
+
# VersionManager.new(
|
27
|
+
# name: "bundler",
|
28
|
+
# version: Version.new("2.1.4"),
|
29
|
+
# requirement: nil
|
30
|
+
# )
|
26
31
|
sig do
|
27
32
|
params(
|
28
33
|
name: String,
|
29
|
-
|
34
|
+
detected_version: T.nilable(Dependabot::Version),
|
35
|
+
version: T.nilable(Dependabot::Version),
|
30
36
|
deprecated_versions: T::Array[Dependabot::Version],
|
31
37
|
supported_versions: T::Array[Dependabot::Version],
|
32
38
|
requirement: T.nilable(Dependabot::Requirement)
|
33
39
|
).void
|
34
40
|
end
|
35
41
|
def initialize(
|
36
|
-
name
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
name:,
|
43
|
+
detected_version: nil,
|
44
|
+
version: nil,
|
45
|
+
deprecated_versions: [],
|
46
|
+
supported_versions: [],
|
47
|
+
requirement: nil
|
41
48
|
)
|
42
49
|
@name = T.let(name, String)
|
43
|
-
@
|
50
|
+
@detected_version = T.let(detected_version || version, T.nilable(Dependabot::Version))
|
51
|
+
@version = T.let(version, T.nilable(Dependabot::Version))
|
44
52
|
@deprecated_versions = T.let(deprecated_versions, T::Array[Dependabot::Version])
|
45
53
|
@supported_versions = T.let(supported_versions, T::Array[Dependabot::Version])
|
46
54
|
@requirement = T.let(requirement, T.nilable(Dependabot::Requirement))
|
@@ -52,10 +60,16 @@ module Dependabot
|
|
52
60
|
sig { returns(String) }
|
53
61
|
attr_reader :name
|
54
62
|
|
63
|
+
# The current version of the package manager or language.
|
64
|
+
# @example
|
65
|
+
# detected_version #=> Dependabot::Version.new("2")
|
66
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
67
|
+
attr_reader :detected_version
|
68
|
+
|
55
69
|
# The current version of the package manager or language.
|
56
70
|
# @example
|
57
71
|
# version #=> Dependabot::Version.new("2.1.4")
|
58
|
-
sig { returns(Dependabot::Version) }
|
72
|
+
sig { returns(T.nilable(Dependabot::Version)) }
|
59
73
|
attr_reader :version
|
60
74
|
|
61
75
|
# Returns an array of deprecated versions of the package manager.
|
@@ -76,16 +90,34 @@ module Dependabot
|
|
76
90
|
sig { returns(T.nilable(Dependabot::Requirement)) }
|
77
91
|
attr_reader :requirement
|
78
92
|
|
93
|
+
# The version of the package manager or language as a string.
|
94
|
+
# @example
|
95
|
+
# version_to_s #=> "2.1"
|
96
|
+
sig { returns(String) }
|
97
|
+
def version_to_s
|
98
|
+
version.to_s
|
99
|
+
end
|
100
|
+
|
101
|
+
# The raw version of the package manager or language.
|
102
|
+
# @example
|
103
|
+
# raw_version #=> "2.1.4"
|
104
|
+
sig { returns(String) }
|
105
|
+
def version_to_raw_s
|
106
|
+
version&.to_semver.to_s
|
107
|
+
end
|
108
|
+
|
79
109
|
# Checks if the current version is deprecated.
|
80
110
|
# Returns true if the version is in the deprecated_versions array; false otherwise.
|
81
111
|
# @example
|
82
112
|
# deprecated? #=> true
|
83
113
|
sig { returns(T::Boolean) }
|
84
114
|
def deprecated?
|
115
|
+
return false unless detected_version
|
116
|
+
|
85
117
|
# If the version is unsupported, the unsupported error is getting raised separately.
|
86
118
|
return false if unsupported?
|
87
119
|
|
88
|
-
deprecated_versions.include?(
|
120
|
+
deprecated_versions.include?(detected_version)
|
89
121
|
end
|
90
122
|
|
91
123
|
# Checks if the current version is unsupported.
|
@@ -93,16 +125,20 @@ module Dependabot
|
|
93
125
|
# unsupported? #=> false
|
94
126
|
sig { returns(T::Boolean) }
|
95
127
|
def unsupported?
|
128
|
+
return false unless detected_version
|
129
|
+
|
96
130
|
return false if supported_versions.empty?
|
97
131
|
|
98
132
|
# Check if the version is not supported
|
99
|
-
supported_versions.all? { |supported| supported >
|
133
|
+
supported_versions.all? { |supported| supported > detected_version }
|
100
134
|
end
|
101
135
|
|
102
136
|
# Raises an error if the current package manager or language version is unsupported.
|
103
137
|
# If the version is unsupported, it raises a ToolVersionNotSupported error.
|
104
138
|
sig { void }
|
105
139
|
def raise_if_unsupported!
|
140
|
+
return unless detected_version
|
141
|
+
|
106
142
|
return unless unsupported?
|
107
143
|
|
108
144
|
# Example: v2.*, v3.*
|
@@ -110,7 +146,7 @@ module Dependabot
|
|
110
146
|
|
111
147
|
raise ToolVersionNotSupported.new(
|
112
148
|
name,
|
113
|
-
|
149
|
+
detected_version.to_s,
|
114
150
|
supported_versions_message
|
115
151
|
)
|
116
152
|
end
|
data/lib/dependabot/errors.rb
CHANGED
@@ -83,6 +83,11 @@ module Dependabot
|
|
83
83
|
# and responsibility for fixing it is on them, not us. As a result we
|
84
84
|
# quietly log these as errors
|
85
85
|
{ "error-type": "server_error" }
|
86
|
+
when BadRequirementError
|
87
|
+
{
|
88
|
+
"error-type": "illformed_requirement",
|
89
|
+
"error-detail": { message: error.message }
|
90
|
+
}
|
86
91
|
when *Octokit::RATE_LIMITED_ERRORS
|
87
92
|
# If we get a rate-limited error we let dependabot-api handle the
|
88
93
|
# retry by re-enqueing the update job after the reset
|
@@ -144,6 +149,11 @@ module Dependabot
|
|
144
149
|
"error-type": "git_dependencies_not_reachable",
|
145
150
|
"error-detail": { "dependency-urls": error.dependency_urls }
|
146
151
|
}
|
152
|
+
when Dependabot::UnresolvableVersionError
|
153
|
+
{
|
154
|
+
"error-type": "unresolvable_version",
|
155
|
+
"error-detail": { dependencies: error.dependencies }
|
156
|
+
}
|
147
157
|
when Dependabot::NotImplemented
|
148
158
|
{
|
149
159
|
"error-type": "not_implemented",
|
@@ -661,6 +671,23 @@ module Dependabot
|
|
661
671
|
end
|
662
672
|
end
|
663
673
|
|
674
|
+
class UnresolvableVersionError < DependabotError
|
675
|
+
extend T::Sig
|
676
|
+
|
677
|
+
sig { returns(T::Array[String]) }
|
678
|
+
attr_reader :dependencies
|
679
|
+
|
680
|
+
sig { params(dependencies: T::Array[String]).void }
|
681
|
+
def initialize(dependencies)
|
682
|
+
@dependencies = dependencies
|
683
|
+
|
684
|
+
msg = "Unable to determine semantic version from tags or commits for dependencies. " \
|
685
|
+
"Dependencies must have a tag or commit that references a semantic version. " \
|
686
|
+
"Affected dependencies: #{@dependencies.join(', ')}"
|
687
|
+
super(msg)
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
664
691
|
class GitDependenciesNotReachable < DependabotError
|
665
692
|
extend T::Sig
|
666
693
|
|
@@ -311,7 +311,7 @@ module Dependabot
|
|
311
311
|
|
312
312
|
SharedHelpers.with_git_configured(credentials: credentials) do
|
313
313
|
Dir.chdir(T.must(repo_contents_path)) do
|
314
|
-
return SharedHelpers.run_shell_command("git rev-parse HEAD").strip
|
314
|
+
return SharedHelpers.run_shell_command("git rev-parse HEAD", stderr_to_stdout: false).strip
|
315
315
|
end
|
316
316
|
end
|
317
317
|
end
|
data/lib/dependabot/notices.rb
CHANGED
@@ -71,15 +71,20 @@ module Dependabot
|
|
71
71
|
# Generates a description for supported versions.
|
72
72
|
# @param supported_versions [Array<Dependabot::Version>, nil] The supported versions of the package manager.
|
73
73
|
# @param support_later_versions [Boolean] Whether later versions are supported.
|
74
|
+
# @param version_manager_type [Symbol] The type of entity being deprecated i.e. :language or :package_manager
|
74
75
|
# @return [String, nil] The generated description or nil if no supported versions are provided.
|
75
76
|
sig do
|
76
77
|
params(
|
77
78
|
supported_versions: T.nilable(T::Array[Dependabot::Version]),
|
78
|
-
support_later_versions: T::Boolean
|
79
|
+
support_later_versions: T::Boolean,
|
80
|
+
version_manager_type: Symbol
|
79
81
|
).returns(String)
|
80
82
|
end
|
81
|
-
def self.generate_supported_versions_description(
|
82
|
-
|
83
|
+
def self.generate_supported_versions_description(
|
84
|
+
supported_versions, support_later_versions, version_manager_type = :package_manager
|
85
|
+
)
|
86
|
+
entity_text = version_manager_type == :language ? "language" : "package manager"
|
87
|
+
return "Please upgrade your #{entity_text} version" unless supported_versions&.any?
|
83
88
|
|
84
89
|
versions_string = supported_versions.map { |version| "`v#{version}`" }
|
85
90
|
|
@@ -94,25 +99,28 @@ module Dependabot
|
|
94
99
|
"Please upgrade to one of the following versions: #{versions_string}#{later_description}."
|
95
100
|
end
|
96
101
|
|
97
|
-
# Generates a deprecation notice for the given
|
98
|
-
# @param
|
99
|
-
# @
|
102
|
+
# Generates a deprecation notice for the given version manager.
|
103
|
+
# @param version_manager [VersionManager] The version manager object.
|
104
|
+
# @param version_manager_type [Symbol] The version manager type e.g. :language or :package_manager
|
105
|
+
# @return [Notice, nil] The generated deprecation notice or nil if the version manager is not deprecated.
|
100
106
|
sig do
|
101
107
|
params(
|
102
|
-
|
108
|
+
version_manager: Ecosystem::VersionManager,
|
109
|
+
version_manager_type: Symbol
|
103
110
|
).returns(T.nilable(Notice))
|
104
111
|
end
|
105
|
-
def self.
|
106
|
-
return nil unless
|
112
|
+
def self.generate_deprecation_notice(version_manager, version_manager_type = :package_manager)
|
113
|
+
return nil unless version_manager.deprecated?
|
107
114
|
|
108
115
|
mode = NoticeMode::WARN
|
109
116
|
supported_versions_description = generate_supported_versions_description(
|
110
|
-
|
111
|
-
|
117
|
+
version_manager.supported_versions,
|
118
|
+
version_manager.support_later_versions?,
|
119
|
+
version_manager_type
|
112
120
|
)
|
113
|
-
notice_type = "#{
|
114
|
-
title = "Package manager deprecation notice"
|
115
|
-
description = "Dependabot will stop supporting `#{
|
121
|
+
notice_type = "#{version_manager.name}_deprecated_warn"
|
122
|
+
title = version_manager_type == :language ? "Language deprecation notice" : "Package manager deprecation notice"
|
123
|
+
description = "Dependabot will stop supporting `#{version_manager.name} v#{version_manager.detected_version}`!"
|
116
124
|
|
117
125
|
## Add the supported versions to the description
|
118
126
|
description += "\n\n#{supported_versions_description}\n" unless supported_versions_description.empty?
|
@@ -120,7 +128,7 @@ module Dependabot
|
|
120
128
|
Notice.new(
|
121
129
|
mode: mode,
|
122
130
|
type: notice_type,
|
123
|
-
package_manager_name:
|
131
|
+
package_manager_name: version_manager.name,
|
124
132
|
title: title,
|
125
133
|
description: description,
|
126
134
|
show_in_pr: true,
|
@@ -7,7 +7,6 @@ require "excon"
|
|
7
7
|
require "fileutils"
|
8
8
|
require "json"
|
9
9
|
require "open3"
|
10
|
-
require "shellwords"
|
11
10
|
require "sorbet-runtime"
|
12
11
|
require "tmpdir"
|
13
12
|
|
@@ -17,9 +16,10 @@ require "dependabot/utils"
|
|
17
16
|
require "dependabot/errors"
|
18
17
|
require "dependabot/workspace"
|
19
18
|
require "dependabot"
|
19
|
+
require "dependabot/command_helpers"
|
20
20
|
|
21
21
|
module Dependabot
|
22
|
-
module SharedHelpers
|
22
|
+
module SharedHelpers # rubocop:disable Metrics/ModuleLength
|
23
23
|
extend T::Sig
|
24
24
|
|
25
25
|
GIT_CONFIG_GLOBAL_PATH = T.let(File.expand_path(".gitconfig", Utils::BUMP_TMP_DIR_PATH), String)
|
@@ -121,8 +121,7 @@ module Dependabot
|
|
121
121
|
# Escapes all special characters, e.g. = & | <>
|
122
122
|
sig { params(command: String).returns(String) }
|
123
123
|
def self.escape_command(command)
|
124
|
-
|
125
|
-
Shellwords.join(command_parts)
|
124
|
+
CommandHelpers.escape_command(command)
|
126
125
|
end
|
127
126
|
|
128
127
|
# rubocop:disable Metrics/MethodLength
|
@@ -135,14 +134,16 @@ module Dependabot
|
|
135
134
|
env: T.nilable(T::Hash[String, String]),
|
136
135
|
stderr_to_stdout: T::Boolean,
|
137
136
|
allow_unsafe_shell_command: T::Boolean,
|
138
|
-
error_class: T.class_of(HelperSubprocessFailed)
|
137
|
+
error_class: T.class_of(HelperSubprocessFailed),
|
138
|
+
timeout: Integer
|
139
139
|
)
|
140
140
|
.returns(T.nilable(T.any(String, T::Hash[String, T.untyped], T::Array[T::Hash[String, T.untyped]])))
|
141
141
|
end
|
142
142
|
def self.run_helper_subprocess(command:, function:, args:, env: nil,
|
143
143
|
stderr_to_stdout: false,
|
144
144
|
allow_unsafe_shell_command: false,
|
145
|
-
error_class: HelperSubprocessFailed
|
145
|
+
error_class: HelperSubprocessFailed,
|
146
|
+
timeout: CommandHelpers::TIMEOUTS::DEFAULT)
|
146
147
|
start = Time.now
|
147
148
|
stdin_data = JSON.dump(function: function, args: args)
|
148
149
|
cmd = allow_unsafe_shell_command ? command : escape_command(command)
|
@@ -157,7 +158,15 @@ module Dependabot
|
|
157
158
|
end
|
158
159
|
|
159
160
|
env_cmd = [env, cmd].compact
|
160
|
-
|
161
|
+
if Experiments.enabled?(:enable_shared_helpers_command_timeout)
|
162
|
+
stdout, stderr, process = CommandHelpers.capture3_with_timeout(
|
163
|
+
env_cmd,
|
164
|
+
stdin_data: stdin_data,
|
165
|
+
timeout: timeout
|
166
|
+
)
|
167
|
+
else
|
168
|
+
stdout, stderr, process = T.unsafe(Open3).capture3(*env_cmd, stdin_data: stdin_data)
|
169
|
+
end
|
161
170
|
time_taken = Time.now - start
|
162
171
|
|
163
172
|
if ENV["DEBUG_HELPERS"] == "true"
|
@@ -177,16 +186,16 @@ module Dependabot
|
|
177
186
|
function: function,
|
178
187
|
args: args,
|
179
188
|
time_taken: time_taken,
|
180
|
-
stderr_output: stderr
|
189
|
+
stderr_output: stderr[0..50_000], # Truncate to ~100kb
|
181
190
|
process_exit_value: process.to_s,
|
182
|
-
process_termsig: process
|
191
|
+
process_termsig: process&.termsig
|
183
192
|
}
|
184
193
|
|
185
194
|
check_out_of_memory_error(stderr, error_context, error_class)
|
186
195
|
|
187
196
|
begin
|
188
197
|
response = JSON.parse(stdout)
|
189
|
-
return response["result"] if process
|
198
|
+
return response["result"] if process&.success?
|
190
199
|
|
191
200
|
raise error_class.new(
|
192
201
|
message: response["error"],
|
@@ -415,6 +424,7 @@ module Dependabot
|
|
415
424
|
safe_directories
|
416
425
|
end
|
417
426
|
|
427
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
418
428
|
sig do
|
419
429
|
params(
|
420
430
|
command: String,
|
@@ -422,7 +432,8 @@ module Dependabot
|
|
422
432
|
cwd: T.nilable(String),
|
423
433
|
env: T.nilable(T::Hash[String, String]),
|
424
434
|
fingerprint: T.nilable(String),
|
425
|
-
stderr_to_stdout: T::Boolean
|
435
|
+
stderr_to_stdout: T::Boolean,
|
436
|
+
timeout: Integer
|
426
437
|
).returns(String)
|
427
438
|
end
|
428
439
|
def self.run_shell_command(command,
|
@@ -430,7 +441,8 @@ module Dependabot
|
|
430
441
|
cwd: nil,
|
431
442
|
env: {},
|
432
443
|
fingerprint: nil,
|
433
|
-
stderr_to_stdout: true
|
444
|
+
stderr_to_stdout: true,
|
445
|
+
timeout: CommandHelpers::TIMEOUTS::DEFAULT)
|
434
446
|
start = Time.now
|
435
447
|
cmd = allow_unsafe_shell_command ? command : escape_command(command)
|
436
448
|
|
@@ -439,7 +451,14 @@ module Dependabot
|
|
439
451
|
opts = {}
|
440
452
|
opts[:chdir] = cwd if cwd
|
441
453
|
|
442
|
-
|
454
|
+
env_cmd = [env || {}, cmd, opts].compact
|
455
|
+
if Experiments.enabled?(:enable_shared_helpers_command_timeout)
|
456
|
+
stdout, stderr, process = CommandHelpers.capture3_with_timeout(
|
457
|
+
env_cmd,
|
458
|
+
stderr_to_stdout: stderr_to_stdout,
|
459
|
+
timeout: timeout
|
460
|
+
)
|
461
|
+
elsif stderr_to_stdout
|
443
462
|
stdout, process = Open3.capture2e(env || {}, cmd, opts)
|
444
463
|
else
|
445
464
|
stdout, stderr, process = Open3.capture3(env || {}, cmd, opts)
|
@@ -449,7 +468,7 @@ module Dependabot
|
|
449
468
|
|
450
469
|
# Raise an error with the output from the shell session if the
|
451
470
|
# command returns a non-zero status
|
452
|
-
return stdout if process
|
471
|
+
return stdout || "" if process&.success?
|
453
472
|
|
454
473
|
error_context = {
|
455
474
|
command: cmd,
|
@@ -461,10 +480,11 @@ module Dependabot
|
|
461
480
|
check_out_of_disk_memory_error(stderr, error_context)
|
462
481
|
|
463
482
|
raise SharedHelpers::HelperSubprocessFailed.new(
|
464
|
-
message: stderr_to_stdout ? stdout : "#{stderr}\n#{stdout}",
|
483
|
+
message: stderr_to_stdout ? (stdout || "") : "#{stderr}\n#{stdout}",
|
465
484
|
error_context: error_context
|
466
485
|
)
|
467
486
|
end
|
487
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
468
488
|
|
469
489
|
sig { params(stderr: T.nilable(String), error_context: T::Hash[Symbol, String]).void }
|
470
490
|
def self.check_out_of_disk_memory_error(stderr, error_context)
|
data/lib/dependabot.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dependabot-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.292.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dependabot
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk-codecommit
|
@@ -531,6 +531,7 @@ files:
|
|
531
531
|
- lib/dependabot/clients/codecommit.rb
|
532
532
|
- lib/dependabot/clients/github_with_retries.rb
|
533
533
|
- lib/dependabot/clients/gitlab_with_retries.rb
|
534
|
+
- lib/dependabot/command_helpers.rb
|
534
535
|
- lib/dependabot/config.rb
|
535
536
|
- lib/dependabot/config/file.rb
|
536
537
|
- lib/dependabot/config/file_fetcher.rb
|
@@ -614,7 +615,7 @@ licenses:
|
|
614
615
|
- MIT
|
615
616
|
metadata:
|
616
617
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
617
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
618
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.292.0
|
618
619
|
post_install_message:
|
619
620
|
rdoc_options: []
|
620
621
|
require_paths:
|
@@ -630,7 +631,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
630
631
|
- !ruby/object:Gem::Version
|
631
632
|
version: 3.3.7
|
632
633
|
requirements: []
|
633
|
-
rubygems_version: 3.5.
|
634
|
+
rubygems_version: 3.5.22
|
634
635
|
signing_key:
|
635
636
|
specification_version: 4
|
636
637
|
summary: Shared code used across Dependabot Core
|