dependabot-go_modules 0.120.5 → 0.121.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fce526d6f1486979f1480199b40e32a9c98f4e50851e4d5821efb8541c31cec2
4
- data.tar.gz: 7cea6d48092354e728a1fef289916415d557dbdb068611a3fa406f7c246a8e03
3
+ metadata.gz: 701e98b35df513747baf443e0438108b3b9654e27f34f8c2222a6a66ca2ed629
4
+ data.tar.gz: 9a072a70eff30b0958a11ea3c18a52a9d226f0638802cac4c1a11ce43c11179e
5
5
  SHA512:
6
- metadata.gz: c075d6cff1b7ef09ccab054cce4f6a8e66d732b4301aaebf8a7b75a6a4e1bc2b9300a7b81a41afb383faad942a56f791d819dd5d0a2cd3c60460661d24feaf1f
7
- data.tar.gz: d9b4889b2dfc29755c10e8545e8e4e0022420e7dcf411bcc4fddf73c79af9bf3aacf7b88324baddd14bee80ab5132103b4db958cf8c8e278acd8641adf96d473
6
+ metadata.gz: f4c5afb8286faed93d541b40f466baa9ccf3204b3ba7ef600658b202dc30ec3085f8ac22a81169372a92d3edbbe4232ba2018c69824394d81db57cae44108f56
7
+ data.tar.gz: 3637c5c1b624bfba9e3b4a4af263e2aff7411c5ff181ae9c35cf1edc34b11fe62206dce9b3917dcc3869f78f76030158f43747fe0205becb969d3593a7ce1216
@@ -17,3 +17,6 @@ Dependabot::PullRequestCreator::Labeler.
17
17
  require "dependabot/dependency"
18
18
  Dependabot::Dependency.
19
19
  register_production_check("go_modules", ->(_) { true })
20
+
21
+ require "dependabot/utils"
22
+ Dependabot::Utils.register_always_clone("go_modules")
@@ -17,23 +17,31 @@ module Dependabot
17
17
  private
18
18
 
19
19
  def fetch_files
20
- unless go_mod
21
- raise(
22
- Dependabot::DependencyFileNotFound,
23
- File.join(directory, "go.mod")
24
- )
20
+ # Ensure we always check out the full repo contents for go_module
21
+ # updates.
22
+ SharedHelpers.in_a_temporary_repo_directory(
23
+ directory,
24
+ clone_repo_contents
25
+ ) do
26
+ unless go_mod
27
+ raise(
28
+ Dependabot::DependencyFileNotFound,
29
+ Pathname.new(File.join(directory, "go.mod")).
30
+ cleanpath.to_path
31
+ )
32
+ end
33
+
34
+ fetched_files = [go_mod]
35
+
36
+ # Fetch the (optional) go.sum
37
+ fetched_files << go_sum if go_sum
38
+
39
+ # Fetch the main.go file if present, as this will later identify
40
+ # this repo as an app.
41
+ fetched_files << main if main
42
+
43
+ fetched_files
25
44
  end
26
-
27
- fetched_files = [go_mod]
28
-
29
- # Fetch the (optional) go.sum
30
- fetched_files << go_sum if go_sum
31
-
32
- # Fetch the main.go file if present, as this will later identify
33
- # this repo as an app.
34
- fetched_files << main if main
35
-
36
- fetched_files
37
45
  end
38
46
 
39
47
  def go_mod
@@ -45,15 +53,21 @@ module Dependabot
45
53
  end
46
54
 
47
55
  def main
48
- return @main if @main
56
+ return @main if defined?(@main)
49
57
 
50
- go_files = repo_contents.select { |f| f.name.end_with?(".go") }
58
+ go_files = Dir.glob("*.go")
51
59
 
52
- go_files.each do |go_file|
53
- file = fetch_file_from_host(go_file.name, type: "package_main")
54
- next unless file.content.match?(/\s*package\s+main/)
60
+ go_files.each do |filename|
61
+ file_content = File.read(filename)
62
+ next unless file_content.match?(/\s*package\s+main/)
55
63
 
