DEPLENS / DOCUMENTATION

Full reference.

Inventory, resolution checks, source edits and Gradle compatibility.

Configuration guide ↗

Behavior and configuration reference for DepLens 0.1.0.

Get started · Configuration

Inventory#

Terminal
./gradlew depLensInventory

The task prints a console inventory and writes build/reports/deplens/inventory.json. It does not check for newer releases or edit version declarations.

The inventory includes:

  • Direct external dependencies, platforms/BOMs, and effective or explicitly declared constraints.
  • All declaration configurations, including annotation processors, custom configurations and plugin-provided tool dependencies.
  • Actual versions from Gradle's resolution graph, preserving different outcomes in different configurations.
  • Root and subproject context, declaration locations, literal selectors, properties and shared literal variables.
  • Standard file catalogs, catalogs imported with create(...) { from(files(...)) }, shared version keys, library/plugin aliases, rich versions, and bundles. Other catalog definitions are read through Gradle's catalog API with unknown physical source locations.
  • Plugin declarations, apply false, catalog aliases, plugin management, plugin marker classpath resolutions and local conventions.
  • Running Gradle and wrapper versions, including a warning when they differ.
  • Project-local inventories for included builds (including build-logic when declared with includeBuild) and buildSrc, merged with their original build paths. Gradle configures these builds within the current build tree; Gradle may compile their build logic as it normally does when configuring a build.

Transitive dependencies participate in Gradle resolution but do not become inventory rows. A direct dependency without a version remains visible. Platform relationships come from actual platform constraint edges, rather than assuming every versionless dependency is BOM-managed.

The report uses schema version 1. Each entry retains its selectors, configurations, declarations, version sources, catalog references, resolutions, platform relationships and plugin states. Multiple declarations and resolutions are retained instead of allowing the first configuration to win.

NOT_CHECKED means that update discovery has not run. A resolution failure also records FAILED; constraints and platforms may add CONSTRAINED. Partial inventories are labelled incomplete, with sanitized warnings. An inventory never claims that dependencies are up to date.

Source attribution boundaries#

Source attribution is conservative. Computed expressions, arbitrary script logic, unusual DSL syntax, remote/programmatic catalogs and declarations contributed by plugins may have an UNKNOWN source or line 0. The declared and resolved Gradle data are retained. DepLens does not guess an editable location. Platform attribution uses Gradle's graph; a BOM import flattened into a library's POM metadata may not expose its original BOM source.

Plugin versions are read from Gradle's marker classpath when available. Local conventions have no external module version. Plugins already supplied through an opaque parent classloader can have an unknown resolved version. Use the settings entry point when you need the settings plugin classpath; the project entry point still scans settings declarations.

Separate-build checks can be controlled independently in the root project's build.gradle:

Groovy
depLens {
    checkIncludedBuilds = false // builds declared with includeBuild(...)
    checkBuildSrc = false      // buildSrc only
}

Both options default to true and are inherited as defaults by nested checks. The current build and its subprojects are always included. Intentionally excluded builds are absent from the DepLens report and do not make it incomplete. These options control DepLens's checks; Gradle may still configure or build included plugins and buildSrc to load the consuming build.

Gradle captures each project through its native isolated model boundary. The public task consumes detached values and performs fresh update queries in the same Gradle process. Settings plugins and the wrapper are collected once per build.

Inventory uses native project models, including with Configuration Cache and Isolated Projects enabled. Separate builds retain their configured repositories and Gradle authentication. If a build cannot be configured at all, Gradle may fail before an inventory can be produced. Per-entry resolution failures within configured builds remain visible as partial results.

Update checks#

Terminal
./gradlew depLensCheck
# Or check one component kind:
./gradlew depLensCheckDependencies
./gradlew depLensCheckPlugins
./gradlew depLensCheckGradle

depLensCheck is available in every project where DepLens is applied. Its report is build/reports/deplens/depLensCheck.json.

depLensCheck checks all component kinds directly. By default, checks show recommendations (coordinate current -> recommended), clearly labelled available-only versions, and successful checks with no matching candidates, grouped into dependencies, platforms/BOMs, version constraints, Gradle plugins and the Gradle wrapper. Empty groups are omitted. Identical updates (same component kind, coordinate, current versions, target and qualification) are shown once across projects and builds; the summary counts these unique rows. Different current versions or recommendations remain separate. JSON and --info retain every consuming project and its resolution context. Purely BOM-managed dependencies have no independent update recommendations: their versions are updated through the owning BOM, even when that BOM is current, pinned or imported transitively. Ownership is recorded per resolution configuration. Explicit dependency versions, constraints and Spring dependency-management overrides remain independently checked. The summary counts recommended update rows, available-only rows and distinct BOM-managed dependencies separately; BOM update rows include their managed-dependency count (counts may overlap across BOMs). JSON and --info retain the ownership details and use BOM_MANAGED for managed dependencies. Spring's effective management, configuration hierarchy, imported BOMs and nested imports are inspected by the optional integrations.spring.SpringDependencyManagementAdapter, activated only when the Spring dependency-management plugin is applied. The inventory and update engine use a common integration interface; Spring reflection stays inside the Spring integration package; effective version management is captured as integration-neutral values for native queries. Spring Boot’s automatic alignment of protoc with protobuf-java and protoc-gen-grpc-java with grpc-util is recorded in versionAlignments; aligned tools have no independent update recommendations. The Protobuf unset-version sentinel is normalized only when the Boot alignment is verified. When runtimeClasspath is excluded from analysis, the supported Boot alignment contract and already resolved, rule-selected Protobuf tool versions identify the sentinel without resolving the excluded classpath. These tools remain version-aligned; BOM ownership is not inferred from an unchecked runtime graph. Explicit tool versions remain independently checked. If the runtime version is overridden, aligned tools use VERSION_ALIGNED instead of claiming BOM ownership. The adapter also recognizes Spring Boot’s automatic kotlin.version alignment to the Kotlin JVM plugin: affected libraries, generated stdlib defaults and implicit Kotlin constraints use VERSION_ALIGNED. Explicit library versions, constraints and user-provided kotlin.version properties remain independently checked; the Kotlin plugin itself remains a separate update target. Unsupported model versions produce an incomplete-inventory diagnostic rather than guessing ownership. A single short summary of updates, failures and unchecked entries follows. The report path and diagnostic details appear with --info. IDE download notifications produced during the check are suppressed at the default log level; downloads and Gradle caching still work normally. --info also retains download progress. Gradle’s own build setup messages and downloads before the check starts are unaffected. Up-to-date entries, local plugins and wrapperless builds do not add console noise. Detailed warnings remain available in the aggregated JSON report and with --info. Use ./gradlew depLensCheck --info for sources, rules, candidate lists and per-configuration resolution details. Each check writes a JSON report at build/reports/deplens/<taskName>.json, including the original inventory and per-configuration updateContexts. Included builds, buildSrc and build-logic are checked in their own Gradle contexts and merged. Spring dependency management is preserved when checking versionless dependencies. No source files are changed.

