Skip to content

Commit

Permalink
Merge pull request #1193 from dependabot/go-handle-local-replacements
Browse files Browse the repository at this point in the history
Go (modules): handle local module replacements
  • Loading branch information
hmarr committed Jun 7, 2019
2 parents 985205d + 3f7a96a commit a785958
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 3 deletions.
44 changes: 41 additions & 3 deletions go_modules/lib/dependabot/go_modules/file_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def parse
dependency_set = Dependabot::FileParsers::Base::DependencySet.new

i = 0
chunks = module_info(go_mod).lines.
chunks = module_info.lines.
group_by { |line| line == "{\n" ? i += 1 : i }
deps = chunks.values.map { |chunk| JSON.parse(chunk.join) }

Expand Down Expand Up @@ -65,11 +65,19 @@ def dependency_from_details(details)
)
end

def module_info(go_mod)
def module_info
@module_info ||=
SharedHelpers.in_a_temporary_directory do |path|
SharedHelpers.with_git_configured(credentials: credentials) do
File.write("go.mod", go_mod.content)
# Create a fake empty module for each local module so that
# `go list` works, even if some modules have been `replace`d with
# a local module that we don't have access to.
local_replacements.each do |_, stub_path|
Dir.mkdir(stub_path) unless Dir.exist?(stub_path)
FileUtils.touch(File.join(stub_path, "go.mod"))
end

File.write("go.mod", go_mod_content)

command = "go mod edit -print > /dev/null"
command += " && go list -m -json all"
Expand All @@ -89,6 +97,36 @@ def module_info(go_mod)
end
end

def local_replacements
@local_replacements ||=
SharedHelpers.in_a_temporary_directory do |path|
File.write("go.mod", go_mod.content)

# Parse the go.mod to get a JSON representation of the replace
# directives
command = "go mod edit -json"
env = { "GO111MODULE" => "on" }
stdout, stderr, status = Open3.capture3(env, command)
handle_parser_error(path, stderr) unless status.success?

# Find all the local replacements, and return them with a stub path
# we can use in their place. Using generated paths is safer as it
# means we don't need to worry about references to parent
# directories, etc.
(JSON.parse(stdout)["Replace"] || []).
map { |r| r["New"]["Path"] }.
compact.
select { |p| p.start_with?(".") || p.start_with?("/") }.
map { |p| [p, "./" + Digest::SHA2.hexdigest(p)] }
end
end

def go_mod_content
local_replacements.reduce(go_mod.content) do |body, (path, stub_path)|
body.sub(path, stub_path)
end
end

GIT_ERROR_REGEX = /go: .*: git fetch .*: exit status 128/.freeze

# rubocop:disable Metrics/AbcSize
Expand Down
16 changes: 16 additions & 0 deletions go_modules/spec/dependabot/go_modules/file_parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,5 +204,21 @@
end
end
end

describe "a dependency replaced by a local override" do
let(:go_mod_content) do
go_mod = fixture("go_mods", go_mod_fixture_name)
go_mod.sub("=> github.com/rsc/qr v0.2.0", "=> ./foo/bar/baz")
end

subject(:dependency) do
dependencies.find { |d| d.name == "rsc.io/qr" }
end

it "has the right details" do
expect(dependency).to be_a(Dependabot::Dependency)
expect(dependency.name).to eq("rsc.io/qr")
end
end
end
end

0 comments on commit a785958

Please sign in to comment.