Skip to content

Commit

Permalink
Support retrieving workspace of path dependencies in cargo (#10550)
Browse files Browse the repository at this point in the history
* Support retrieving workspace of path dependencies in cargo

* Fix fixture typo in cargo file_fetcher spec

---------

Co-authored-by: AbdulFattaah Popoola <abdulapopoola@github.com>
  • Loading branch information
Jefffrey and abdulapopoola authored Sep 9, 2024
1 parent d111771 commit f238da5
Show file tree
Hide file tree
Showing 11 changed files with 402 additions and 2 deletions.
66 changes: 65 additions & 1 deletion cargo/lib/dependabot/cargo/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,13 @@ def fetch_path_dependency_files(file:, previously_fetched_files:)
file: fetched_file,
previously_fetched_files: previously_fetched_files
)
[fetched_file, *grandchild_requirement_files]

# If this path dependency file is a workspace member that inherits from
# its root workspace, we search for the root to include it so Cargo can
# resolve the path dependency file manifest properly.
root = find_workspace_root(fetched_file, file) if workspace_member?(parsed_file(fetched_file))

[fetched_file, *grandchild_requirement_files, root]
rescue Dependabot::DependencyFileNotFound
next unless required_path?(file, path)

Expand Down Expand Up @@ -218,6 +224,64 @@ def replacement_path_dependency_paths_from_file(file)
paths
end

# See if this Cargo manifest inherits any property from a workspace
# (e.g. edition = { workspace = true }).
def workspace_member?(hash)
hash.each do |key, value|
if key == "workspace" && value == true
return true
elsif value.is_a?(Hash)
return workspace_member?(value)
end
end
false
end

# Find workspace root of this workspace member, first via package.workspace
# manifest key if present, otherwise resort to searching parent directories
# up till the repository root.
#
# original_manifest used for providing a useful error message.
sig do
params(workspace_member: Dependabot::DependencyFile,
original_manifest: Dependabot::DependencyFile).returns(T.nilable(Dependabot::DependencyFile))
end
def find_workspace_root(workspace_member, original_manifest)
current_dir = workspace_member.name.rpartition("/").first

workspace_root_dir = parsed_file(workspace_member).dig("package", "workspace")
unless workspace_root_dir.nil?
workspace_root = fetch_file_from_host(
File.join(current_dir, workspace_root_dir, "Cargo.toml"),
fetch_submodules: true
)
return workspace_root if parsed_file(workspace_root)["workspace"]

msg = "Could not resolve workspace root for path dependency " \
"#{workspace_member.path} of #{original_manifest.path}"
raise Dependabot::DependencyFileNotEvaluatable, msg
end

parent_dirs = current_dir.scan("/").length - 1
while parent_dirs >= 0
current_dir = File.join(current_dir, "..")
begin
parent_manifest = fetch_file_from_host(
File.join(current_dir, "Cargo.toml"),
fetch_submodules: true
)
return parent_manifest if parsed_file(parent_manifest)["workspace"]
rescue Dependabot::DependencyFileNotFound
# Cargo.toml not found in this parent, keep searching up
end
parent_dirs -= 1
end

msg = "Could not resolve workspace root for path dependency " \
"#{workspace_member.path} of #{original_manifest.path}"
raise Dependabot::DependencyFileNotEvaluatable, msg
end

def workspace_dependency_paths_from_file(file)
if parsed_file(file)["workspace"] &&
!parsed_file(file)["workspace"].key?("members")
Expand Down
160 changes: 159 additions & 1 deletion cargo/spec/dependabot/cargo/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@

stub_request(:get, url + "excluded/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(status: 200, body: member_fixture, headers: json_header)
.to_return(status: 200, body: excluded_fixture, headers: json_header)
end

let(:parent_fixture) do
Expand Down Expand Up @@ -742,4 +742,162 @@
.to raise_error(Dependabot::DependencyFileNotFound)
end
end

context "with a path dependency to a workspace member" do
let(:url) do
"https://api.github.com/repos/gocardless/bump/contents/"
end

before do
# Contents of these dirs aren't important
stub_request(:get, /#{Regexp.escape(url)}detached_crate_(success|fail_1|fail_2)\?ref=sha/)
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member", "contents_dir_detached_crate_success.json"),
headers: json_header
)

# Ignoring any .cargo requests
stub_request(:get, %r{#{Regexp.escape(url)}\w+/\.cargo\?ref=sha})
.with(headers: { "Authorization" => "token token" })
.to_return(status: 404, headers: json_header)

# All the manifest requests
stub_request(:get, url + "detached_crate_fail_1/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_detached_crate_fail_1.json"),
headers: json_header
)
stub_request(:get, url + "detached_crate_fail_2/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_detached_crate_fail_2.json"),
headers: json_header
)
stub_request(:get, url + "detached_crate_success/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_detached_crate_success.json"),
headers: json_header
)
stub_request(:get, url + "detached_workspace_member/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_detached_workspace_member.json"),
headers: json_header
)
stub_request(:get, url + "incorrect_detached_workspace_member/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_incorrect_detached_workspace_member.json"),
headers: json_header
)
stub_request(:get, url + "incorrect_workspace/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_incorrect_workspace.json"),
headers: json_header
)
stub_request(:get, url + "workspace/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member", "contents_cargo_manifest_workspace.json"),
headers: json_header
)
stub_request(:get, url + "workspace/nested_one/nested_two/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(
status: 200,
body: fixture("github", "path_dependency_workspace_member",
"contents_cargo_manifest_workspace_nested_one_nested_two.json"),
headers: json_header
)