Parallel project checks#

Update checks run through one partialDepLensCheck_<id> task per analyzed project, followed by the requested aggregate task (depLensCheck, a component-specific check, or depLensInteractiveUpdate). Gradle can schedule independent project tasks concurrently when org.gradle.parallel=true or --parallel is enabled. Gradle worker limits still apply; tasks within one project retain Gradle's project locking. This includes selected included builds and buildSrc. The inventory-only task collects declarations without checking for updates.

Projects in the requesting build own their check tasks. Models from included builds and buildSrc are checked by separate tasks hosted in the requesting project, using the same captured inputs and detached resolution engine. This preserves their check coverage without adding cross-build task dependencies to the consumer's task graph. Hosted tasks share the requesting project's Gradle lock and therefore run sequentially with its other tasks; ordinary project checks remain parallel. Consumer build code does not need to be patched to accommodate foreign project vertices.

Each producer uses that project's captured policies and a separate detached resolver. An invocation-local shared build service coordinates identical version/selector/graph queries and HTTP 429 cooldowns across the producers. Only identical query keys wait for each other; independent queries proceed concurrently. Batch prefetch never waits while holding another batch's query locks. No live resolver or mutable report is shared between project tasks.

Dependency discovery finalizes the captured models once per requested check and memoizes the producer-output plan. Repeated reads of the lazy file collection do not recapture the build tree or reconstruct the merged inventory. Producer registration retains only its own serialized inventory snapshot, allowing the temporary merged inventory to be released after planning.

Producers write internal JSON files under their hosting project's build directory at reports/deplens/partials/. The aggregate reads exactly the scheduled outputs, validates project ownership, and merges entries in captured project order. Warning additions and removals are both preserved, including warnings cleared by a successful baseline refresh. Plugin-version inheritance, final sorting/grouping, completion flags, the public JSON report, failure-on-updates policy, and interactive selection are applied after all partial reports are available. Partial tasks do not print duplicate update reports or enforce failure-on-updates individually. The aggregate writes the public report for the selected full or fast check mode.

Fail CI builds on incomplete checks#

depLens.failIfChecksIncomplete defaults to false. Enable it in the project whose check task you invoke to fail CI when the aggregated inventory is incomplete or the selected update checks did not complete:

Groovy
depLens {
    failIfChecksIncomplete = true
}
Kotlin
depLens {
    failIfChecksIncomplete.set(true)
}

The policy applies to depLensCheck and component-specific check tasks, covering all analyzed projects and included builds in that invocation. It evaluates the full report before output filtering. The JSON report and console summary are written before the task fails with DepLens checks incomplete. JSON complete describes inventory completeness; checksComplete describes update-check coverage.

Repository/resolution failures, incomplete inventory diagnostics and unchecked selected targets (such as an offline wrapper feed) can trigger this policy. NO_MATCHING_CANDIDATES is a successful policy result and does not trigger it. Pinned entries, local components, wrapperless builds and deliberately unselected component types do not count as unchecked update targets. A completed --fast check remains successful in its selected mode; it does not claim full graph validation. Analysis filters determine the checked scope; output filters cannot hide a failure from the policy.

This setting is independent of section-level failIfUpdatesFound: available updates alone do not fail a check unless that policy is enabled. When both apply, incomplete checks are reported as the failure reason. Inventory-only and interactive update tasks do not enforce either CI failure policy.

Optional fast check#

Full build-context checks are enabled by default. To skip them for one invocation:

Terminal
./gradlew depLensCheck --fast
./gradlew depLensCheckDependencies --fast

Alternatively, configure the default in Groovy:

Groovy
depLens {
    checkBuildContext = false
}

Or in Kotlin:

Kotlin
depLens {
    checkBuildContext.set(false)
}

The setting is inherited by analyzed projects and builds without their own DepLens extension. --fast overrides the setting for every scope in that task invocation. Omitting --fast uses the configured default.

Fast checks retain current-inventory resolution, repositories, version discovery, normalization, stability/ignore filters, update tiers and selector queries. They skip full consumer-graph resolution for update candidates, including the older-compatible-candidate fallback. Consequently, candidate Java/variant compatibility and peer/BOM/constraint interactions are not verified. Current-inventory failures remain visible. Gradle wrapper checks are unchanged.

Fast-mode recommendations are the highest candidates allowed by the version policy, without a build-compatibility verdict. Dynamic selectors still require an explicit selection. Update rows show (build context not checked) and the summary identifies skipped full checks, including when no updates are found. JSON marks fast scopes with fastCheck, and their latestAllowedByBuild remains null. checksComplete describes completion of the selected mode; it does not claim full build-context validation in fast mode. Interactive updates also support this setting and retain the visible fast-check qualification.

For each resolved current version, the report distinguishes:

  • latestWithinSelector: the highest version Gradle resolves within the declaration in that configuration.
  • latestAvailable: the highest repository-listed version after stability/ignore filtering.
  • patch, minor, major: highest listed updates in the same minor, a later minor of the same major, or a later major respectively. candidates applies the configured allowed update tier.
  • latestAllowedByBuild: a separate resolution with the query dependency, original attributes, peer declarations, platforms, constraints and configuration resolution strategy.
  • outsideSelector: available versions requiring an explicit selector/version change. Dynamic selectors and ranges are preserved; their recommendedVersion stays null to avoid silently freezing them.

