diff --git a/common/lib/dependabot/pull_request_creator/message_builder.rb b/common/lib/dependabot/pull_request_creator/message_builder.rb index 51004e8976a..ce23bffbddd 100644 --- a/common/lib/dependabot/pull_request_creator/message_builder.rb +++ b/common/lib/dependabot/pull_request_creator/message_builder.rb @@ -192,11 +192,16 @@ def requirement_commit_message_intro end # rubocop:disable Metrics/PerceivedComplexity + # rubocop:disable Metrics/AbcSize def version_commit_message_intro return multidependency_property_intro if dependencies.count > 1 && updating_a_property? return dependency_set_intro if dependencies.count > 1 && updating_a_dependency_set? + return transitive_multidependency_intro if dependencies.count > 1 && + updating_top_level_and_transitive_dependencies? && + dependencies.none?(&:removed?) + return multidependency_intro if dependencies.count > 1 dependency = dependencies.first @@ -216,6 +221,7 @@ def version_commit_message_intro end # rubocop:enable Metrics/PerceivedComplexity + # rubocop:enable Metrics/AbcSize def multidependency_property_intro dependency = dependencies.first @@ -239,6 +245,23 @@ def multidependency_intro "dependencies needed to be updated together." end + def transitive_multidependency_intro + dependency = dependencies.first + + msg = "Bumps #{dependency_links[0]} to #{new_version(dependency)}" + + msg += if dependencies.count > 2 + " and updates ancestor dependencies #{dependency_links[0..-2].join(', ')} " \ + "and #{dependency_links[-1]}. " + else + " and updates ancestor dependency #{dependency_links[1]}. " + end + + msg += "These dependencies need to be updated together.\n" + + msg + end + def from_version_msg(previous_version) return "" unless previous_version @@ -257,6 +280,11 @@ def updating_a_dependency_set? any? { |r| r.dig(:metadata, :dependency_set) } end + def updating_top_level_and_transitive_dependencies? + dependencies.any?(&:top_level?) && + dependencies.any? { |dep| !dep.top_level? } + end + def property_name @property_name ||= dependencies.first.requirements. find { |r| r.dig(:metadata, :property_name) }&. diff --git a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb index 2b0f60b888a..4107a72a765 100644 --- a/common/spec/dependabot/pull_request_creator/message_builder_spec.rb +++ b/common/spec/dependabot/pull_request_creator/message_builder_spec.rb @@ -1242,6 +1242,71 @@ def commits_details(base:, head:) end end + context "and transitive security vulnerabilities fixed" do + let(:dependencies) { [transitive_dependency, dependency] } + let(:transitive_dependency) do + Dependabot::Dependency.new( + name: "statesman", + version: "1.6.0", + previous_version: "1.5.0", + package_manager: "dummy", + requirements: [], + previous_requirements: [] + ) + end + + before do + statesman_repo_url = + "https://api.github.com/repos/gocardless/statesman" + stub_request(:get, statesman_repo_url). + to_return(status: 200, + body: fixture("github", "statesman_repo.json"), + headers: json_header) + stub_request(:get, "#{statesman_repo_url}/contents/"). + to_return(status: 200, + body: fixture("github", "statesman_files.json"), + headers: json_header) + stub_request(:get, "#{statesman_repo_url}/releases?per_page=100"). + to_return(status: 200, + body: fixture("github", "business_releases.json"), + headers: json_header) + stub_request(:get, "https://api.github.com/repos/gocardless/" \ + "statesman/contents/CHANGELOG.md?ref=master"). + to_return(status: 200, + body: fixture("github", "changelog_contents.json"), + headers: json_header) + stub_request(:get, "https://rubygems.org/api/v1/gems/statesman.json"). + to_return( + status: 200, + body: fixture("ruby", "rubygems_response_statesman.json") + ) + + service_pack_url = + "https://github.com/gocardless/statesman.git/info/refs" \ + "?service=git-upload-pack" + stub_request(:get, service_pack_url). + to_return( + status: 200, + body: fixture("git", "upload_packs", "no_tags"), + headers: { + "content-type" => "application/x-git-upload-pack-advertisement" + } + ) + end + + it "includes details of both dependencies" do + expect(pr_message). + to start_with( + "Bumps [statesman](https://github.com/gocardless/statesman) to 1.6.0 " \ + "and updates ancestor dependency [business](https://github.com/gocardless/business). " \ + "These dependencies need to be updated together.\n\n" \ + "Updates `statesman` from 1.5.0 to 1.6.0\n" \ + "
\n" \ + "Release notes\n" + ) + end + end + context "and an upgrade guide that can be pulled in" do let(:dependency) do Dependabot::Dependency.new(