Skip to main content

Build dependencies

info

Build-dependencies capture is new in 7.0.0. The Gradle plugin walks the resolved classpath at task execution and uploads a normalised DFS-ordered list. The same blob feeds Bugsee's vulnerability scan, so capturing dependencies is also the gate for OSV-backed CVE reporting.

What gets captured​

The plugin's DependencyCollector resolves a Gradle configuration (runtime by default), walks it in depth-first pre-order, and emits a DependencyEntry for every node:

FieldMeaning
idStable identifier — group:name:version for libraries, project path for project deps, file name for file deps
group / name / versionMaven coordinates (library entries only)
directtrue if the entry is in your build.gradle directly; false for transitive entries
scopeThe configuration the entry was resolved from (e.g. "runtime")
type"library" (Maven artefact), "project" (multi-module project dep), "file" (local jar/aar)
parentsThe list of ids that pulled this entry in — useful for "why is this here?" debugging
selected_reasonGradle's resolution reason string (e.g. "between versions 1.2 and 1.4 chose 1.4 by conflict resolution"); only populated when explicitly requested via DSL
ecosystemAlways "maven" on library entries — required so the vulnerability scanner can query OSV against the correct package universe

The walk is bounded by maxCount (default 5000); entries past the cap are dropped and the upload is flagged truncated: true.

What gets uploaded​

The inline dependencies_summary is a top-level key on the build POST body — a compact aggregate that always ships with the build POST:

{
"total": 312,
"direct": 24,
"transitive": 288,
"by_type": {
"library": 296,
"project": 12,
"file": 4
},
"truncated": false,
"collected_at": "2026-06-10T14:30:00Z",
"collection_config": {
"scope": "runtime",
"include_selected_reason": false,
"max_count": 5000
}
}

The detail blob — gzipped JSON — is shipped to a separate presigned PUT URL when dependencies.enabled is true:

{
"schema_version": 1,
"truncated": false,
"collection_config": { "scope": "runtime", "include_selected_reason": false, "max_count": 5000 },
"dependencies": [
{
"id": "androidx.compose.ui:ui:1.6.4",
"group": "androidx.compose.ui",
"name": "ui",
"version": "1.6.4",
"direct": true,
"scope": "runtime",
"type": "library",
"ecosystem": "maven",
"parents": []
}
]
}

The collection_config block is captured on every blob so the worker's diff logic can refuse to compare apples-to-oranges: a build collected with max_count=5000 is never diffed against one collected with max_count=1000.

Configuration​

bugsee {
buildInfo {
dependencies {
// Enable the upload of the gzipped detail blob (and
// therefore the dashboard's per-dependency table and
// the vulnerability scan). The inline summary always
// ships when `buildInfo.enabled` is true.
// Default: true.
enabled.set(true)

// Gradle configuration to walk. Default: "runtime".
// Use "compileClasspath" or a custom configuration name
// to capture compile-only or annotation-processor deps.
// scope.set("runtime")

// Include Gradle's `selectedReason` string on each
// entry. Useful for diagnosing version-conflict
// resolution but bloats the blob significantly.
// Default: false.
// includeSelectedReason.set(false)

// Hard cap on entries. Entries past the cap are
// dropped and `truncated: true` is set on the blob.
// Default: 5000.
// maxCount.set(5000)
}
}
}

What you see in the dashboard​

The build detail page's Dependencies tab is rendered when dependencies_status == 'ready'. Two parts:

Stat chip strip​

Across the top: Total, Direct, Transitive, Library, Project, File. When truncated is true, a warning chip appears so the cap doesn't silently hide the tail.

When the worker job is still in flight, a status pill replaces the chips — uploading (blob in transit), processing (worker normalising and diffing), or failed (schema mismatch / oversize / S3 transient).

Vulnerability strip​

The Dependencies tab is also where Bugsee's vulnerability findings show up — see the dedicated Vulnerabilities page for the details. The strip renders between the chip row and the table whenever vuln_scan_summary is populated.

Per-dependency table​

A scrollable, sortable, filterable table listing every captured entry. Columns: id, direct/transitive, type, parents count, and (when present) selected_reason. Click a row to expand the full parents path — useful for tracing "why is this transitive dep here?" through the import graph.

Worker pipeline​

The S3 ObjectCreated event for the uploaded dependencies blob is routed to jobs.dependencies.Process on the Bugsee worker. The pipeline mirrors timings: bootstrap PATCH (dependencies_status='processing') → download with cap (32 MiB inflated, 100 000 entries) → validate schema_version → re-upload the gzipped bytes to the per-build S3 prefix → run a diff vs the previous build (gated on identical collection_config so the diff stays meaningful) → final PATCH (dependencies_ref, dependencies_status='ready').

When dependencies_status flips to 'ready', the worker also enqueues the vulnerability scan job for the same build — vulnerabilities are derived from the dependencies list, so the scan can't run before the dependencies blob lands.

Found an issue, typo, or wrong statement on this page? Report it now →