Candidate listing uses Gradle's component-selection API. Repository access, ordering, content filters, settings repository policies and authentication remain Gradle's responsibility; DepLens does not scrape plugin pages or use a separate Maven HTTP client. Buildscript dependencies use their script repository context. External plugins query their marker modules through their plugin classpath repositories; use the settings entry point for settings plugins and plugin management repository context.

When pluginManagement.repositories is empty, settings-plugin queries recreate Gradle's implicit Plugin Portal using the native gradlePluginPortal() repository. Explicit plugin repositories retain their configured order and filters; no fallback is appended to them. Consumer settings files are not modified.

Dependency and component-selection rules are preserved in their original order. Visible rule classes with only final scalar captures can cross the configuration-cache boundary directly. Complex or opaque captures use a generic fallback: an invocation-local Gradle build service retains the original rule object and executes it under its owning project's state boundary (settings rules retain their original settings context). Analysis models carry service handles instead of captured extensions or projects. No rules are dropped, sampled, or approximated, and candidate and full-graph checks are unchanged. Original rule state is released at the end of the invocation.

When this fallback is needed, Gradle 9.7+ disables configuration-cache storage and reuse for that invocation through its native degradation mechanism, even with --configuration-cache-problems=warn. Gradle's diagnostic identifies the affected tasks. Other invocations that do not need the fallback remain cache-compatible. On older supported Gradle versions, run with --no-configuration-cache when DepLens reports a stateful rule; DepLens fails with that instruction instead of risking a cached check without its original rules.

Independent uncached + listings are prefetched in groups of up to 32 modules sharing identical effective repository settings. Gradle can schedule metadata work for these modules together; DepLens does not access Gradle configurations from its own worker threads. Each module still records its full candidate walk, and its consumer-specific filters, selectors and graph checks remain separate. Empty or ambiguous batch results fall back to the original individual query path. HTTP 429 cooldown and retry handling also covers batches. This optimization applies to both fast and full checks; full consumer graphs continue to resolve separately. --info reports batch counts and the number of prefetched module listings. Actual concurrency and speed depend on Gradle's scheduler, repository behavior and cached metadata.

The console counts recommended updates separately from available only versions. Only recommendations use the current → recommended format. Available-only rows show the current version, discovered candidate, build-accepted version (when known), and why an explicit change is required. A newer available candidate above a recommendation is labelled separately. The interactive selector likewise labels Recommended and Available; an available choice still requires your explicit selection.

In JSON, UPDATE_AVAILABLE means that newer policy-accepted candidates exist. A non-null recommendedVersion is a recommendation when neither FAILED nor NOT_CHECKED is present; a null value means available only. failIfUpdatesFound includes available-only updates.

NO_MATCHING_CANDIDATES means version queries succeeded, but the prerelease/ignore policy accepted no versions. Each affected updateContexts item has noMatchingCandidates: true. This successful result is distinct from UP_TO_DATE, FAILED, and NOT_CHECKED, and does not make checksComplete false. It can coexist with updates in another configuration. An accepted current version with no newer allowed update still means UP_TO_DATE. The field is part of JSON schema version 1.

The available list records repository metadata, while latestAllowedByBuild verifies a selected graph. A listed version is not a guarantee of source/binary compatibility or successful compilation. Platform/strict-version/selection restrictions remain visible as CONSTRAINED, alongside UPDATE_AVAILABLE when a deliberate version or platform change could expose an update. Constrained versions above the build-accepted version are not recommended automatically.

A newer dependency or plugin can introduce transitive version requirements that conflict with existing strict constraints. When Gradle identifies a version rejection, DepLens first verifies the complete graph with the installed target version. If that baseline resolves, it tries older candidates while preserving peer dependencies, constraints and resolution rules. The newest release remains visible as latestAvailable; latestAllowedByBuild may be an older release or the installed version. An existing baseline conflict, missing module, repository failure or broken metadata remains a failed check. Unresolved version rejections are reported as version rejected by constraints or selection rules, without raw exception messages.

Reconstructed consumer configurations retain their dependency-resolution rules after graph resolution. This preserves Spring's captured effective version management when a baseline is refreshed and subsequently copied for an update query. Candidate-retry templates also retain their rules before resolution because Gradle does not inherit the retention flag when copying a configuration.

Within a check invocation, identical version queries and successful consumer-graph resolutions are reused. Graph reuse requires matching repositories, attribute schema, dependencies, constraints, attributes, exclusions, forced and managed versions, and update-selection inputs. Configurations with arbitrary dependency or component-selection rules are resolved separately. Known permanent graph failures (missing modules or incompatible variants) are also reused for identical contexts, replaying every unresolved edge into each consumer's diagnostics. A failure is reusable only when every terminal cause is recognized as permanent; HTTP, network, cancellation and unknown failures are not cached. Each consumer retains its own report context. No query results are reused across invocations. --info reports graph request/reuse counts, reused permanent failures and total graph-resolution time.

Identical repository/schema contexts also share Gradle's metadata session. Candidate listings share a repository-only cache because their reject-all walk runs before variant matching. On HTTP 429, DepLens pauses further uncached queries and retries the affected query in a fresh Gradle session, with at most three attempts. Structured Retry-After values support delay-seconds and HTTP dates. Some Gradle versions discard HTTP response headers when creating their exceptions; when no valid retry value survives, DepLens explicitly reports that limitation and uses 30/60/120-second exponential backoff. The cooldown also applies to later queries, and interrupted waits cancel the retry. This handles DepLens's execution-time queries, not repository traffic during earlier Gradle build configuration. Failed or rate-limited partial version listings are never cached as successful results.

When a consumer graph cannot be resolved, update rows identify the blocking module and a credential-safe reason. Variant failures include Java-version and artifact-format mismatches when Gradle exposes them. Repository failures include HTTP status codes when exposed by Gradle, for example repository access failed (HTTP 429: rate limit). JSON resolutionFailures and --info also identify the affected configuration. Raw exception messages and repository URLs are not copied into these explanations; failures without an unresolved module retain a generic diagnostic hint.

