skip to content

Repository Content Filtering

Routing specific groups to specific repositories with content filters and exclusiveContent. Interviewers like it because it both speeds up resolution and closes the door on dependency-confusion attacks.

on this pageshow

questions

5

What is repository content filtering in Gradle, and why would you use it?

level: juniorimportance: must knowfreq 55%

answer

  1. content {} per repository
  2. includeGroup / excludeGroup
  3. prunes the repo search order
  4. fewer 404s = faster
  5. exclusiveContent = stronger pin

basics

~10 s

It tells Gradle which repositories can or cannot supply which modules, using a content {} block with includeGroup/excludeGroup. This avoids pointless lookups and speeds up resolution.

solid answer

~40 s

Repository content filtering lets you declare, per repository, which dependency groups/modules that repository may provide. You add a `content {}` block under a repository in the `repositories {}` section and call rules like `includeGroup("com.example")` or `excludeGroup("org.springframework")`. By default Gradle searches every declared repository in order until it finds a module, which means many wasted HTTP requests (and 404s) against repos that will never have it. Filtering prunes that search space: a request for `com.example:lib` only hits the repo declared to hold `com.example`. Benefits are faster resolution, fewer network round-trips, and correctness/security — you can guarantee an internal group is only fetched from your private repo and never accidentally from Maven Central (dependency-confusion protection).

code

kotlin · 9 lines
kotlin
repositories {
    mavenCentral {
        content { excludeGroup("com.acme") }
    }
    maven {
        url = uri("https://repo.mycompany.com/internal")
        content { includeGroup("com.acme") }
    }
}

go deeper

for a junior

Know that content { includeGroup() } tells a repo which groups it may serve, and that it speeds up resolution by cutting wasted lookups.

for a middle

Explain include-vs-exclude semantics, regex variants, and that any include rule switches the repo into allowlist mode.

for a senior

Tie it to dependency-confusion security, centralized declaration in settings, and quantify the network/perf benefit.

for a principal

Frame org-wide policy: pin internal groups exclusively to the private repo via settings dependencyResolutionManagement to enforce supply-chain hygiene across all builds.

## The problem A Gradle build typically declares several repositories: ```kotlin repositories { mavenCentral() maven { url = uri("https://repo.mycompany.com/internal") } } ``` When Gradle resolves `com.acme:internal-lib:1.0`, it does **not** know which repository holds it. It searches them **in declaration order**, issuing HTTP requests (for the POM/`module` metadata, sometimes a `maven-metadata.xml`) until one repo answers. Misses produce 404s that are still real network round-trips. Multiply that across hundreds of dependencies and the wasted lookups add up — and you also expose internal coordinates to public repositories. ## Content filtering Each repository accepts a `content {}` block describing what it is *allowed* to serve. The matching rules: - `includeGroup("com.acme")` — only this exact group may be fetched here. - `includeGroupByRegex("com\\.acme\\..*")` — groups matching the regex. - `includeModule("com.acme", "lib")` / `includeModuleByRegex(...)` — narrower than group. - `excludeGroup("com.acme")`, `excludeModule(...)`, `excludeGroupByRegex(...)` — this repo must NOT be used for these. - `includeVersion(...)` / `excludeVersion(...)` — version-level. Include and exclude rules combine: if any `include*` rule is present, the repo serves **only** what the include rules match; `exclude*` rules then carve holes out of what remains. ```kotlin repositories { mavenCentral { content { excludeGroup("com.acme") } // never look here for internal stuff } maven { url = uri("https://repo.mycompany.com/internal") content { includeGroup("com.acme") } // only internal groups } } ``` ## exclusiveContent `content {}` is *inclusive* per-repo but does not stop a group from also being declared in another repo. `exclusiveContent {}` is stronger: it states a set of coordinates is served **exclusively** by one repository and filtered out of **all others** automatically: ```kotlin exclusiveContent { forRepository { maven { url = uri("https://repo.mycompany.com/internal") } } filter { includeGroup("com.acme") } } ``` Now `com.acme` can come *only* from that repo, and Gradle won't even try the others — concise and confusion-proof. ## Where it can live Filtering can be declared in the build's `repositories {}`, in `settings.gradle(.kts)` under `dependencyResolutionManagement { repositories { ... } }` (centralized), and the same API exists for plugin repositories in `pluginManagement`. ## Why it matters 1. **Speed** — fewer HTTP requests and 404s; resolution and `--refresh-dependencies` runs are faster. 2. **Correctness/security** — internal coordinates can be pinned to the private repo, mitigating dependency-confusion attacks where a public repo publishes a malicious same-named artifact. 3. **Determinism** — you remove ambiguity about which repo a module comes from.

  • If you add only an includeGroup rule to a repo, what happens to all other groups for that repo?
    They are excluded — once any include rule exists, the repository serves only what the include rules match; everything else is filtered out of that repo.
  • Does content filtering change which version is selected?
    No. It only narrows which repositories are queried for a module. Version selection (highest, constraints, resolution strategy) is unaffected.

Like routing mail: instead of asking every post office whether they hold your parcel, you label which depot serves which postcode so the courier goes straight there.

saying these in an interview costs you the question

  • Saying it blocks/forbids a dependency outright — it only routes which repo serves it.
  • Claiming includeGroup is additive with the default 'serve everything' — adding an include flips the repo to allowlist mode.

context

open as a page

Explain how includeGroup, includeGroupByRegex, and excludeGroup combine within a repository's content block. What is the precedence?

level: middleimportance: must knowfreq 45%

basics

~10 s

Any include* rule flips the repo to allowlist mode — only matching coordinates are served. exclude* rules then subtract from what remains. With no rules at all, the repo serves everything.

open as a page

How does exclusiveContent {} differ from a per-repository content {} block, and when would you choose it?

level: middleimportance: should knowfreq 35%

basics

~20 s

content {} only describes one repo; you'd still have to exclude the same group everywhere else. exclusiveContent {} declares a group is served by exactly one repo and auto-excludes it from all others in one place.

open as a page

Give a realistic example of using content filtering to separate snapshot/internal modules from a public mirror, and explain how it improves resolution performance.

level: middleimportance: should knowfreq 20%

basics

~10 s

Route internal/snapshot groups to your private repo with includeGroup, and excludeGroup them from the public mirror. Gradle then skips the public repo for those modules, removing dead lookups and 404s, so resolution is faster.

open as a page

Where can repository content filtering be declared, and how does it interact with centralized dependencyResolutionManagement and pluginManagement?

level: seniorimportance: should knowfreq 25%

basics

~10 s

The same content {}/exclusiveContent {} API works in a project's repositories {}, in settings.gradle under dependencyResolutionManagement.repositories {}, and in pluginManagement.repositories {} for plugin resolution. Settings-level filtering applies project-wide.

open as a page