How do you apply a Settings convention plugin, and what makes its bootstrapping tricky?
answer
- applied with plugins {} in settings
- pluginManagement first, then plugins
- sources: buildSrc / includeBuild / published
- chicken-and-egg: can't self-source repos
- precompiled *.settings.gradle.kts
basics
~20 sApply it via plugins {} in settings.gradle(.kts). The catch: the plugin must already be resolvable when settings is evaluated, so it comes from buildSrc, an included build under pluginManagement, or a published plugin in pluginManagement repositories.
solid answer
~40 sA settings plugin is applied in `settings.gradle(.kts)` using the `plugins { id("...") version "..." }` block (or legacy `apply`). The bootstrapping subtlety is that the plugin has to exist *before* the rest of settings runs, yet `pluginManagement` — which says where plugins come from — is itself part of settings. So a settings convention plugin must be sourced from one of: a **published plugin** in `pluginManagement { repositories {} }`; an **included build** declared via `pluginManagement { includeBuild("build-logic") }`; **buildSrc** (compiled automatically before settings); or a **precompiled settings convention plugin** (`*.settings.gradle.kts` in build-logic). You can also apply settings plugins programmatically/sequentially. Getting the order right — pluginManagement first, then the plugins block — is the part people trip on.
code
kotlin · 8 lines// settings.gradle.kts — build-logic supplies the settings plugin
pluginManagement {
includeBuild("build-logic")
repositories { gradlePluginPortal() }
}
plugins {
id("com.example.conventions.settings") // no version: from build-logic
}go deeper
Know it's applied via plugins {} in settings.gradle(.kts).
List the valid sources (buildSrc, includeBuild, published) and the pluginManagement-before-plugins ordering.
Articulate the bootstrapping chicken-and-egg and why includeBuild/buildSrc are the escape hatches; mention precompiled settings convention plugins.
Design the org bootstrap: publish a settings plugin to a trusted internal repo and standardize its application across repositories without circular sourcing.
## Applying a settings plugin ```kotlin // settings.gradle.kts pluginManagement { repositories { gradlePluginPortal() } } plugins { id("com.example.conventions.settings") version "1.0" } ``` `pluginManagement {}` must come **first**, before `plugins {}`, because the plugins block needs to know where to resolve from. ## Sources a settings plugin can come from 1. **Published plugin** — available in a `pluginManagement` repository (Plugin Portal or internal Nexus). Apply with `id(...) version ...`. 2. **Included build (build-logic)** — `pluginManagement { includeBuild("build-logic") }`, then apply by id without a version. This is the common monorepo pattern; the convention plugin's source lives right in the repo. 3. **buildSrc** — Gradle compiles `buildSrc` automatically before evaluating settings, so a `Plugin<Settings>` defined there is available. (Note: buildSrc affects every build invocation and isn't an included build.) 4. **Precompiled settings convention plugin** — a script named `something.settings.gradle.kts` in a `build-logic` project compiles to a `Plugin<Settings>` you can apply by id. ## The bootstrapping chicken-and-egg The tricky part: the thing that configures plugin resolution (`pluginManagement`) lives inside the same file where you want to *apply* a plugin that does that configuration. You cannot have the settings plugin set up `pluginManagement.repositories` and *also* be resolved from those repositories — that is circular. The escape hatches are exactly buildSrc and `pluginManagement.includeBuild`, which make the plugin available without needing remote repositories first. ## Precompiled settings convention plugin example ```kotlin // build-logic/src/main/kotlin/conventions.settings.gradle.kts dependencyResolutionManagement { repositories { mavenCentral() } } ``` Then in the root `settings.gradle.kts`: ```kotlin pluginManagement { includeBuild("build-logic") } plugins { id("conventions") } ``` ## Why bother All of this exists so that cross-cutting build setup (repos, catalogs, cache, project structure conventions) can be authored once and applied with a single id in many repositories — the standardization payoff of settings plugins.
- Why can't a settings plugin configure the very repositories it is resolved from?That would be circular — Gradle must resolve the plugin before running its code, so the repositories must already be in place via buildSrc or pluginManagement.includeBuild, not set up by the plugin itself.
- What file naming makes a precompiled settings convention plugin?A script named *.settings.gradle.kts in a build-logic project compiles into a Plugin<Settings> applied by the file's base id.
saying these in an interview costs you the question
- Placing the plugins {} block before pluginManagement {} in settings.
- Expecting a settings plugin published to your internal Nexus to also configure that Nexus as its own source — a circular dependency.