Console update rows use short error references such as (build context blocked: E1). The shared Errors section prints each reason once per build with its blocking dependencies and affected project/configuration contexts. Full per-entry details remain available in JSON and --info.

Current-inventory failures also retain structured resolution diagnostics, so a failed baseline with no update candidates still appears in Errors. Gradle's missing-repository diagnostic is rendered as no repositories configured for this classpath. Refreshing a context replaces its earlier diagnostics, and a failed version listing with an existing structured cause does not produce an additional generic resolution failed entry.

If the highest candidate accepted by the version policy or allowed by the build context ranks below the current version, the normal console output includes a Version selection warnings section even when that dependency has no update row. Compact rows show coordinate current > query result (version policy) or (build context), with bold versions. In standard console mode, the same indented gray project paths follow both update rows and warnings. Identical warnings are grouped within each build across projects and configurations. Selection rules and version ordering can both explain this condition; it is a warning, not an automatic query failure, and never recommends a downgrade. Per-configuration diagnostics remain in JSON entry warnings and --info.

When the newest release has incompatible consumer attributes (for example, a higher minimum Java version), DepLens checks older candidates with the same build constraints and recommends the newest compatible version. The newer release remains visible as latestAvailable; an expected incompatibility does not count as a repository or update-query failure. The fallback skips a version already proven incompatible by the same query, using Gradle's structured target component rather than assuming that + selected the first listed version. If Gradle does not expose that version, the conservative retry remains unchanged.

PINNED entries have no update candidates and are omitted from normal check output, while remaining in JSON for auditing. IGNORED removes only the specified versions; lower candidates remain available. Local convention plugins and entries with no resolved baseline remain NOT_CHECKED. Metadata/access/selection errors record FAILED and NOT_CHECKED, with sanitized warnings. The summary reports failed and unchecked counts, never an unconditional “everything is up to date”. complete describes inventory/resolution completeness; checksComplete additionally requires complete coverage of the requested component kinds.

Gradle releases come from https://services.gradle.org/versions/all, using the wrapper version as the baseline when present, with the running version retained in the inventory. Stable versions are the default; includePreReleases also permits release candidates and nightlies. Offline mode skips the remote Gradle feed and reports NOT_CHECKED. gradle.versionsUrl can point to an alternative HTTPS release feed or a local file: JSON feed with the same schema.

Interactive updates#

Apply DepLens to the root project, then run from its root directory in a macOS/Linux terminal:

Terminal
./gradlew depLensInteractiveUpdate

DepLens detects the invoking terminal automatically, reads arrow keys directly and pauses Gradle's console rendering during selection. No environment variable, input redirection or console flag is needed. macOS and Linux terminals are supported; Windows terminals are not supported by this console implementation. Headless invocation fails without changing sources; use depLensCheck for unattended discovery. DEPLENS_TTY provides an explicit terminal-device override. On macOS, terminal discovery uses the system ps and lsof tools; Linux uses /proc.

The task checks all component kinds, including separate builds, then opens the selection screen. Every row starts at Current; nothing is preselected for updating. failIfUpdatesFound and failIfChecksIncomplete apply to check tasks, not this interactive task.

Key Action
↑ / ↓ Move between version sources
← / → Select a version for the row
c Current for all rows
r Highest common candidate accepted by all checked build contexts (Range); in fast mode, highest common policy-allowed candidate without graph validation
l Latest common candidate permitted by all consumers' update rules
Enter Apply selected changes
Esc / Ctrl+C / Ctrl+D Cancel without editing

Interactive choices match the check output: the update target and the latest eligible patch/minor alternatives, plus Current. Intermediate repository versions are not offered. Duplicate versions appear only once. One circle per row is marked. Each version source occupies one row, with aligned columns for Current and available candidates. Long lists scroll around the focused row and selected version. The focused row shows its source relative to the owning build, selected/recommended/latest versions, consumer coordinates, shared aliases and relevant warnings or platforms. Empty fields are omitted. Full configuration, resolution, rule and bundle details remain available through --info and JSON. A compact check summary is printed before the screen.

Shared catalog version keys, properties and variables appear once per physical value. Candidate choices must be common to every checked consumer and configuration. A pinned, failed or unchecked consumer protects the whole shared source from editing. All catalog aliases using a shared key are listed, including aliases not used by this build. Platform/BOM versions remain separate choices; choosing a platform never inserts versions into its versionless managed dependencies.

Dynamic selectors (1.+, ranges, latest.release) retain Current for all bulk actions. Arrow keys can explicitly choose an option labelled FIXED / selector change; Enter then replaces that selector with the selected fixed version. Candidates outside the selector stay visible. Range does not recommend a silent conversion.

Edits replace only located literal version values in catalog files (including rich-version strictly/require/prefer fields), properties, literal variables, Groovy/Kotlin dependency and plugin declarations, settings and wrapper distribution URLs. Other fields, comments and line endings are preserved. Computed values, generated sources, ambiguous same-line repetitions and unsupported syntax remain read-only with a notice. When a selected wrapper has distributionSha256Sum, DepLens automatically replaces it with the checksum for the exact selected bin or all distribution. Custom distribution URLs and surrounding formatting remain intact; wrappers without a checksum do not gain one.

The task compares every selected file with its selection-time snapshot and checks for overlapping edits before writing any file, so a concurrent edit cancels applying. After applying, build/reports/deplens/depLensInteractiveUpdate.json retains the discovery results and records each changed physical source in selectedSources and selectedVersion where a consumer has one consistent selection. No build, tests, compatibility validation or automatic rollback run after applying. An I/O failure during writing identifies the failed file (which may be partially written) and files already changed; review them manually.

Wrapper distribution checksums#

For a selected wrapper with an existing distributionSha256Sum, DepLens reads <selected-distribution-url>.sha256, as described in the Gradle wrapper checksum documentation. The selected version and existing bin/all type determine the exact sidecar. Custom mirrors use their own sidecar; there is no fallback to an unrelated checksum from the public Gradle service. Only HTTPS and local file: metadata are supported; HTTPS redirects must remain HTTPS. Checksums must be a single 64-character hexadecimal SHA-256 value (surrounding whitespace is accepted).

