dependabot-go_modules 0.373.0 → 0.374.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/go_modules/file_fetcher.rb +106 -13
- data/lib/dependabot/go_modules/file_parser.rb +101 -8
- data/lib/dependabot/go_modules/file_updater/go_mod_updater.rb +113 -1
- data/lib/dependabot/go_modules/file_updater.rb +86 -20
- data/lib/dependabot/go_modules/go_work_parser.rb +40 -0
- data/lib/dependabot/go_modules/package/package_details_fetcher.rb +12 -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: 79d4612bc061dae5110e47d2213548ed4dc3b2d93fadb04494cb85f7b78fcef5
|
|
4
|
+
data.tar.gz: aacdfc5d7c13bf0a8e3c1fb9db5ab9e30dfb3fbcc0adcd1059cea0551eee5ade
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a7e6bbb25f8999253090edc7639c804df977ad05495b6f562579bc4ef06799b317fd166e7a896ca5bc1801652cdfaa580a4e3da3dcf901cfdb10dc641e7397d
|
|
7
|
+
data.tar.gz: f5bbfefc7577f2d65e035b2b976fe2824cc423b063dd62875f1af201185c32332a6daf4f6c603d5e5165b75aa664728aec712fabf8ce9be529a1b8002546a083
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
require "sorbet-runtime"
|
|
5
5
|
require "dependabot/file_fetchers"
|
|
6
6
|
require "dependabot/file_fetchers/base"
|
|
7
|
+
require "dependabot/go_modules/go_work_parser"
|
|
7
8
|
|
|
8
9
|
module Dependabot
|
|
9
10
|
module GoModules
|
|
@@ -13,41 +14,68 @@ module Dependabot
|
|
|
13
14
|
|
|
14
15
|
sig { override.params(filenames: T::Array[String]).returns(T::Boolean) }
|
|
15
16
|
def self.required_files_in?(filenames)
|
|
16
|
-
filenames.include?("go.mod")
|
|
17
|
+
filenames.include?("go.mod") || filenames.include?("go.work")
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
sig { override.returns(String) }
|
|
20
21
|
def self.required_files_message
|
|
21
|
-
"Repo must contain a go.mod."
|
|
22
|
+
"Repo must contain a go.mod or go.work."
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
sig { override.returns(T::Hash[Symbol, T.untyped]) }
|
|
25
26
|
def ecosystem_versions
|
|
27
|
+
version = go_version_from_file(go_mod) ||
|
|
28
|
+
go_version_from_file(go_work) ||
|
|
29
|
+
all_workspace_go_mods.filter_map { |f| go_version_from_file(f) }.first ||
|
|
30
|
+
"unknown"
|
|
31
|
+
|
|
26
32
|
{
|
|
27
33
|
package_managers: {
|
|
28
|
-
"gomod" =>
|
|
34
|
+
"gomod" => version
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
37
|
end
|
|
32
38
|
|
|
33
39
|
sig { override.returns(T::Array[DependencyFile]) }
|
|
34
40
|
def fetch_files
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
directory,
|
|
39
|
-
clone_repo_contents
|
|
40
|
-
) do
|
|
41
|
-
fetched_files = go_mod ? [go_mod] : []
|
|
42
|
-
# Fetch the (optional) go.sum
|
|
43
|
-
fetched_files << T.must(go_sum) if go_sum
|
|
44
|
-
fetched_files << T.must(go_env) if go_env
|
|
41
|
+
SharedHelpers.in_a_temporary_repo_directory(directory, clone_repo_contents) do
|
|
42
|
+
fetched_files = collect_dependency_files
|
|
43
|
+
validate_files!(fetched_files)
|
|
45
44
|
fetched_files
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
private
|
|
50
49
|
|
|
50
|
+
sig { returns(T::Array[DependencyFile]) }
|
|
51
|
+
def collect_dependency_files
|
|
52
|
+
fetched_files = T.let([], T::Array[DependencyFile])
|
|
53
|
+
|
|
54
|
+
if go_work
|
|
55
|
+
fetched_files << T.must(go_work)
|
|
56
|
+
fetched_files << T.must(go_work_sum) if go_work_sum
|
|
57
|
+
fetched_files.concat(workspace_module_files)
|
|
58
|
+
else
|
|
59
|
+
fetched_files << T.must(go_mod) if go_mod
|
|
60
|
+
fetched_files << T.must(go_sum) if go_sum
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
fetched_files << T.must(go_env) if go_env
|
|
64
|
+
|
|
65
|
+
fetched_files
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sig { params(files: T::Array[DependencyFile]).void }
|
|
69
|
+
def validate_files!(files)
|
|
70
|
+
return if files.any? { |f| f.name.end_with?("go.mod") }
|
|
71
|
+
|
|
72
|
+
error_msg = go_work ? "No go.mod files found in workspace" : "No go.mod files found"
|
|
73
|
+
raise Dependabot::DependencyFileNotFound.new(
|
|
74
|
+
"go.mod",
|
|
75
|
+
error_msg
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
51
79
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
52
80
|
def go_mod
|
|
53
81
|
@go_mod ||= T.let(fetch_file_if_present("go.mod"), T.nilable(Dependabot::DependencyFile))
|
|
@@ -58,6 +86,11 @@ module Dependabot
|
|
|
58
86
|
@go_sum ||= T.let(fetch_file_if_present("go.sum"), T.nilable(Dependabot::DependencyFile))
|
|
59
87
|
end
|
|
60
88
|
|
|
89
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
90
|
+
def go_work_sum
|
|
91
|
+
@go_work_sum ||= T.let(fetch_file_if_present("go.work.sum"), T.nilable(Dependabot::DependencyFile))
|
|
92
|
+
end
|
|
93
|
+
|
|
61
94
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
62
95
|
def go_env
|
|
63
96
|
return @go_env if defined?(@go_env)
|
|
@@ -65,6 +98,66 @@ module Dependabot
|
|
|
65
98
|
@go_env = T.let(fetch_support_file("go.env"), T.nilable(Dependabot::DependencyFile))
|
|
66
99
|
@go_env
|
|
67
100
|
end
|
|
101
|
+
|
|
102
|
+
sig { params(file: T.nilable(Dependabot::DependencyFile)).returns(T.nilable(String)) }
|
|
103
|
+
def go_version_from_file(file)
|
|
104
|
+
file&.content&.match(/^go\s+(\d+\.\d+)/)&.captures&.first
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
108
|
+
def all_workspace_go_mods
|
|
109
|
+
return [] unless go_work
|
|
110
|
+
|
|
111
|
+
workspace_module_paths.filter_map do |module_path|
|
|
112
|
+
name = module_path == "." ? "go.mod" : File.join(module_path, "go.mod")
|
|
113
|
+
fetch_file_if_present(name)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
118
|
+
def go_work
|
|
119
|
+
@go_work ||= T.let(fetch_file_if_present("go.work"), T.nilable(Dependabot::DependencyFile))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
sig { returns(T::Array[String]) }
|
|
123
|
+
def workspace_module_paths
|
|
124
|
+
return [] unless go_work
|
|
125
|
+
|
|
126
|
+
content = T.must(T.must(go_work).content)
|
|
127
|
+
GoWorkParser.use_paths(content)
|
|
128
|
+
.select { |p| valid_module_path?(p) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
sig { params(path: String).returns(T::Boolean) }
|
|
132
|
+
def valid_module_path?(path)
|
|
133
|
+
return false if path.empty?
|
|
134
|
+
return false if Pathname.new(path).absolute?
|
|
135
|
+
return false if path.include?("\0")
|
|
136
|
+
|
|
137
|
+
clean = Pathname.new(path).cleanpath.to_s
|
|
138
|
+
return false if clean.start_with?("../")
|
|
139
|
+
|
|
140
|
+
true
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
sig { returns(T::Array[DependencyFile]) }
|
|
144
|
+
def workspace_module_files
|
|
145
|
+
files = T.let([], T::Array[DependencyFile])
|
|
146
|
+
|
|
147
|
+
workspace_module_paths.each do |module_path|
|
|
148
|
+
mod_name = module_path == "." ? "go.mod" : File.join(module_path, "go.mod")
|
|
149
|
+
mod_file = fetch_file_if_present(mod_name)
|
|
150
|
+
next unless mod_file
|
|
151
|
+
|
|
152
|
+
files << mod_file
|
|
153
|
+
|
|
154
|
+
sum_name = module_path == "." ? "go.sum" : File.join(module_path, "go.sum")
|
|
155
|
+
sum_file = fetch_file_if_present(sum_name)
|
|
156
|
+
files << sum_file if sum_file
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
files
|
|
160
|
+
end
|
|
68
161
|
end
|
|
69
162
|
end
|
|
70
163
|
end
|
|
@@ -6,6 +6,7 @@ require "sorbet-runtime"
|
|
|
6
6
|
require "open3"
|
|
7
7
|
require "dependabot/dependency"
|
|
8
8
|
require "dependabot/file_parsers/base/dependency_set"
|
|
9
|
+
require "dependabot/go_modules/go_work_parser"
|
|
9
10
|
require "dependabot/go_modules/path_converter"
|
|
10
11
|
require "dependabot/go_modules/replace_stubber"
|
|
11
12
|
require "dependabot/errors"
|
|
@@ -52,11 +53,13 @@ module Dependabot
|
|
|
52
53
|
def parse
|
|
53
54
|
dependency_set = Dependabot::FileParsers::Base::DependencySet.new
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
if workspace?
|
|
57
|
+
parse_workspace_dependencies(dependency_set)
|
|
58
|
+
else
|
|
59
|
+
required_packages.each do |hsh|
|
|
60
|
+
next if skip_dependency?(hsh)
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
dependency_set << dep
|
|
62
|
+
dependency_set << dependency_from_details(hsh)
|
|
60
63
|
end
|
|
61
64
|
end
|
|
62
65
|
|
|
@@ -190,9 +193,98 @@ module Dependabot
|
|
|
190
193
|
@go_env ||= T.let(get_original_file("go.env"), T.nilable(Dependabot::DependencyFile))
|
|
191
194
|
end
|
|
192
195
|
|
|
196
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
197
|
+
def go_work
|
|
198
|
+
@go_work ||= T.let(get_original_file("go.work"), T.nilable(Dependabot::DependencyFile))
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
sig { returns(T::Boolean) }
|
|
202
|
+
def workspace?
|
|
203
|
+
!go_work.nil?
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
207
|
+
def all_go_mods
|
|
208
|
+
@all_go_mods ||= T.let(
|
|
209
|
+
if go_work
|
|
210
|
+
workspace_mod_names = GoWorkParser.use_paths(T.must(T.must(go_work).content)).map do |path|
|
|
211
|
+
path == "." ? "go.mod" : "#{path}/go.mod"
|
|
212
|
+
end
|
|
213
|
+
dependency_files.select { |f| workspace_mod_names.include?(f.name) }
|
|
214
|
+
else
|
|
215
|
+
dependency_files.select { |f| f.name.end_with?("go.mod") }
|
|
216
|
+
end,
|
|
217
|
+
T.nilable(T::Array[Dependabot::DependencyFile])
|
|
218
|
+
)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
sig { params(dependency_set: Dependabot::FileParsers::Base::DependencySet).void }
|
|
222
|
+
def parse_workspace_dependencies(dependency_set)
|
|
223
|
+
all_go_mods.each do |mod_file|
|
|
224
|
+
parse_single_module(mod_file).each do |dep|
|
|
225
|
+
dependency_set << dep
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
sig { params(mod_file: Dependabot::DependencyFile).returns(T::Array[Dependabot::Dependency]) }
|
|
231
|
+
def parse_single_module(mod_file)
|
|
232
|
+
SharedHelpers.in_a_temporary_directory do |path|
|
|
233
|
+
File.write("go.mod", mod_file.content)
|
|
234
|
+
|
|
235
|
+
command = "go mod edit -json"
|
|
236
|
+
stdout, stderr, status = Open3.capture3(command)
|
|
237
|
+
handle_parser_error(path, stderr, file_path: mod_file.path) unless status.success?
|
|
238
|
+
|
|
239
|
+
parsed = JSON.parse(stdout)
|
|
240
|
+
packages = parsed["Require"] || []
|
|
241
|
+
|
|
242
|
+
packages.filter_map do |hsh|
|
|
243
|
+
next if skip_dependency_in_manifest?(hsh, parsed)
|
|
244
|
+
|
|
245
|
+
source = { type: "default", source: hsh["Path"] }
|
|
246
|
+
version = hsh["Version"]&.sub(/^v?/, "")
|
|
247
|
+
|
|
248
|
+
reqs = [{
|
|
249
|
+
requirement: hsh["Version"],
|
|
250
|
+
file: mod_file.name,
|
|
251
|
+
source: source,
|
|
252
|
+
groups: []
|
|
253
|
+
}]
|
|
254
|
+
|
|
255
|
+
Dependency.new(
|
|
256
|
+
name: hsh["Path"],
|
|
257
|
+
version: version,
|
|
258
|
+
requirements: hsh["Indirect"] ? [] : reqs,
|
|
259
|
+
package_manager: "go_modules"
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
sig { params(dep: T::Hash[String, T.untyped], mod_manifest: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
266
|
+
def skip_dependency_in_manifest?(dep, mod_manifest)
|
|
267
|
+
return true if dependency_is_replaced_in?(dep, mod_manifest)
|
|
268
|
+
|
|
269
|
+
path_uri = URI.parse("https://#{dep['Path']}")
|
|
270
|
+
!path_uri.host&.include?(".")
|
|
271
|
+
rescue URI::InvalidURIError
|
|
272
|
+
false
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
sig { params(details: T::Hash[String, T.untyped], mod_manifest: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
276
|
+
def dependency_is_replaced_in?(details, mod_manifest)
|
|
277
|
+
return false unless mod_manifest["Replace"]
|
|
278
|
+
|
|
279
|
+
mod_manifest["Replace"].any? do |replace|
|
|
280
|
+
replace["Old"]["Path"] == details["Path"] &&
|
|
281
|
+
(!replace["Old"]["Version"] || replace["Old"]["Version"] == details["Version"])
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
193
285
|
sig { override.void }
|
|
194
286
|
def check_required_files
|
|
195
|
-
raise "No go.mod!" unless go_mod
|
|
287
|
+
raise "No go.mod or go.work!" unless go_mod || go_work
|
|
196
288
|
end
|
|
197
289
|
|
|
198
290
|
sig { params(details: T::Hash[String, T.untyped]).returns(Dependabot::Dependency) }
|
|
@@ -264,10 +356,11 @@ module Dependabot
|
|
|
264
356
|
end
|
|
265
357
|
end
|
|
266
358
|
|
|
267
|
-
sig { params(path: T.any(Pathname, String), stderr: String).returns(T.noreturn) }
|
|
268
|
-
def handle_parser_error(path, stderr)
|
|
359
|
+
sig { params(path: T.any(Pathname, String), stderr: String, file_path: T.nilable(String)).returns(T.noreturn) }
|
|
360
|
+
def handle_parser_error(path, stderr, file_path: nil)
|
|
269
361
|
msg = stderr.gsub(path.to_s, "").strip
|
|
270
|
-
|
|
362
|
+
resolved_path = file_path || go_mod&.path || go_work&.path || "go.mod"
|
|
363
|
+
raise Dependabot::DependencyFileNotParseable.new(resolved_path, msg)
|
|
271
364
|
end
|
|
272
365
|
|
|
273
366
|
sig { params(dep: T::Hash[String, T.untyped]).returns(T::Boolean) }
|
|
@@ -7,13 +7,14 @@ require "dependabot/shared_helpers"
|
|
|
7
7
|
require "dependabot/errors"
|
|
8
8
|
require "dependabot/logger"
|
|
9
9
|
require "dependabot/go_modules/file_updater"
|
|
10
|
+
require "dependabot/go_modules/go_work_parser"
|
|
10
11
|
require "dependabot/go_modules/replace_stubber"
|
|
11
12
|
require "dependabot/go_modules/resolvability_errors"
|
|
12
13
|
|
|
13
14
|
module Dependabot
|
|
14
15
|
module GoModules
|
|
15
16
|
class FileUpdater
|
|
16
|
-
class GoModUpdater
|
|
17
|
+
class GoModUpdater # rubocop:disable Metrics/ClassLength
|
|
17
18
|
extend T::Sig
|
|
18
19
|
|
|
19
20
|
RESOLVABILITY_ERROR_REGEXES = T.let(
|
|
@@ -151,6 +152,14 @@ module Dependabot
|
|
|
151
152
|
updated_files[:go_sum]
|
|
152
153
|
end
|
|
153
154
|
|
|
155
|
+
sig { returns(T::Hash[String, String]) }
|
|
156
|
+
def updated_workspace_module_files
|
|
157
|
+
@updated_workspace_module_files ||= T.let(
|
|
158
|
+
update_workspace_files,
|
|
159
|
+
T.nilable(T::Hash[String, String])
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
|
|
154
163
|
private
|
|
155
164
|
|
|
156
165
|
sig { returns(T::Array[Dependabot::Dependency]) }
|
|
@@ -220,6 +229,109 @@ module Dependabot
|
|
|
220
229
|
end
|
|
221
230
|
end
|
|
222
231
|
|
|
232
|
+
sig { returns(T::Hash[String, String]) }
|
|
233
|
+
def update_workspace_files
|
|
234
|
+
in_repo_path do
|
|
235
|
+
dependency_files.each do |file|
|
|
236
|
+
path = Pathname.new(file.name).expand_path
|
|
237
|
+
FileUtils.mkdir_p(path.dirname)
|
|
238
|
+
File.write(path, file.content)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Run `go get dep@version` in each module directory so every go.mod
|
|
242
|
+
# that requires the dependency gets the version bump, not just the first.
|
|
243
|
+
# Follow with a bare `go get` validation pass per module, matching the
|
|
244
|
+
# single-module update path's intent (see run_go_get comment).
|
|
245
|
+
workspace_module_paths.each do |mod_dir|
|
|
246
|
+
Dir.chdir(mod_dir) do
|
|
247
|
+
run_go_get(dependencies)
|
|
248
|
+
run_go_get
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
run_go_work_sync
|
|
253
|
+
run_workspace_tidy
|
|
254
|
+
|
|
255
|
+
collect_workspace_file_contents
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
sig { void }
|
|
260
|
+
def run_go_work_sync
|
|
261
|
+
command = "go work sync"
|
|
262
|
+
_, stderr, status = Open3.capture3(command)
|
|
263
|
+
return if status.success?
|
|
264
|
+
|
|
265
|
+
handle_subprocess_error(stderr)
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
sig { void }
|
|
269
|
+
def run_workspace_tidy
|
|
270
|
+
return unless tidy?
|
|
271
|
+
|
|
272
|
+
workspace_module_paths.each do |mod_path|
|
|
273
|
+
Dir.chdir(mod_path) do
|
|
274
|
+
command = "go mod tidy -e"
|
|
275
|
+
_, stderr, status = Open3.capture3(command)
|
|
276
|
+
if status.success?
|
|
277
|
+
Dependabot.logger.info "`go mod tidy` succeeded in #{mod_path}"
|
|
278
|
+
else
|
|
279
|
+
Dependabot.logger.info "Failed to `go mod tidy` in #{mod_path}: #{stderr}"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
sig { returns(T::Array[String]) }
|
|
286
|
+
def workspace_module_paths
|
|
287
|
+
go_work_file = dependency_files.find { |f| f.name.end_with?("go.work") }
|
|
288
|
+
return ["."] unless go_work_file
|
|
289
|
+
|
|
290
|
+
fetched_mod_names = dependency_files.select { |f| f.name.end_with?("go.mod") }
|
|
291
|
+
.to_set(&:name)
|
|
292
|
+
|
|
293
|
+
GoWorkParser.use_paths(T.must(go_work_file.content))
|
|
294
|
+
.select { |p| valid_workspace_path?(p) && fetched_mod_names.include?(workspace_mod_name(p)) }
|
|
295
|
+
.map { |p| p == "." ? "." : "./#{p}" }
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
sig { params(path: String).returns(T::Boolean) }
|
|
299
|
+
def valid_workspace_path?(path)
|
|
300
|
+
return false if Pathname.new(path).absolute?
|
|
301
|
+
|
|
302
|
+
!Pathname.new(path).cleanpath.to_s.start_with?("../")
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
sig { params(use_path: String).returns(String) }
|
|
306
|
+
def workspace_mod_name(use_path)
|
|
307
|
+
use_path == "." ? "go.mod" : "#{use_path}/go.mod"
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
sig { returns(T::Hash[String, String]) }
|
|
311
|
+
def collect_workspace_file_contents
|
|
312
|
+
results = T.let({}, T::Hash[String, String])
|
|
313
|
+
|
|
314
|
+
workspace_module_paths.each do |mod_path|
|
|
315
|
+
relative_base = mod_path.delete_prefix("./")
|
|
316
|
+
|
|
317
|
+
mod_file = File.join(mod_path, "go.mod")
|
|
318
|
+
if File.exist?(mod_file)
|
|
319
|
+
key = relative_base.empty? || relative_base == "." ? "go.mod" : "#{relative_base}/go.mod"
|
|
320
|
+
results[key] = File.read(mod_file)
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
sum_file = File.join(mod_path, "go.sum")
|
|
324
|
+
next unless File.exist?(sum_file)
|
|
325
|
+
|
|
326
|
+
key = relative_base.empty? || relative_base == "." ? "go.sum" : "#{relative_base}/go.sum"
|
|
327
|
+
results[key] = File.read(sum_file)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
results["go.work.sum"] = File.read("go.work.sum") if File.exist?("go.work.sum")
|
|
331
|
+
|
|
332
|
+
results
|
|
333
|
+
end
|
|
334
|
+
|
|
223
335
|
sig { void }
|
|
224
336
|
def run_go_mod_tidy
|
|
225
337
|
return unless tidy?
|
|
@@ -7,6 +7,7 @@ require "dependabot/shared_helpers"
|
|
|
7
7
|
require "dependabot/file_updaters"
|
|
8
8
|
require "dependabot/file_updaters/base"
|
|
9
9
|
require "dependabot/file_updaters/vendor_updater"
|
|
10
|
+
require "dependabot/go_modules/go_work_parser"
|
|
10
11
|
|
|
11
12
|
module Dependabot
|
|
12
13
|
module GoModules
|
|
@@ -37,7 +38,55 @@ module Dependabot
|
|
|
37
38
|
|
|
38
39
|
sig { override.returns(T::Array[Dependabot::DependencyFile]) }
|
|
39
40
|
def updated_dependency_files
|
|
40
|
-
updated_files =
|
|
41
|
+
updated_files = if workspace?
|
|
42
|
+
updated_workspace_files
|
|
43
|
+
else
|
|
44
|
+
updated_single_module_files
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
raise "No files changed!" if updated_files.none?
|
|
48
|
+
|
|
49
|
+
updated_files
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
sig { params(go_mod: Dependabot::DependencyFile).returns(T::Boolean) }
|
|
55
|
+
def dependency_changed?(go_mod)
|
|
56
|
+
# file_changed? only checks for changed requirements. Need to check for indirect dep version changes too.
|
|
57
|
+
file_changed?(go_mod) || dependencies.any? { |dep| dep.previous_version != dep.version }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
sig { override.void }
|
|
61
|
+
def check_required_files
|
|
62
|
+
return if go_mod || go_work
|
|
63
|
+
|
|
64
|
+
raise "No go.mod or go.work!"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
68
|
+
def go_mod
|
|
69
|
+
@go_mod ||= T.let(get_original_file("go.mod"), T.nilable(Dependabot::DependencyFile))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
73
|
+
def go_sum
|
|
74
|
+
@go_sum ||= T.let(get_original_file("go.sum"), T.nilable(Dependabot::DependencyFile))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
78
|
+
def go_work
|
|
79
|
+
@go_work ||= T.let(get_original_file("go.work"), T.nilable(Dependabot::DependencyFile))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
sig { returns(T::Boolean) }
|
|
83
|
+
def workspace?
|
|
84
|
+
!go_work.nil?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
88
|
+
def updated_single_module_files
|
|
89
|
+
updated_files = T.let([], T::Array[Dependabot::DependencyFile])
|
|
41
90
|
|
|
42
91
|
if go_mod && dependency_changed?(T.must(go_mod))
|
|
43
92
|
updated_files <<
|
|
@@ -60,34 +109,51 @@ module Dependabot
|
|
|
60
109
|
end
|
|
61
110
|
end
|
|
62
111
|
|
|
63
|
-
raise "No files changed!" if updated_files.none?
|
|
64
|
-
|
|
65
112
|
updated_files
|
|
66
113
|
end
|
|
67
114
|
|
|
68
|
-
|
|
115
|
+
sig { returns(T::Array[Dependabot::DependencyFile]) }
|
|
116
|
+
def updated_workspace_files
|
|
117
|
+
check_workspace_not_vendored!
|
|
69
118
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
# file_changed? only checks for changed requirements. Need to check for indirect dep version changes too.
|
|
73
|
-
file_changed?(go_mod) || dependencies.any? { |dep| dep.previous_version != dep.version }
|
|
74
|
-
end
|
|
119
|
+
updated_files = T.let([], T::Array[Dependabot::DependencyFile])
|
|
120
|
+
workspace_results = file_updater.updated_workspace_module_files
|
|
75
121
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
122
|
+
workspace_results.each do |file_path, content|
|
|
123
|
+
original = dependency_files.find { |f| f.name == file_path }
|
|
124
|
+
next unless original
|
|
125
|
+
next if original.content == content
|
|
79
126
|
|
|
80
|
-
|
|
81
|
-
|
|
127
|
+
updated_files << updated_file(file: original, content: content)
|
|
128
|
+
end
|
|
82
129
|
|
|
83
|
-
|
|
84
|
-
def go_mod
|
|
85
|
-
@go_mod ||= T.let(get_original_file("go.mod"), T.nilable(Dependabot::DependencyFile))
|
|
130
|
+
updated_files
|
|
86
131
|
end
|
|
87
132
|
|
|
88
|
-
sig {
|
|
89
|
-
def
|
|
90
|
-
|
|
133
|
+
sig { void }
|
|
134
|
+
def check_workspace_not_vendored!
|
|
135
|
+
mod_paths = GoWorkParser.use_paths(T.must(T.must(go_work).content))
|
|
136
|
+
|
|
137
|
+
vendored_path = mod_paths.find do |mod_path|
|
|
138
|
+
vendor_modules_txt = if mod_path == "."
|
|
139
|
+
File.join(vendor_dir, "modules.txt")
|
|
140
|
+
else
|
|
141
|
+
File.join(
|
|
142
|
+
T.must(repo_contents_path),
|
|
143
|
+
T.must(directory),
|
|
144
|
+
mod_path,
|
|
145
|
+
"vendor",
|
|
146
|
+
"modules.txt"
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
File.exist?(vendor_modules_txt)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
return unless vendored_path
|
|
153
|
+
|
|
154
|
+
raise Dependabot::DependencyFileNotResolvable,
|
|
155
|
+
"Go workspace module \"#{vendored_path}\" has a vendor directory. " \
|
|
156
|
+
"Vendored workspaces are not yet supported."
|
|
91
157
|
end
|
|
92
158
|
|
|
93
159
|
sig { returns(T.nilable(String)) }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# typed: strong
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "sorbet-runtime"
|
|
5
|
+
|
|
6
|
+
module Dependabot
|
|
7
|
+
module GoModules
|
|
8
|
+
class GoWorkParser
|
|
9
|
+
extend T::Sig
|
|
10
|
+
|
|
11
|
+
# Parses the `use` directives from go.work content.
|
|
12
|
+
# Returns an array of module paths with `./` prefix stripped.
|
|
13
|
+
# `"."` is included when the root module is listed.
|
|
14
|
+
# Result is deduped but not sorted, filtered, or re-prefixed.
|
|
15
|
+
sig { params(content: String).returns(T::Array[String]) }
|
|
16
|
+
def self.use_paths(content)
|
|
17
|
+
paths = T.let([], T::Array[String])
|
|
18
|
+
|
|
19
|
+
# Multi-line use block: use (\n ./path\n ...\n)
|
|
20
|
+
content.scan(/^use\s+\(([^)]+)\)/m).each do |block_match|
|
|
21
|
+
T.must(block_match[0]).each_line do |line|
|
|
22
|
+
path = line.split("//").first&.strip || ""
|
|
23
|
+
next if path.empty?
|
|
24
|
+
|
|
25
|
+
paths << path.sub(%r{^\./}, "")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Single-line use directive: use . or use ./path
|
|
30
|
+
# [^\s]* (zero-or-more) so bare `use .` is captured
|
|
31
|
+
content.scan(/^use\s+(?!\()(\.[^\s]*)/m).each do |match|
|
|
32
|
+
path = T.must(match[0]).sub(%r{^\./}, "")
|
|
33
|
+
paths << path
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
paths.uniq
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -120,7 +120,18 @@ module Dependabot
|
|
|
120
120
|
|
|
121
121
|
sig { returns(T.nilable(Dependabot::DependencyFile)) }
|
|
122
122
|
def go_mod
|
|
123
|
-
@go_mod ||= T.let(
|
|
123
|
+
@go_mod ||= T.let(
|
|
124
|
+
begin
|
|
125
|
+
req_file = dependency.requirements.first&.fetch(:file, nil)
|
|
126
|
+
if req_file
|
|
127
|
+
dependency_files.find { |f| f.name == req_file }
|
|
128
|
+
else
|
|
129
|
+
dependency_files.find { |f| f.name == "go.mod" } ||
|
|
130
|
+
dependency_files.find { |f| f.name.end_with?("/go.mod") }
|
|
131
|
+
end
|
|
132
|
+
end,
|
|
133
|
+
T.nilable(Dependabot::DependencyFile)
|
|
134
|
+
)
|
|
124
135
|
end
|
|
125
136
|
|
|
126
137
|
sig do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dependabot-go_modules
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.374.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dependabot
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - '='
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: 0.
|
|
18
|
+
version: 0.374.0
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - '='
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: 0.
|
|
25
|
+
version: 0.374.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: debug
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -256,6 +256,7 @@ files:
|
|
|
256
256
|
- lib/dependabot/go_modules/file_parser.rb
|
|
257
257
|
- lib/dependabot/go_modules/file_updater.rb
|
|
258
258
|
- lib/dependabot/go_modules/file_updater/go_mod_updater.rb
|
|
259
|
+
- lib/dependabot/go_modules/go_work_parser.rb
|
|
259
260
|
- lib/dependabot/go_modules/language.rb
|
|
260
261
|
- lib/dependabot/go_modules/metadata_finder.rb
|
|
261
262
|
- lib/dependabot/go_modules/native_helpers.rb
|
|
@@ -274,7 +275,7 @@ licenses:
|
|
|
274
275
|
- MIT
|
|
275
276
|
metadata:
|
|
276
277
|
bug_tracker_uri: https://github.com/dependabot/dependabot-core/issues
|
|
277
|
-
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.
|
|
278
|
+
changelog_uri: https://github.com/dependabot/dependabot-core/releases/tag/v0.374.0
|
|
278
279
|
rdoc_options: []
|
|
279
280
|
require_paths:
|
|
280
281
|
- lib
|