diff --git a/cargo/lib/dependabot/cargo/file_fetcher.rb b/cargo/lib/dependabot/cargo/file_fetcher.rb index d304fab524b..26d50a29cb6 100644 --- a/cargo/lib/dependabot/cargo/file_fetcher.rb +++ b/cargo/lib/dependabot/cargo/file_fetcher.rb @@ -152,30 +152,32 @@ def fetch_path_dependency_files(file:, previously_fetched_files:) unfetchable_required_path_deps end - # rubocop:enable Metrics/PerceivedComplexity + def collect_path_dependencies_paths(dependencies) + paths = [] + dependencies.each do |_, details| + next unless details.is_a?(Hash) && details["path"] + paths << File.join(details["path"], "Cargo.toml").delete_prefix("/") + end + paths + end + + # rubocop:enable Metrics/PerceivedComplexity def path_dependency_paths_from_file(file) paths = [] - # Paths specified in dependency declaration + workspace = parsed_file(file).fetch("workspace", {}) Cargo::FileParser::DEPENDENCY_TYPES.each do |type| - parsed_file(file).fetch(type, {}).each do |_, details| - next unless details.is_a?(Hash) - next unless details["path"] - - paths << File.join(details["path"], "Cargo.toml").delete_prefix("/") - end + # Paths specified in dependency declaration + paths += collect_path_dependencies_paths(parsed_file(file).fetch(type, {})) + # Paths specified as workspace dependencies in workspace root + paths += collect_path_dependencies_paths(workspace.fetch(type, {})) end # Paths specified for target-specific dependencies parsed_file(file).fetch("target", {}).each do |_, t_details| Cargo::FileParser::DEPENDENCY_TYPES.each do |type| - t_details.fetch(type, {}).each do |_, details| - next unless details.is_a?(Hash) - next unless details["path"] - - paths << File.join(details["path"], "Cargo.toml").delete_prefix("/") - end + paths += collect_path_dependencies_paths(t_details.fetch(type, {})) end end @@ -263,6 +265,16 @@ def required_path?(file, path) end end + # Paths specified for workspace-wide dependencies + workspace = parsed_file(file).fetch("workspace", {}) + workspace.fetch("dependencies", {}).each do |_, details| + next unless details.is_a?(Hash) + next unless details["path"] + next unless path == File.join(details["path"], "Cargo.toml") + + return true if details["git"].nil? + end + # Paths specified as replacements parsed_file(file).fetch("replace", {}).each do |_, details| next unless details.is_a?(Hash) diff --git a/cargo/spec/dependabot/cargo/file_fetcher_spec.rb b/cargo/spec/dependabot/cargo/file_fetcher_spec.rb index a3202b9b45f..4038c06eedc 100644 --- a/cargo/spec/dependabot/cargo/file_fetcher_spec.rb +++ b/cargo/spec/dependabot/cargo/file_fetcher_spec.rb @@ -555,6 +555,45 @@ end end + context "with another workspace that uses excluded dependency" do + before do + stub_request(:get, url + "?ref=sha"). + with(headers: { "Authorization" => "token token" }). + to_return( + status: 200, + body: fixture("github", "contents_cargo_without_lockfile.json"), + headers: json_header + ) + stub_request(:get, url + "Cargo.toml?ref=sha"). + with(headers: { "Authorization" => "token token" }). + to_return(status: 200, body: parent_fixture, headers: json_header) + + stub_request(:get, url + "member/Cargo.toml?ref=sha"). + with(headers: { "Authorization" => "token token" }). + to_return(status: 200, body: member_fixture, headers: json_header) + + stub_request(:get, url + "excluded/Cargo.toml?ref=sha"). + with(headers: { "Authorization" => "token token" }). + to_return(status: 200, body: member_fixture, headers: json_header) + end + let(:parent_fixture) do + fixture("github", "contents_cargo_manifest_workspace_excluded_dependencies_root.json") + end + let(:member_fixture) do + fixture("github", "contents_cargo_manifest_workspace_excluded_dependencies_member.json") + end + let(:excluded_fixture) do + fixture("github", "contents_cargo_manifest_workspace_excluded_dependencies_excluded.json") + end + + it "uses excluded dependency as a support file" do + expect(file_fetcher_instance.files.map(&:name)). + to match_array(%w(Cargo.toml member/Cargo.toml excluded/Cargo.toml)) + expect(file_fetcher_instance.files.map(&:support_file?)). + to match_array([false, false, true]) + end + end + context "with a Cargo.toml that is unparseable" do before do stub_request(:get, url + "?ref=sha"). diff --git a/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_excluded.json b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_excluded.json new file mode 100644 index 00000000000..03a41437d2f --- /dev/null +++ b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_excluded.json @@ -0,0 +1,18 @@ +{ + "name": "Cargo.toml", + "path": "excluded/Cargo.toml", + "sha": "051e2034b8a01e834993e0252af26789829bbf1d", + "size": 115, + "url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/excluded/Cargo.toml?ref=main", + "html_url": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/excluded/Cargo.toml", + "git_url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/051e2034b8a01e834993e0252af26789829bbf1d", + "download_url": "https://raw.githubusercontent.com/ggawryal/test-cargo-inherited-excluded-dependency/main/excluded/Cargo.toml", + "type": "file", + "content": "W3BhY2thZ2VdCm5hbWUgPSAiZXhjbHVkZWQiCnZlcnNpb24gPSAiMC4xLjAi\nCmVkaXRpb24gPSAiMjAyMSIKCltkZXBlbmRlbmNpZXNdCmhleC1saXRlcmFs\nID0geyB2ZXJzaW9uID0gIjAuMy40IiB9Cg==\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/excluded/Cargo.toml?ref=main", + "git": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/051e2034b8a01e834993e0252af26789829bbf1d", + "html": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/excluded/Cargo.toml" + } +} diff --git a/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_member.json b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_member.json new file mode 100644 index 00000000000..ab7e6e5eaca --- /dev/null +++ b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_member.json @@ -0,0 +1,18 @@ +{ + "name": "Cargo.toml", + "path": "member/Cargo.toml", + "sha": "808c974349f211292687c550431634373b72df74", + "size": 143, + "url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/member/Cargo.toml?ref=main", + "html_url": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/member/Cargo.toml", + "git_url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/808c974349f211292687c550431634373b72df74", + "download_url": "https://raw.githubusercontent.com/ggawryal/test-cargo-inherited-excluded-dependency/main/member/Cargo.toml", + "type": "file", + "content": "W3BhY2thZ2VdCm5hbWUgPSAibWVtYmVyIgp2ZXJzaW9uID0gIjAuMS4wIgpl\nZGl0aW9uID0gIjIwMjEiCgpbZGVwZW5kZW5jaWVzXQpleGNsdWRlZCA9IHsg\nd29ya3NwYWNlID0gdHJ1ZSB9CmhleC1saXRlcmFsID0geyB3b3Jrc3BhY2Ug\nPSB0cnVlIH0=\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/member/Cargo.toml?ref=main", + "git": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/808c974349f211292687c550431634373b72df74", + "html": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/member/Cargo.toml" + } +} diff --git a/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_root.json b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_root.json new file mode 100644 index 00000000000..51d9a3699da --- /dev/null +++ b/cargo/spec/fixtures/github/contents_cargo_manifest_workspace_excluded_dependencies_root.json @@ -0,0 +1,18 @@ +{ + "name": "Cargo.toml", + "path": "Cargo.toml", + "sha": "db652f42a677b4686ee668d1d2af9b4266daa15d", + "size": 219, + "url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/Cargo.toml?ref=main", + "html_url": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/Cargo.toml", + "git_url": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/db652f42a677b4686ee668d1d2af9b4266daa15d", + "download_url": "https://raw.githubusercontent.com/ggawryal/test-cargo-inherited-excluded-dependency/main/Cargo.toml", + "type": "file", + "content": "W3dvcmtzcGFjZS5wYWNrYWdlXQp2ZXJzaW9uID0gIjAuMS4wIgoKW3dvcmtz\ncGFjZV0KCm1lbWJlcnMgPSBbIm1lbWJlciJdCmV4Y2x1ZGUgPSBbImV4Y2x1\nZGVkIl0KClt3b3Jrc3BhY2UuZGVwZW5kZW5jaWVzXQptZW1iZXIgPSB7IHBh\ndGggPSAibWVtYmVyIiB9CmV4Y2x1ZGVkID0geyBwYXRoID0gImV4Y2x1ZGVk\nIiB9CmhleC1saXRlcmFsID0geyB2ZXJzaW9uID0gIjAuMy40IiB9\n", + "encoding": "base64", + "_links": { + "self": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/contents/Cargo.toml?ref=main", + "git": "https://api.github.com/repos/ggawryal/test-cargo-inherited-excluded-dependency/git/blobs/db652f42a677b4686ee668d1d2af9b4266daa15d", + "html": "https://github.com/ggawryal/test-cargo-inherited-excluded-dependency/blob/main/Cargo.toml" + } +}