All selected checksum metadata is resolved and validated before any selected sources are written, including selections across included builds. Missing or invalid metadata, ambiguous wrapper properties, offline remote requests or a source changed during selection prevent the entire selection from being applied. Errors omit URL credentials and transport details. Existing comments, whitespace, line endings and the distribution type remain intact. A cancelled or Current selection performs no checksum request. Wrappers without a checksum field do not gain one and do not require checksum metadata.

--offline permits local checksum sidecars but does not download remote ones. Inventory and check tasks remain read-only and do not fetch distribution checksums. Only the distribution URL and its checksum are edited; wrapper scripts and the wrapper JAR are not regenerated.

Supported configuration#

Configure depLens in the root project's build.gradle or build.gradle.kts, including when the plugin is applied through com.sneedit.deplens.settings. The following options configure DepLens 0.1.0.

Build scope Default Meaning
checkIncludedBuilds true Check Gradle included builds declared with includeBuild(...). A build-logic directory participates when it is included by Gradle; the directory name alone does not create a build.
checkBuildSrc true Check buildSrc independently of included builds.

The current build and its subprojects are always included. Scope options also apply to inventory and interactive discovery. Gradle can still load or build excluded build logic to configure the consuming build.

Check scope and output filters#

checkResolvableConfigurationFilter limits analysis before inventory collection and update discovery. outputDeclaredConfigurationFilter filters the check console, JSON view and interactive selection after all checks finish. Shared version sources still respect every consumer, including hidden consumers, when validating updates.

Filter Matches Effect
checkResolvableConfigurationFilter Resolvable configuration names, e.g. testRuntimeClasspath Skips analysis of rejected configurations.
outputDeclaredConfigurationFilter Declaration configuration names, e.g. testImplementation or runtimeOnly Hides report entries and interactive sources after checking them.

Both have includes and excludes lists of Java regular expressions matched against the whole configuration name. Empty includes accept every name; exclusions take precedence. Both lists default to empty, preserving the unfiltered behavior. Invalid patterns fail configuration with the filter and field name. These declarative settings support the Configuration Cache and Isolated Projects; they do not accept closures or live Gradle objects. Gradle 8 uses a calling-thread model adapter to avoid lifecycle-lock deadlocks in isolated composites; see Configuration Cache and Isolated Projects.

Groovy:

Groovy
depLens {
    checkResolvableConfigurationFilter {
        includes = ['compileClasspath', 'runtimeClasspath', 'test.*Classpath']
        excludes = ['testRuntimeClasspath']
    }
    outputDeclaredConfigurationFilter {
        excludes = ['jacocoAnt', 'kotlinCompilerPluginClasspath.+']
    }
}

Kotlin:

Kotlin
depLens {
    checkResolvableConfigurationFilter {
        includes.set(listOf("compileClasspath", "runtimeClasspath", "test.*Classpath"))
        excludes.set(listOf("testRuntimeClasspath"))
    }
    outputDeclaredConfigurationFilter {
        excludes.set(listOf("jacocoAnt", "kotlinCompilerPluginClasspath.+"))
    }
}

To check everything while hiding test and runtimeOnly declarations, leave checkResolvableConfigurationFilter unset and configure only the output filter:

Groovy
depLens {
    outputDeclaredConfigurationFilter {
        excludes = ['(?i).*test.*', 'runtimeOnly']
    }
}
Kotlin
depLens {
    outputDeclaredConfigurationFilter {
        excludes.set(listOf("(?i).*test.*", "runtimeOnly"))
    }
}

A dependency also declared in an accepted configuration, such as implementation, remains visible. runtimeOnly declarations are checked through runtimeClasspath; the two names refer to different stages and are intentionally matched by different filters.

Analysis scope: checkResolvableConfigurationFilter selects resolvable project configurations by their actual names. DepLens does not resolve excluded configurations or run their version queries. When a filter is active, declarations reachable only through excluded configurations are omitted, including otherwise orphaned declarations. Declarations and constraints inherited by a selected configuration remain part of its inventory and complete consumer graph, even when their declaring configuration's name does not match the filter. Rejecting one context does not remove a dependency still reached from another selected context. Intentionally excluded contexts do not make the selected inventory incomplete. --info lists skipped configurations. Gradle or another plugin can still resolve a configuration while setting up the build; this filter controls DepLens analysis, not Gradle's build configuration.

Root analysis-filter lists are inherited by subprojects, included builds and buildSrc. An explicitly configured list in a project's own DepLens extension replaces that inherited list; an explicit empty list resets it. Build-scope switches still decide which builds participate. The filter applies to inventory, update checks and interactive discovery.

Report view: outputDeclaredConfigurationFilter matches declaration configuration names such as implementation or jacocoAnt, rather than their consuming runtimeClasspath. Generated entries without declarations use their recorded configuration names. An entry is hidden only when none of its origins passes the filter; an entry without any origins stays visible. All origins of a retained entry remain available in JSON. The requesting task's report filter also applies to merged entries from subprojects and separate builds.

The report filter does not skip resolution, version queries or graph checks, and does not change failIfUpdatesFound. Sources belonging only to hidden entries are omitted from interactive selection. Shared sources remain subject to all consumers, including hidden ones; their pins, failures and allowed candidates still protect the shared value. Hidden peer dependencies, constraints and BOMs still affect recommendations. Console update counts describe visible rows. JSON hiddenEntries and hiddenFailedEntries, and a console notice, identify hidden results. complete and checksComplete still describe the full analyzed scope; warnings are retained, so hiding an entry cannot make a failed check appear complete. Interactive reports apply the same view again after selections are written.

Both filters leave Gradle plugins, the wrapper, and buildscript/settings classpaths unchanged. Their versions and constraints remain checked independently of project configuration names. Use checkResolvableConfigurationFilter to avoid analyzing a tool graph; use outputDeclaredConfigurationFilter when the graph must still be checked but its entries should be omitted from reports and interactive selection.

Console presentation#

consoleMode defaults to 'compact', which hides the indented Projects: lines below update rows and version-selection warnings. Use --console-mode standard to show these lines. Update discovery, grouping, summaries and JSON reports retain all project and configuration contexts. Error diagnostics and the interactive selection screen keep their context information.

