dependabot-go_modules 0.120.2 → 0.121.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60ff8a8d9facd8061fd4fe740c5509a2bc77072aa46a808d757567bbab6f4786
4
- data.tar.gz: f20caca983548b129ee9568b7329bf168095811a856d122b3c4ecce0b361e74f
3
+ metadata.gz: d399469f8a61821f96b70b4665ef9037c9c103194ae3aa7224fa7bbf45e19cc6
4
+ data.tar.gz: 041406a7f88713b93f2c91b3fcf903934037975c82a8a7facebc9849baba4488
5
5
  SHA512:
6
- metadata.gz: d46fd067694962cb3916badc5c042cccf05eff815dd13ef9dfe6d2410a4b3e2184ed69c3bfbbc0ae0e16d66907a52fa081628822f0e943f88c05c097268ebc66
7
- data.tar.gz: 7ac16e2ee4b22806bd08ead2ea38d837d4893bc54221e6e8a87748f68e44be30336beba80243c800686a6db21786d44b122ba944865798e4f5ef679b8b032ce7
6
+ metadata.gz: df43946eea402a831d37a56637c69afe605b0c7ea3ad8e387d5e74175b13f629861b71073f7e0548b5630025089bdbc3fd8f7f8d6d00f6a9a08f9b20d96f84b9
7
+ data.tar.gz: beb0a877aa9c40b20d499da92c26aa03ebfdb9737e61ca1d296bbbb02ead76ec5ab2f953b3430cb7051de9a8bc58405ee6e0ddb105fe1b43f30136850e231594
@@ -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,27 @@ 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 config --global user.email "no-reply@github.com"`
24
+ `git config --global user.name "Dependabot"`
25
+ `git init .`
26
+ `git add .`
27
+ `git commit -m'fake repo_contents_path'`
28
+ end
29
+ @repo_contents_path = tmp
30
+ @repo_contents_stub = true
31
+ end
32
+
12
33
  def self.updated_files_regex
13
34
  [
14
35
  /^go\.mod$/,
@@ -56,13 +77,18 @@ module Dependabot
56
77
  @go_sum ||= get_original_file("go.sum")
57
78
  end
58
79
 
80
+ def directory
81
+ dependency_files.first.directory
82
+ end
83
+
59
84
  def file_updater
60
85
  @file_updater ||=
61
86
  GoModUpdater.new(
62
87
  dependencies: dependencies,
63
- go_mod: go_mod,
64
- go_sum: go_sum,
65
- credentials: credentials
88
+ credentials: credentials,
89
+ repo_contents_path: repo_contents_path,
90
+ directory: directory,
91
+ tidy: !@repo_contents_stub && options.fetch(:go_mod_tidy, false)
66
92
  )
67
93
  end
68
94
  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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dependabot-go_modules
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.120.2
4
+ version: 0.121.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dependabot
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-25 00:00:00.000000000 Z
11
+ date: 2020-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dependabot-common
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.120.2
19
+ version: 0.121.1
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.2
26
+ version: 0.121.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: byebug
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.91.0
103
+ version: 0.92.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.91.0
110
+ version: 0.92.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: vcr
113
113
  requirement: !ruby/object:Gem::Requirement