56
- return @main = file.tap { |f| f.support_file = true }
64
+ return @main = DependencyFile.new(
65
+ name: Pathname.new(filename).cleanpath.to_path,
66
+ directory: "/",
67
+ type: "package_main",
68
+ support_file: true,
69
+ content: file_content
70
+ )
57
71
  end
58
72
 
59
73
  nil
@@ -9,6 +9,25 @@ module Dependabot
9
9
  class FileUpdater < Dependabot::FileUpdaters::Base
10
10
  require_relative "file_updater/go_mod_updater"
11
11
 
12
+ def initialize(dependencies:, dependency_files:, repo_contents_path: nil,
13
+ credentials:, options: {})
14
+ super
15
+ return unless repo_contents_path.nil?
16
+
17
+ # masquerade repo_contents_path for GoModUpdater during transition
18
+ tmp = Dir.mktmpdir
19
+ Dir.chdir(tmp) do
20
+ dependency_files.each do |file|
21
+ File.write(file.name, file.content)
22
+ end
23
+ `git init .`
24
+ `git add .`
25
+ `git commit -m'fake repo_contents_path'`
26
+ end
27
+ @repo_contents_path = tmp
28
+ @repo_contents_stub = true
29
+ end
30
+
12
31
  def self.updated_files_regex
13
32
  [
14
33
  /^go\.mod$/,
@@ -56,13 +75,18 @@ module Dependabot
56
75
  @go_sum ||= get_original_file("go.sum")
57
76
  end
58
77
 
78
+ def directory
79
+ dependency_files.first.directory
80
+ end
81
+
59
82
  def file_updater
60
83
  @file_updater ||=
61
84
  GoModUpdater.new(
62
85
  dependencies: dependencies,
63
- go_mod: go_mod,
64
- go_sum: go_sum,
65
- credentials: credentials
86
+ credentials: credentials,
87
+ repo_contents_path: repo_contents_path,
88
+ directory: directory,
89
+ tidy: !@repo_contents_stub && options.fetch(:go_mod_tidy, false)
66
90
  )
67
91
  end
68
92
  end
@@ -25,11 +25,13 @@ module Dependabot
25
25
  /go: ([^@\s]+)(?:@[^\s]+)?: .* declares its path as: ([\S]*)/m
26
26
  ].freeze
27
27
 
28
- def initialize(dependencies:, go_mod:, go_sum:, credentials:)
28
+ def initialize(dependencies:, credentials:, repo_contents_path:,
29
+ directory:, tidy:)
29
30
  @dependencies = dependencies
30
- @go_mod = go_mod
31
- @go_sum = go_sum
32
31
  @credentials = credentials
32
+ @repo_contents_path = repo_contents_path
33
+ @directory = directory
34
+ @tidy = tidy
33
35
  end
34
36
 
35
37
  def updated_go_mod_content
@@ -42,64 +44,74 @@ module Dependabot
42
44
 
43
45
  private
44
46
 
45
- attr_reader :dependencies, :go_mod, :go_sum, :credentials
47
+ attr_reader :dependencies, :credentials, :repo_contents_path,
48
+ :directory
46
49
 
47
50
  def updated_files
48
51
  @updated_files ||= update_files
49
52
  end
50
53
 
51
- # rubocop:disable Metrics/AbcSize
52
54
  def update_files
53
- # Map paths in local replace directives to path hashes
54
- substitutions = replace_directive_substitutions(go_mod.content)
55
- stub_dirs = substitutions.values
55
+ in_repo_path do
56
+ # Map paths in local replace directives to path hashes
56
57
 
57
- # Replace full paths with path hashes in the go.mod
58
- clean_go_mod = substitute_all(go_mod.content, substitutions)
58
+ original_go_mod = File.read("go.mod")
59
+ original_manifest = parse_manifest
60
+ original_go_sum = File.read("go.sum") if File.exist?("go.sum")
59
61
 
60
- # Set the new dependency versions in the go.mod
61
- updated_go_mod = in_temp_dir(stub_dirs) do
62
- update_go_mod(clean_go_mod, dependencies)
63
- end
62
+ substitutions = replace_directive_substitutions(original_manifest)
63
+ build_module_stubs(substitutions.values)
64
64
 