For one invocation, use --console-mode standard; this overrides the configured default and is supported directly by depLensCheck:

Terminal
./gradlew depLensCheck --console-mode standard

To keep project lines visible by default, configure Groovy:

Groovy
depLens { consoleMode = 'standard' }

Or in Kotlin:

Kotlin
depLens { consoleMode.set("standard") }

--console-mode compact hides the project lines for one invocation even when the configured mode is standard. Only standard and compact are accepted. This option is independent of Gradle's --console=plain/--console=rich setting.

Update policy defaults#

The following options are available separately in dependencies and plugins. Dependency policies also apply to platforms/BOMs and constraints; plugin keys are plugin IDs.

Option Default Meaning
includePreReleases false Allow prerelease update candidates.
allowedUpdates 'major' 'patch': patches only; 'minor': patches and minors within the current major; 'major': all update tiers.
failIfUpdatesFound false Fail a check after writing its report when this section has updates.
pinCurrentVersions [] Coordinates (group:artifact) or plugin IDs to pin; omit their update candidates and normal output rows.
ignoredVersions [:] / empty map Coordinates or plugin IDs mapped to lists of exact versions to ignore.

The gradle section supports:

Option Default Meaning
includePreReleases false Include prerelease Gradle versions.
allowedUpdates 'major' 'patch', 'minor' or 'major', with the same meaning as for dependencies and plugins.
failIfUpdatesFound false Fail a check after writing its report if Gradle updates are available.
pinCurrentVersion false Pin the wrapper version.
versionsUrl https://services.gradle.org/versions/all Gradle release feed: HTTPS URL or local file: JSON feed.

Groovy DSL:

Groovy
depLens {
    checkIncludedBuilds = true
    checkBuildSrc = false
    dependencies {
        includePreReleases = false
        allowedUpdates = 'minor'
        failIfUpdatesFound = false
        pinCurrentVersions = ['org.example:legacy']
        ignoredVersions = ['org.example:library': ['2.5.0']]
    }
    plugins {
        includePreReleases = false
        allowedUpdates = 'minor'
        failIfUpdatesFound = false
        pinCurrentVersions = ['org.example.plugin']
        ignoredVersions = ['org.example.plugin': ['2.0.0']]
    }
    gradle {
        includePreReleases = false
        allowedUpdates = 'minor'
        failIfUpdatesFound = false
        pinCurrentVersion = false
        versionsUrl = 'https://services.gradle.org/versions/all'
    }
}

Kotlin DSL uses the same sections and names with Gradle properties:

Kotlin
depLens {
    checkIncludedBuilds.set(true)
    checkBuildSrc.set(false)
    dependencies {
        includePreReleases.set(false)
        allowedUpdates.set("minor")
        failIfUpdatesFound.set(false)
        pinCurrentVersions.set(listOf("org.example:legacy"))
        ignoredVersions.set(mapOf("org.example:library" to listOf("2.5.0")))
    }
    plugins {
        includePreReleases.set(false)
        allowedUpdates.set("minor")
        failIfUpdatesFound.set(false)
        pinCurrentVersions.set(listOf("org.example.plugin"))
        ignoredVersions.set(mapOf("org.example.plugin" to listOf("2.0.0")))
    }
    gradle {
        includePreReleases.set(false)
        allowedUpdates.set("minor")
        failIfUpdatesFound.set(false)
        pinCurrentVersion.set(false)
        versionsUrl.set("https://services.gradle.org/versions/all")
    }
}

Console output is text; each task also writes its JSON report to build/reports/deplens/. Use --info for per-project and per-configuration details. Configure repositories, credentials and content filters through Gradle's normal repositories, pluginManagement.repositories and buildscript repository declarations.

Version ordering and date versions#

Dependency and plugin versions are ordered by the running Gradle runtime's VersionParser and DefaultVersionComparator, isolated in integrations.gradle.GradleVersionAdapter. Wrapper releases retain Gradle's wrapper-specific GradleVersion ordering. Patch/minor tiers compare the first two/first numeric parts parsed by Gradle. Thus SonarQube 7.5.0.8588 -> 7.5.0.9000 is a patch-tier update; the fourth part still participates in ordering.

Without an explicit versionPattern, complete, valid calendar dates in yyyyMMdd, yyyy-MM-dd, yyyy.MM.dd and yyyy_MM_dd form (years 1900–2999) have unknown compatibility, rather than an inferred major/minor/patch tier. For example, org.json:json:20260814 -> 20260901 is displayed with (compatibility unknown: date version). Each JSON update context records these newer versions in unknownCompatibility; they do not populate patch, minor or major. --info and the interactive console also show this classification.

allowedUpdates = 'major' permits all update tiers, including unknown compatibility. 'patch' and 'minor' exclude date-version updates; a specific dependency rule can set 'major' to allow them. Pins, ignored versions, prerelease filtering and build selection constraints continue to apply. Detection does not imply a guarantee of compatibility for other version schemes.

Update rules#

Rules are resolved per configured field: exact dependency/plugin/platform/Gradle rule > group > project > global > section defaults. Unspecified fields inherit, and the last rule at the same specificity wins. allowedUpdates accepts patch, minor (including patches), or major (all updates). Ignore lists contain exact version strings; specifying a list replaces the broader list. A more specific pinned = false can override a broader pin.

Groovy
depLens {
    rules {
        global { includePreReleases = false; allowedUpdates = 'major' }
        project(':app') { allowedUpdates = 'minor' }
        group('org.example') { allowedUpdates = 'patch' }
        dependency('org.example:library') { ignoredVersions = ['2.5.0', '3.0.0'] }
        dependency('org.example:legacy') { pinned = true }
        platform('org.example:dependencies-bom') { allowedUpdates = 'minor' }
        plugin('org.example.plugin') { pinned = true }
        gradle { allowedUpdates = 'minor' }
    }
}

Kotlin DSL:

Kotlin
depLens {
    rules {
        global { includePreReleases.set(false) }
        dependency("org.example:library") {
            allowedUpdates.set("minor")
            ignoredVersions.set(listOf("2.5.0"))
        }
        plugin("org.example.plugin") { pinned.set(true) }
    }
}

