From 846b944f3f6c9d504707d692a4ac41ce47d51aac Mon Sep 17 00:00:00 2001 From: Barry Gordon Date: Tue, 10 Mar 2026 15:59:46 +0000 Subject: [PATCH 1/2] Add scanned_manifests_path metadata to snapshots --- .../lib/github_api/dependency_submission.rb | 22 ++++++++++++++++++- .../dependabot/update_graph_processor_spec.rb | 7 ++++++ .../github_api/dependency_submission_spec.rb | 5 +++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/updater/lib/github_api/dependency_submission.rb b/updater/lib/github_api/dependency_submission.rb index 0be58c43e25..761ab31f311 100644 --- a/updater/lib/github_api/dependency_submission.rb +++ b/updater/lib/github_api/dependency_submission.rb @@ -110,7 +110,8 @@ def payload manifests: manifests, metadata: { status: status.serialize, - reason: reason + reason: reason, + scanned_manifest_paths: scanned_manifest_paths }.compact } end @@ -179,5 +180,24 @@ def manifests } } end + + # Returns the manifest paths this snapshot scanned. + # + # This allows the snapshot service to check the snapshot against those requested + # even if the `manifests` property is empty. + # + # Note: We currently submit each manifest path separately, but we use an array + # to align with the `manifests` property and allow flexibility in future. + sig do + returns(T::Array[T::Hash[Symbol, String]]) + end + def scanned_manifest_paths + [ + { + ecosystem: GithubApi::EcosystemMapper.ecosystem_for(package_manager), + manifest_path: File.dirname(manifest_file.path) + } + ] + end end end diff --git a/updater/spec/dependabot/update_graph_processor_spec.rb b/updater/spec/dependabot/update_graph_processor_spec.rb index fac84de23c4..5b833d38e7e 100644 --- a/updater/spec/dependabot/update_graph_processor_spec.rb +++ b/updater/spec/dependabot/update_graph_processor_spec.rb @@ -388,6 +388,7 @@ # It should contain the expected metadata expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::FAILED.serialize) expect(payload[:metadata][:reason]).to eql("dependency_file_not_evaluatable") + expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) end it "correctly snapshots the second directory" do @@ -418,6 +419,10 @@ # We should have metadata indicating a successful snapshot expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::SUCCESS.serialize) expect(payload[:metadata][:reason]).to be_nil + expect(payload[:metadata][:scanned_manifest_paths]).to eql( + [{ ecosystem: "rubygems", + manifest_path: "/subproject" }] + ) end end end @@ -557,6 +562,7 @@ # It should contain the expected metadata expect(payload[:metadata][:status]).to eq(GithubApi::DependencySubmission::SnapshotStatus::SKIPPED.serialize) expect(payload[:metadata][:reason]).to eq(GithubApi::DependencySubmission::EMPTY_REASON_NO_MANIFESTS) + expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) end update_graph_processor.run @@ -665,6 +671,7 @@ def fetch_subdependencies(_dependency) # We should have metadata indicating a successful snapshot expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::DEGRADED.serialize) expect(payload[:metadata][:reason]).to eql(GithubApi::DependencySubmission::DEGRADED_REASON_SUBDEPENDENCY_ERR) + expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) end expect(service).to receive(:record_update_job_warning) do |args| diff --git a/updater/spec/github_api/dependency_submission_spec.rb b/updater/spec/github_api/dependency_submission_spec.rb index 44ce98772b8..c9556b70ebd 100644 --- a/updater/spec/github_api/dependency_submission_spec.rb +++ b/updater/spec/github_api/dependency_submission_spec.rb @@ -111,6 +111,11 @@ expect(payload[:detector][:version]).to eq(Dependabot::VERSION) expect(payload[:job][:correlator]).to eq("dependabot-bundler") expect(payload[:job][:id]).to eq("9999") + + # Check dependabot-specific metadata keys + expect(payload[:metadata][:status]).to eql("ok") + expect(payload[:metadata][:reason]).to be_nil + expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) end it "affixes to use the updater sha if available" do From 0c3042b9ed3c7a2f799f6b440d91f2f25b53f505 Mon Sep 17 00:00:00 2001 From: Barry Gordon Date: Wed, 11 Mar 2026 17:40:05 +0000 Subject: [PATCH 2/2] Switch to using a string representation --- .../lib/github_api/dependency_submission.rb | 29 +++++++++---------- .../dependabot/update_graph_processor_spec.rb | 11 +++---- .../github_api/dependency_submission_spec.rb | 2 +- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/updater/lib/github_api/dependency_submission.rb b/updater/lib/github_api/dependency_submission.rb index 761ab31f311..d1ec9b72d8b 100644 --- a/updater/lib/github_api/dependency_submission.rb +++ b/updater/lib/github_api/dependency_submission.rb @@ -108,10 +108,17 @@ def payload url: SNAPSHOT_DETECTOR_URL }, manifests: manifests, + # TODO: Move use of metadata to a Dependabot-specific object + # + # We are using the existing job metadata as a bag-of-values for error handling + # and job tracking that is specific to Dependabot-created submissions. + # + # In future, we should extend the public API schema with a validated object to + # harden this contract. metadata: { status: status.serialize, reason: reason, - scanned_manifest_paths: scanned_manifest_paths + scanned_manifest_path: scanned_manifest_path }.compact } end @@ -181,23 +188,15 @@ def manifests } end - # Returns the manifest paths this snapshot scanned. + # Returns a synopsis of the scan performed in the format `ecosystem::manifest_path`, e.g. + # - `golang::/` + # - `rubygems::rails_app/` # - # This allows the snapshot service to check the snapshot against those requested - # even if the `manifests` property is empty. - # - # Note: We currently submit each manifest path separately, but we use an array - # to align with the `manifests` property and allow flexibility in future. sig do - returns(T::Array[T::Hash[Symbol, String]]) + returns(String) end - def scanned_manifest_paths - [ - { - ecosystem: GithubApi::EcosystemMapper.ecosystem_for(package_manager), - manifest_path: File.dirname(manifest_file.path) - } - ] + def scanned_manifest_path + "#{GithubApi::EcosystemMapper.ecosystem_for(package_manager)}::#{File.dirname(manifest_file.path)}" end end end diff --git a/updater/spec/dependabot/update_graph_processor_spec.rb b/updater/spec/dependabot/update_graph_processor_spec.rb index 5b833d38e7e..f1c64469399 100644 --- a/updater/spec/dependabot/update_graph_processor_spec.rb +++ b/updater/spec/dependabot/update_graph_processor_spec.rb @@ -388,7 +388,7 @@ # It should contain the expected metadata expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::FAILED.serialize) expect(payload[:metadata][:reason]).to eql("dependency_file_not_evaluatable") - expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) + expect(payload[:metadata][:scanned_manifest_path]).to eql("rubygems::/") end it "correctly snapshots the second directory" do @@ -419,10 +419,7 @@ # We should have metadata indicating a successful snapshot expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::SUCCESS.serialize) expect(payload[:metadata][:reason]).to be_nil - expect(payload[:metadata][:scanned_manifest_paths]).to eql( - [{ ecosystem: "rubygems", - manifest_path: "/subproject" }] - ) + expect(payload[:metadata][:scanned_manifest_path]).to eql("rubygems::/subproject") end end end @@ -562,7 +559,7 @@ # It should contain the expected metadata expect(payload[:metadata][:status]).to eq(GithubApi::DependencySubmission::SnapshotStatus::SKIPPED.serialize) expect(payload[:metadata][:reason]).to eq(GithubApi::DependencySubmission::EMPTY_REASON_NO_MANIFESTS) - expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) + expect(payload[:metadata][:scanned_manifest_path]).to eql("rubygems::/") end update_graph_processor.run @@ -671,7 +668,7 @@ def fetch_subdependencies(_dependency) # We should have metadata indicating a successful snapshot expect(payload[:metadata][:status]).to eql(GithubApi::DependencySubmission::SnapshotStatus::DEGRADED.serialize) expect(payload[:metadata][:reason]).to eql(GithubApi::DependencySubmission::DEGRADED_REASON_SUBDEPENDENCY_ERR) - expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) + expect(payload[:metadata][:scanned_manifest_path]).to eql("rubygems::/") end expect(service).to receive(:record_update_job_warning) do |args| diff --git a/updater/spec/github_api/dependency_submission_spec.rb b/updater/spec/github_api/dependency_submission_spec.rb index c9556b70ebd..999691af656 100644 --- a/updater/spec/github_api/dependency_submission_spec.rb +++ b/updater/spec/github_api/dependency_submission_spec.rb @@ -115,7 +115,7 @@ # Check dependabot-specific metadata keys expect(payload[:metadata][:status]).to eql("ok") expect(payload[:metadata][:reason]).to be_nil - expect(payload[:metadata][:scanned_manifest_paths]).to eql([{ ecosystem: "rubygems", manifest_path: "/" }]) + expect(payload[:metadata][:scanned_manifest_path]).to eql("rubygems::/") end it "affixes to use the updater sha if available" do