65
- # Then run `go get` to pick up other changes to the file caused by
66
- # the upgrade
67
- regenerated_files = in_temp_dir(stub_dirs) do
68
- run_go_get(updated_go_mod, go_sum)
69
- end
65
+ # Replace full paths with path hashes in the go.mod
66
+ substitute_all(substitutions)
70
67
 
71
- # At this point, the go.mod returned from run_go_get contains the
72
- # correct set of modules, but running `go get` can change the file in
73
- # undesirable ways (such as injecting the current Go version), so we
74
- # need to update the original go.mod with the updated set of
75
- # requirements rather than using the regenerated file directly
76
- original_reqs = in_temp_dir(stub_dirs) do
77
- parse_manifest_requirements(go_mod.content)
78
- end
79
- updated_reqs = in_temp_dir(stub_dirs) do
80
- parse_manifest_requirements(regenerated_files[:go_mod])
81
- end
68
+ # Set the stubbed replace directives
69
+ update_go_mod(dependencies)
82
70
 
83
- original_paths = original_reqs.map { |r| r["Path"] }
84
- updated_paths = updated_reqs.map { |r| r["Path"] }
85
- req_paths_to_remove = original_paths - updated_paths
71
+ # Then run `go get` to pick up other changes to the file caused by
72
+ # the upgrade
73
+ run_go_get
74
+ run_go_mod_tidy
86
75
 
87
- output_go_mod = in_temp_dir(stub_dirs) do
88
- remove_requirements(go_mod.content, req_paths_to_remove)
89
- end
76
+ # At this point, the go.mod returned from run_go_get contains the
77
+ # correct set of modules, but running `go get` can change the file
78
+ # in undesirable ways (such as injecting the current Go version),
79
+ # so we need to update the original go.mod with the updated set of
80
+ # requirements rather than using the regenerated file directly
81
+ original_reqs = original_manifest["Require"] || []
82
+ updated_reqs = parse_manifest["Require"] || []
90
83
 
91
- output_go_mod = in_temp_dir(stub_dirs) do
84
+ original_paths = original_reqs.map { |r| r["Path"] }
85
+ updated_paths = updated_reqs.map { |r| r["Path"] }
86
+ req_paths_to_remove = original_paths - updated_paths
87
+
88
+ # Put back the original content before we replace just the updated
89
+ # dependencies.
90
+ write_go_mod(original_go_mod)
91
+
92
+ remove_requirements(req_paths_to_remove)
92
93
  deps = updated_reqs.map { |r| requirement_to_dependency_obj(r) }
93
- update_go_mod(output_go_mod, deps)
94
- end
94
+ update_go_mod(deps)
95
+
96
+ # put the old replace directives back again
97
+ substitute_all(substitutions.invert)
95
98
 
96
- { go_mod: output_go_mod, go_sum: regenerated_files[:go_sum] }
99
+ updated_go_sum = original_go_sum ? File.read("go.sum") : nil
100
+ updated_go_mod = File.read("go.mod")
101
+
102
+ { go_mod: updated_go_mod, go_sum: updated_go_sum }
103
+ end
97
104
  end
98
- # rubocop:enable Metrics/AbcSize
99
105
 
100
- def update_go_mod(go_mod_content, dependencies)
101
- File.write("go.mod", go_mod_content)
106
+ def run_go_mod_tidy
107
+ return unless tidy?
108
+
109
+ command = "go mod tidy"
110
+ _, stderr, status = Open3.capture3(ENVIRONMENT, command)
111
+ handle_subprocess_error(stderr) unless status.success?
112
+ end
102
113
 
114
+ def update_go_mod(dependencies)
103
115
  deps = dependencies.map do |dep|
104
116
  {
105
117
  name: dep.name,
@@ -108,78 +120,75 @@ module Dependabot
108
120
  }
109
121
  end
110
122
 