Root rules are inherited by subprojects and separate builds; their own rules can override fields at the same or greater specificity. Project keys are Gradle paths within each build (:, :app, or <settings>). A group key is an exact Maven group. Dependency rules also cover matching constraint entries; platform rules apply to platform entries.

Each rule supports pinned, includePreReleases, allowedUpdates, ignoredVersions and ignoredVersionPatterns; fields are unset by default and inherit the broader policy. Section defaults are documented above. Use pinned to protect a version source from updates. Inventory does not run update policies. Interactive updates use discovery rules but do not use failIfUpdatesFound as an exit policy.

Independent version exclusions#

ignoredVersionPatterns is an optional list of Java regular expressions available in every rule scope: global, project, group, dependency, platform, plugin and Gradle. A version is ignored if any expression matches with find(), or if it appears in ignoredVersions. Use ^ and $ to match a whole version and (?i) for case-insensitive matching. No versions are excluded by pattern by default.

Patterns inspect the original version string before normalization. They remain active with includePreReleases = true and do not replace or change prerelease detection. Invalid expressions fail configuration. Like exact ignore lists, a more specific list replaces the inherited list; an explicit empty list clears inherited patterns without clearing exact ignored versions.

Groovy
depLens {
    rules {
        global { ignoredVersionPatterns = [/(?i)-redhat(?:[.-]|$)/] }
        dependency('org.example:library') {
            includePreReleases = true // Beta/RC versions remain eligible, except excluded families.
        }
        plugin('org.example.plugin') { ignoredVersionPatterns = [] }
    }
}

Kotlin DSL:

Kotlin
depLens {
    rules {
        global { ignoredVersionPatterns.set(listOf("(?i)-redhat(?:[.-]|$)")) }
        dependency("org.example:library") { includePreReleases.set(true) }
        plugin("org.example.plugin") { ignoredVersionPatterns.set(emptyList()) }
    }
}

Exclusions apply to update candidates, selector queries and build-context checks, including wrapper release candidates. The installed module version remains resolvable as the baseline even if it matches an exclusion; it is never proposed as an update. Entries with matching excluded candidates receive the IGNORED status. Inventory discovery is unchanged, and interactive updates use the filtered candidates.

Version normalization#

Exact dependency('group:artifact') and plugin('plugin.id') rules can configure versionNormalizationPattern and versionNormalizationReplacement together. The first is a Java regex; the second uses Java replacement syntax ($1, ${name}, or literal text). Both fields are optional and inherited as a pair. Unsupported scopes, invalid regexes and invalid replacement references fail configuration.

Normalization uses replaceAll to create comparison keys. Nonmatching versions retain their original keys. Empty normalized keys fail the query. Gradle compares these keys for candidate ordering, newer-version detection, recommendation limits, report alternatives and interactive selection. Original versions remain unchanged in repository requests, source edits and reports. Versions with equal normalized keys compare as equivalent. Explicit versionPattern, prerelease detection and ignored-version lists still inspect original versions; automatic tier classification uses normalized keys.

For ApacheDS's milestone naming change:

Groovy
depLens {
    rules {
        dependency('org.apache.directory.server:apacheds-core-api') {
            versionNormalizationPattern = /[.]AM(?=[0-9]+$)/
            versionNormalizationReplacement = '-M'
            preReleasePattern = /(?i)[.-](?:AM|M)[0-9]+$/
            includePreReleases = true
        }
    }
}

The comparison keys are 2.0.0-M24, 2.0.0-M25, 2.0.0-M26, 2.0.0-M27, so the original versions are ordered as 2.0.0-M24 < 2.0.0.AM25 < 2.0.0.AM26 < 2.0.0.AM27. Milestones require the explicit prerelease opt-in shown above.

Kotlin DSL uses the same properties:

Kotlin
depLens {
    rules {
        dependency("org.apache.directory.server:apacheds-core-api") {
            versionNormalizationPattern.set("""[.]AM(?=[0-9]+$)""")
            versionNormalizationReplacement.set("-M")
            preReleasePattern.set("""(?i)[.-](?:AM|M)[0-9]+$""")
            includePreReleases.set(true)
        }
    }
}

For normalized ordering, compatibility checks query original candidates in that order rather than trusting Gradle's unnormalized + selection. Consumer attributes, policies, peer dependencies and graph errors remain active. Normalization does not rewrite strict selectors or forced versions; those retain Gradle's selection. JSON entries retain the effective normalization pattern/replacement for report and interactive ordering; --info shows the mapping. Query-cache identities include normalization wherever it can affect a result.

Explicit version and prerelease patterns#

dependency('group:artifact') and plugin('plugin.id') rules additionally support these optional string properties. They are unset by default; automatic classification and prerelease detection apply when omitted. They are not supported on global, project, group, platform or Gradle rules, or in the section defaults.

Rule field Meaning
versionPattern Java regex matched against the whole current and candidate version. Requires named groups major, minor and patch, each capturing a nonnegative integer. Overrides automatic tier classification, including calendar-date detection.
preReleasePattern Java regex searched within each candidate with find(). Replaces automatic prerelease detection completely: a match is prerelease; no match is stable for DepLens. Use (?i) for case-insensitive matching.

Groovy DSL uses strings, including slashy strings (without the ~ Pattern operator):

Groovy
depLens {
    rules {
        plugin('org.sonarqube') {
            versionPattern = /^(?<major>[0-9]+)[.](?<minor>[0-9]+)[.](?<patch>[0-9]+)[.][0-9]+$/
            preReleasePattern = /(?i)(?:alpha|beta|rc|eap)[0-9]*$/
        }
        dependency('org.json:json') {
            versionPattern = /^(?<major>[0-9]{4})(?<minor>[0-9]{2})(?<patch>[0-9]{2})$/
            allowedUpdates = 'minor'
        }
    }
}

The org.json example deliberately maps year/month/day to major/minor/patch: a new day is patch, a new month is minor, a new year is major. This is a user-defined update policy, not a compatibility guarantee. In the SonarQube example the fourth number still determines ordering, and changes in that number remain patch-tier updates.