# nested_one dir has nothing of interest
stub_request(:get, url + "workspace/nested_one?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(status: 200, body: "[]", headers: json_header)
stub_request(:get, url + "workspace/nested_one/Cargo.toml?ref=sha")
.with(headers: { "Authorization" => "token token" })
.to_return(status: 404, headers: json_header)
end

context "with a resolvable workspace root" do
let(:source) do
Dependabot::Source.new(
provider: "github",
repo: "gocardless/bump",
directory: "detached_crate_success/"
)
end

it "fetches the dependency successfully" do
expect(file_fetcher_instance.files.map(&:name))
.to match_array(%w(
Cargo.toml
../detached_workspace_member/Cargo.toml
../workspace/Cargo.toml
../workspace/nested_one/nested_two/Cargo.toml
))
expect(file_fetcher_instance.files.map(&:path))
.to match_array(%w(
/detached_crate_success/Cargo.toml
/detached_workspace_member/Cargo.toml
/workspace/Cargo.toml
/workspace/nested_one/nested_two/Cargo.toml
))
end
end

context "with no workspace root via parent directory search" do
let(:source) do
Dependabot::Source.new(
provider: "github",
repo: "gocardless/bump",
directory: "detached_crate_fail_1/"
)
end

it "raises a DependencyFileNotEvaluatable error" do
expect { file_fetcher_instance.files }.to raise_error(Dependabot::DependencyFileNotEvaluatable) do |error|
expect(error.message)
.to eq("Could not resolve workspace root for path dependency " \
"/incorrect_workspace/Cargo.toml of /detached_crate_fail_1/Cargo.toml")
end
end
end

context "with no workspace root via package.workspace key" do
let(:source) do
Dependabot::Source.new(
provider: "github",
repo: "gocardless/bump",
directory: "detached_crate_fail_2/"
)
end

it "raises a DependencyFileNotEvaluatable error" do
expect { file_fetcher_instance.files }.to raise_error(Dependabot::DependencyFileNotEvaluatable) do |error|
expect(error.message)
.to eq("Could not resolve workspace root for path dependency " \
"/incorrect_detached_workspace_member/Cargo.toml of /detached_crate_fail_2/Cargo.toml")
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "detached_crate_fail_1/Cargo.toml",
"sha": "478610ce460c32a710e00cc0d1528cf1f03002e8",
"size": 150,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_fail_1/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_fail_1/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/478610ce460c32a710e00cc0d1528cf1f03002e8",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/detached_crate_fail_1/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiZGV0YWNoZWRfY3JhdGVfZmFpbF8xIgp2ZXJz\naW9uID0gIjAuMS4wIgplZGl0aW9uID0gIjIwMjEiCgpbZGVwZW5kZW5jaWVz\nXQppbmNvcnJlY3Rfd29ya3NwYWNlID0geyBwYXRoID0gIi4uL2luY29ycmVj\ndF93b3Jrc3BhY2UiIH0K\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_fail_1/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/478610ce460c32a710e00cc0d1528cf1f03002e8",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_fail_1/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "detached_crate_fail_2/Cargo.toml",
"sha": "64b36bdb1f27623f91db2fce80c442dbfdc1e3de",
"size": 182,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_fail_2/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_fail_2/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/64b36bdb1f27623f91db2fce80c442dbfdc1e3de",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/detached_crate_fail_2/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiZGV0YWNoZWRfY3JhdGVfZmFpbF8yIgp2ZXJz\naW9uID0gIjAuMS4wIgplZGl0aW9uID0gIjIwMjEiCgpbZGVwZW5kZW5jaWVz\nXQppbmNvcnJlY3RfZGV0YWNoZWRfd29ya3NwYWNlX21lbWJlciA9IHsgcGF0\naCA9ICIuLi9pbmNvcnJlY3RfZGV0YWNoZWRfd29ya3NwYWNlX21lbWJlciIg\nfQo=\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_fail_2/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/64b36bdb1f27623f91db2fce80c442dbfdc1e3de",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_fail_2/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "detached_crate_success/Cargo.toml",
"sha": "ac8001fcf77093cd9d06e07dcb6150a5b94f80cc",
"size": 224,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_success/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_success/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/ac8001fcf77093cd9d06e07dcb6150a5b94f80cc",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/detached_crate_success/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiZGV0YWNoZWRfY3JhdGVfc3VjY2VzcyIKdmVy\nc2lvbiA9ICIwLjEuMCIKZWRpdGlvbiA9ICIyMDIxIgoKW2RlcGVuZGVuY2ll\nc10KbmVzdGVkX3R3byA9IHsgcGF0aCA9ICIuLi93b3Jrc3BhY2UvbmVzdGVk\nX29uZS9uZXN0ZWRfdHdvIiB9CmRldGFjaGVkX3dvcmtzcGFjZV9tZW1iZXIg\nPSB7IHBhdGggPSAiLi4vZGV0YWNoZWRfd29ya3NwYWNlX21lbWJlciIgfQo=\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_crate_success/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/ac8001fcf77093cd9d06e07dcb6150a5b94f80cc",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_crate_success/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "detached_workspace_member/Cargo.toml",
"sha": "722adc04921df580cef0ac8dc7a6f255ba2d785a",
"size": 134,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_workspace_member/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_workspace_member/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/722adc04921df580cef0ac8dc7a6f255ba2d785a",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/detached_workspace_member/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiZGV0YWNoZWRfd29ya3NwYWNlX21lbWJlciIK\ndmVyc2lvbiA9IHsgd29ya3NwYWNlID0gdHJ1ZSB9CmVkaXRpb24gPSB7IHdv\ncmtzcGFjZSA9IHRydWUgfQp3b3Jrc3BhY2UgPSAiLi4vd29ya3NwYWNlIgo=\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/detached_workspace_member/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/722adc04921df580cef0ac8dc7a6f255ba2d785a",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/detached_workspace_member/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "incorrect_detached_workspace_member/Cargo.toml",
"sha": "5e66c8d8fefaa5597fcc8e42f3825fd5345da41d",
"size": 156,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/incorrect_detached_workspace_member/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/incorrect_detached_workspace_member/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/5e66c8d8fefaa5597fcc8e42f3825fd5345da41d",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/incorrect_detached_workspace_member/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiaW5jb3JyZWN0X2RldGFjaGVkX3dvcmtzcGFj\nZV9tZW1iZXIiCnZlcnNpb24gPSB7IHdvcmtzcGFjZSA9IHRydWUgfQplZGl0\naW9uID0geyB3b3Jrc3BhY2UgPSB0cnVlIH0Kd29ya3NwYWNlID0gIi4uL2Rl\ndGFjaGVkX2NyYXRlX2ZhaWxfMSIK\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/incorrect_detached_workspace_member/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/5e66c8d8fefaa5597fcc8e42f3825fd5345da41d",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/incorrect_detached_workspace_member/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "incorrect_workspace/Cargo.toml",
"sha": "06d462e30161e08716938ad0a3eec47d4287795d",
"size": 101,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/incorrect_workspace/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/incorrect_workspace/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/06d462e30161e08716938ad0a3eec47d4287795d",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/incorrect_workspace/Cargo.toml",
"type": "file",
"content": "W3BhY2thZ2VdCm5hbWUgPSAiaW5jb3JyZWN0X3dvcmtzcGFjZSIKdmVyc2lv\nbiA9IHsgd29ya3NwYWNlID0gdHJ1ZSB9CmVkaXRpb24gPSB7IHdvcmtzcGFj\nZSA9IHRydWUgfQo=\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/incorrect_workspace/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/06d462e30161e08716938ad0a3eec47d4287795d",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/incorrect_workspace/Cargo.toml"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Cargo.toml",
"path": "workspace/Cargo.toml",
"sha": "18480978e4ccef6558c226de82efb70bb0afd3e8",
"size": 143,
"url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/workspace/Cargo.toml?ref=master",
"html_url": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/workspace/Cargo.toml",
"git_url": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/18480978e4ccef6558c226de82efb70bb0afd3e8",
"download_url": "https://raw.githubusercontent.com/Jefffrey/dependabot-cargo-test/master/workspace/Cargo.toml",
"type": "file",
"content": "W3dvcmtzcGFjZS5wYWNrYWdlXQp2ZXJzaW9uID0gIjAuMS4wIgplZGl0aW9u\nID0gIjIwMjEiCgpbd29ya3NwYWNlXQptZW1iZXJzID0gWwogICJuZXN0ZWRf\nb25lL25lc3RlZF90d28iLAogICIuLi9kZXRhY2hlZF93b3Jrc3BhY2VfbWVt\nYmVyIgpdCgo=\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/contents/workspace/Cargo.toml?ref=master",
"git": "https://api.github.com/repos/Jefffrey/dependabot-cargo-test/git/blobs/18480978e4ccef6558c226de82efb70bb0afd3e8",
"html": "https://github.com/Jefffrey/dependabot-cargo-test/blob/master/workspace/Cargo.toml"
}
}
Loading

0 comments on commit f238da5

Please sign in to comment.