111
- SharedHelpers.run_helper_subprocess(
123
+ body = SharedHelpers.run_helper_subprocess(
112
124
  command: NativeHelpers.helper_path,
113
125
  env: ENVIRONMENT,
114
126
  function: "updateDependencyFile",
115
127
  args: { dependencies: deps }
116
128
  )
129
+
130
+ write_go_mod(body)
117
131
  end
118
132
 
119
- def run_go_get(go_mod_content, go_sum)
120
- File.write("go.mod", go_mod_content)
121
- File.write("go.sum", go_sum.content) if go_sum
122
- File.write("main.go", dummy_main_go)
133
+ def run_go_get
134
+ tmp_go_file = "#{SecureRandom.hex}.go"
135
+
136
+ unless Dir.glob("*.go").any?
137
+ File.write(tmp_go_file, "package dummypkg\n")
138
+ end
123
139
 
124
140
  _, stderr, status = Open3.capture3(ENVIRONMENT, "go get -d")
125
141
  handle_subprocess_error(stderr) unless status.success?
126
-
127
- updated_go_sum = go_sum ? File.read("go.sum") : nil
128
- { go_mod: File.read("go.mod"), go_sum: updated_go_sum }
142
+ ensure
143
+ File.delete(tmp_go_file) if File.exist?(tmp_go_file)
129
144
  end
130
145
 
131
- def parse_manifest_requirements(go_mod_content)
132
- File.write("go.mod", go_mod_content)
133
-
146
+ def parse_manifest
134
147
  command = "go mod edit -json"
135
148
  stdout, stderr, status = Open3.capture3(ENVIRONMENT, command)
136
149
  handle_subprocess_error(stderr) unless status.success?
137
150
 
138
- JSON.parse(stdout)["Require"] || []
151
+ JSON.parse(stdout) || {}
139
152
  end
140
153
 
141
- def remove_requirements(go_mod_content, requirement_paths)
142
- File.write("go.mod", go_mod_content)
143
-
154
+ def remove_requirements(requirement_paths)
144
155
  requirement_paths.each do |path|
145
156
  escaped_path = Shellwords.escape(path)
146
157
  command = "go mod edit -droprequire #{escaped_path}"
147
158
  _, stderr, status = Open3.capture3(ENVIRONMENT, command)
148
159
  handle_subprocess_error(stderr) unless status.success?
149
160
  end
150
-
151
- File.read("go.mod")
152
161
  end
153
162
 
154
- def add_requirements(go_mod_content, requirements)
155
- File.write("go.mod", go_mod_content)
156
-
163
+ def add_requirements(requirements)
157
164
  requirements.each do |r|
158
165
  escaped_req = Shellwords.escape("#{r['Path']}@#{r['Version']}")
159
166
  command = "go mod edit -require #{escaped_req}"
160
167
  _, stderr, status = Open3.capture3(ENVIRONMENT, command)
161
168
  handle_subprocess_error(stderr) unless status.success?
162
169
  end
163
-
164
- File.read("go.mod")
165
170
  end
166
171
 
167
- def in_temp_dir(stub_paths, &block)
168
- SharedHelpers.in_a_temporary_directory do
172
+ def in_repo_path(&block)
173
+ SharedHelpers.
174
+ in_a_temporary_repo_directory(directory, repo_contents_path) do
169
175
  SharedHelpers.with_git_configured(credentials: credentials) do
170
- # Create a fake empty module for each local module so that
171
- # `go get -d` works, even if some modules have been `replace`d
172
- # with a local module that we don't have access to.
173
- stub_paths.each do |stub_path|
174
- Dir.mkdir(stub_path) unless Dir.exist?(stub_path)
175
- FileUtils.touch(File.join(stub_path, "go.mod"))
176
- end
177
-
178
176
  block.call
179
177
  end
180
178
  end
181
179
  end
182
180
 
181
+ def build_module_stubs(stub_paths)
182
+ # Create a fake empty module for each local module so that
183
+ # `go get -d` works, even if some modules have been `replace`d
184
+ # with a local module that we don't have access to.
185
+ stub_paths.each do |stub_path|
186
+ Dir.mkdir(stub_path) unless Dir.exist?(stub_path)
187
+ FileUtils.touch(File.join(stub_path, "go.mod"))
188
+ FileUtils.touch(File.join(stub_path, "main.go"))
189
+ end
190
+ end
191
+
183
192
  # Given a go.mod file, find all `replace` directives pointing to a path
184
193
  # on the local filesystem, and return an array of pairs mapping the
185
194
  # original path to a hash of the path.
@@ -188,22 +197,14 @@ module Dependabot
188
197
  # the layout of the filesystem with a structure we can reproduce (i.e.
189
198
  # no paths such as ../../../foo), run the Go tooling, then reverse the
190
199
  # process afterwards.
191
- def replace_directive_substitutions(go_mod_content)
200
+ def replace_directive_substitutions(manifest)
192
201
  @replace_directive_substitutions ||=
193
- SharedHelpers.in_a_temporary_directory do |path|
194
- File.write("go.mod", go_mod_content)
195
-
196
- # Parse the go.mod to get a JSON representation of the replace
197
- # directives
198
- command = "go mod edit -json"
199
- stdout, stderr, status = Open3.capture3(ENVIRONMENT, command)
200
- handle_subprocess_error(path, stderr) unless status.success?
201
-
202
+ begin
202
203
  # Find all the local replacements, and return them with a stub
203
204
  # path we can use in their place. Using generated paths is safer
204
205
  # as it means we don't need to worry about references to parent
205
206
  # directories, etc.
206
- (JSON.parse(stdout)["Replace"] || []).
207
+ (manifest["Replace"] || []).
207
208
  map { |r| r["New"]["Path"] }.
208
209
  compact.
209
210
  select { |p| p.start_with?(".") || p.start_with?("/") }.
@@ -212,10 +213,12 @@ module Dependabot
212
213
  end
213
214
  end
214
215
 
215
- def substitute_all(file, substitutions)
216
- substitutions.reduce(file) do |text, (a, b)|
216
+ def substitute_all(substitutions)
217
+ body = substitutions.reduce(File.read("go.mod")) do |text, (a, b)|
217
218
  text.sub(a, b)
218
219
  end
220
+
221
+ write_go_mod(body)
219
222
  end
220
223
 
221
224
  def handle_subprocess_error(stderr)
@@ -231,28 +234,18 @@ module Dependabot
231
234
  if path_regex
232
235
  match = path_regex.match(stderr)
233
236
  raise Dependabot::GoModulePathMismatch.
234
- new(go_mod.path, match[1], match[2])
237
+ new(go_mod_path, match[1], match[2])
235
238
  end
236
239
 
237
240
  msg = stderr.lines.last(10).join.strip
238
- raise Dependabot::DependencyFileNotParseable.new(go_mod.path, msg)
241
+ raise Dependabot::DependencyFileNotParseable.
242
+ new(go_mod_path, msg)
239
243
  end
240
244
 
241
- def dummy_main_go
242
- # If we use `main` as the package name, running `go get -d` seems to
243
- # invoke the build systems, which can cause problems. For instance,
244
- # if the go.mod includes a module that doesn't have a top-level
245
- # package, we have no way of working out the import path, so the
246
- # build step fails.
247
- #
248
- # In due course, if we end up fetching the full repo, it might be
249
- # good to switch back to `main` so we can surface more errors.
250
- lines = ["package dummypkg", "import ("]
251
- dependencies.each do |dep|
252
- lines << "_ \"#{dep.name}\"" unless dep.requirements.empty?
253
- end
254
- lines << ")"
255
- lines.join("\n")
245
+ def go_mod_path
246
+ return "go.mod" if directory == "/"
247
+
248
+ File.join(directory, "go.mod")
256
249
  end
257
250
 
258
251
  def requirement_to_dependency_obj(req)
@@ -272,6 +265,14 @@ module Dependabot
272
265
  package_manager: "go_modules"
273
266
  )
274
267
  end
268
+
269
+ def write_go_mod(body)
270
+ File.write("go.mod", body)
271
+ end
272
+
273
+ def tidy?
274
+ !!@tidy
275
+ end
275
276
  end
276
277
  end
277
278
  end
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.120.5
4
+ version: 0.121.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.120.5
19
+ version: 0.121.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.120.5
26
+ version: 0.121.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement