dependabot-common 0.290.0 → 0.292.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/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
|