Kotlin DSL uses the same string patterns:

Kotlin
depLens {
    rules {
        dependency("org.json:json") {
            versionPattern.set("""^(?<major>[0-9]{4})(?<minor>[0-9]{2})(?<patch>[0-9]{2})$""")
        }
        plugin("org.sonarqube") {
            preReleasePattern.set("""(?i)(?:alpha|beta|rc|eap)[0-9]*$""")
        }
    }
}

Both versions must match versionPattern and provide all three numeric captures. Otherwise the tier is unknown, with no fallback to the automatic classifier. allowedUpdates = 'major' permits unknown tiers; 'minor' and 'patch' exclude them. Matching major/minor captures define a patch-tier update even if only an uncaptured fourth segment changes. Without version normalization, Gradle's comparator determines whether a candidate is newer. versionPattern and preReleasePattern alone do not change ordering or dependency selectors.

preReleasePattern applies to both candidate listing and Gradle resolution checks. includePreReleases = true still permits detected prereleases. A custom expression that only recognizes eap treats rc as stable; include all markers relevant to the library. (?!) explicitly disables prerelease detection for that component. Without a pattern, the built-in detector recognizes alpha, beta, rc, cr, m, milestone, preview, snapshot, dev, ea and nightly, case-insensitively.

Patterns inherit from root rules into subprojects and separate builds. Later rules for the same component override only explicitly set fields; changing allowedUpdates does not discard its patterns. Malformed regexes or missing required named groups fail configuration. Nonmatching versions, absent optional captures and nonnumeric captures yield unknown compatibility. JSON update contexts record the effective versionPattern, preReleasePattern and unknownCompatibility; --info shows the patterns and mismatch details. Console update rows identify unknown pattern matches with (compatibility unknown: version pattern mismatch).

A build that cannot configure at all can still fail before a report exists. Plugin resolution that replaces a marker with an opaque/custom module mapping may remain unchecked or fail visibly rather than guessing a plugin release.

Configuration Cache and Isolated Projects#

See the isolated composite example for a reproducible multi-project build with buildSrc, two included builds, and commands to verify cache reuse and byte-for-byte identical JSON reports.

DepLens supports Configuration Cache and Isolated Projects natively in the running Gradle build. It does not launch another Gradle process, disable either feature, or generate analysis init scripts. Project and settings entry points use the same analysis tasks and reports.

On Gradle 8.x, DepLens runs its intermediate tooling-model requests on the calling thread. This avoids a lifecycle-lock deadlock during task-graph discovery for isolated composite builds, while retaining Gradle's native model boundaries, dependency tracking and configuration-cache reuse. Gradle 9 uses its standard model provider. This adaptation does not disable parallel project check tasks or change the build's global parallelism settings.

CI runs every read-only task (depLensInventory, depLensCheckDependencies, depLensCheckPlugins, depLensCheckGradle, and depLensCheck) twice with strict Configuration Cache checking, both with and without project isolation. Composite fixtures cover subprojects, buildSrc, and included builds; the second run must reuse the cache and reproduce the complete JSON report byte-for-byte. Plugin and wrapper checks use local marker modules and a release feed so that actual update recommendations are verified too.

depLensInteractiveUpdate is tested in a real POSIX terminal in both cache modes, including project isolation and composite build contexts. Tests cancel twice and compare the JSON reports, apply a catalog update on a cache hit, and verify that changing the catalog invalidates the cached configuration before the next run.

Terminal
./gradlew :depLensCheck --configuration-cache
./gradlew :depLensCheck --configuration-cache # reuses configuration; checks updates again
# Gradle 9.7+:
./gradlew :depLensCheck --isolated-projects
# Gradle 8.8–9.6:
./gradlew :depLensCheck --configuration-cache -Dorg.gradle.unsafe.isolated-projects=true

Gradle obtains the inventory and configuration values at each owning project's model boundary, including included builds and buildSrc. Tasks retain detached analysis data rather than live Project, Settings, or Configuration objects. With Configuration Cache disabled, project data is captured before task execution under the owning project's model lock. Project check producers are also registered under that project-state boundary before Gradle schedules the work. Task dependencies are wired from their output providers within the requesting build. Selected foreign models retain their original source and resolution identities when hosted there. Update tasks always run and query candidate versions through Gradle's native dependency resolver, even on a cache hit. The wrapper release feed is also checked again. Gradle's regular offline mode, authentication and dependency metadata caching still apply; use --refresh-dependencies when an immediate remote metadata refresh is needed.

Root rules, build-scope switches, failure policies, concise output, JSON paths and interactive terminal detection are preserved. The native model and resolution adapters use Gradle internal services where the public API has no equivalent; compatibility tests cover the supported Gradle versions. Repository filters (including configuration restrictions), Ivy layouts, attribute matching rules, component-selection and eachDependency rules, rich constraints and constraints exported by project dependencies are captured for native queries. Other plugins and build scripts must also support Configuration Cache and Isolated Projects. Repository-specific custom metadata suppliers and version-listing rules produce an explicit unsupported-rule error rather than silently changing their behavior.

Nullness contracts#

Production packages use JSpecify @NullMarked: parameters, fields, return values, array elements and generic type arguments are non-null unless explicitly marked @Nullable. Every subpackage has its own package-info.java. Nullable contracts cover absent versions/sources, optional update policies, optional credentials and reflection values. Required analysis snapshots are initialized in constructors; required reflection/deserialization results are checked at their boundaries.

NullAway runs in JSpecify mode during :plugin:compileJava. Nullness violations and new classes without an explicit null-marking scope fail compilation, including in CI. To run just the production analysis locally:

Terminal
./gradlew :plugin:nullAway

The compiler toolchain needs JDK 21.0.8 or newer for reading type-use annotations from dependencies. JSpecify is an explicit API dependency; Error Prone and NullAway are build-time dependencies and are not published as plugin runtime dependencies. Test fixtures deliberately exercise missing values and mocks, so their compilation is excluded from NullAway. This does not suppress checks in production code. Other compatibility warnings (for example reflective unchecked casts) are separate from nullness checking.