Behavior and configuration reference for DepLens 0.1.0.
Inventory#
./gradlew depLensInventoryThe 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-logicwhen declared withincludeBuild) andbuildSrc, 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:
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#
./gradlew depLensCheck
# Or check one component kind:
./gradlew depLensCheckDependencies
./gradlew depLensCheckPlugins
./gradlew depLensCheckGradledepLensCheck 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:
depLens {
failIfChecksIncomplete = true
}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:
./gradlew depLensCheck --fast
./gradlew depLensCheckDependencies --fastAlternatively, configure the default in Groovy:
depLens {
checkBuildContext = false
}Or in 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.candidatesapplies 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; theirrecommendedVersionstays 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:
./gradlew depLensInteractiveUpdateDepLens 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:
depLens {
checkResolvableConfigurationFilter {
includes = ['compileClasspath', 'runtimeClasspath', 'test.*Classpath']
excludes = ['testRuntimeClasspath']
}
outputDeclaredConfigurationFilter {
excludes = ['jacocoAnt', 'kotlinCompilerPluginClasspath.+']
}
}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:
depLens {
outputDeclaredConfigurationFilter {
excludes = ['(?i).*test.*', 'runtimeOnly']
}
}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:
./gradlew depLensCheck --console-mode standardTo keep project lines visible by default, configure Groovy:
depLens { consoleMode = 'standard' }Or in 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:
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:
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.
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:
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.
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:
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:
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:
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):
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:
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.
./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=trueGradle 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:
./gradlew :plugin:nullAwayThe 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.
No matching settings. Try a property name, “wrapper”, “filters” or “rules”.