Gradle Plugins, Core Plugins, Community Plugins, Convention Plugins, and Plugin Management: Concepts, Architecture, and Mental Model
Treat Gradle plugins as executable build dependencies: understand where plugin code comes from, how it is resolved and applied, what model elements it contributes, and where reusable convention policy belongs.
Treat Gradle plugins as executable build dependencies: understand where plugin code comes from, how it is resolved and applied, what model elements it contributes, and where reusable convention policy belongs.
Learning objectives
- Distinguish core, community, and repository-owned/local plugins by provenance and version policy.
-
Trace a plugin request through
pluginManagement, marker/module resolution, application, configuration, and contributed model elements. - Explain why plugin repositories are a different trust boundary from ordinary dependency repositories.
- Describe convention plugins and precompiled script plugins as reusable build policy rather than copied root-project scripting.
- Separate plugin application from plugin configuration and explain why public Gradle APIs matter for upgrade compatibility.
- Inspect plugin-provided tasks and build logic before changing the plugin graph.
1. The practical problem: plugins quietly become part of your build engine
Chapter 17 modeled Gradle as a truthful task graph. But many of those tasks, configurations, extensions, and lifecycle rules are not declared by Gradle core itself; they arrive when a plugin is applied. The Java plugin creates compilation and test-related model elements. A community formatter can add formatting tasks. A repository-owned convention plugin can apply both tools and set defaults across many subprojects.
That convenience creates a supply-chain boundary. Plugin code
executes inside the Gradle process with the same filesystem,
network, environment, and CI credential access that Gradle has. A
successful build therefore proves only that the plugin
ran successfully—not that its source, version, repository, or
behavior was trustworthy.
Chapter invariant: every active plugin should have an intentional source, identity/version policy, application scope, and configuration owner. Shared policy should be reviewable as code, not hidden in workstation state.
2. Baseline and terminology before configuration
The mandatory path uses the already verified Gradle
9.7.1 Wrapper, JDK 21 to run Gradle, Java 17 for
the small JVM projects, and an isolated
GRADLE_USER_HOME. One public community plugin—com.diffplug.spotless
8.10.0—is used only as a controlled resolution
example. All reusable Java conventions remain repository-owned
source under build-logic/.
| Term | Meaning | Version/source expectation |
|---|---|---|
| Core plugin |
Plugin distributed with Gradle, such as java,
java-library, or application.
|
Version follows the pinned Gradle distribution; do not add a separate plugin version in normal use. |
| Community plugin | Third-party plugin resolved from a configured plugin repository or mapping. | Pin an exact reviewed version and constrain repositories. |
| Convention plugin | Repository/team plugin that applies/configures other plugins and shared defaults. | Review its source and version/commit like production code. |
| Precompiled script plugin |
A .gradle.kts or .gradle source
file compiled into a normal plugin; its ID derives from the
filename.
|
Useful for conventions; usually stored in
buildSrc or an included build.
|
| Plugin request |
An ID, optional version, and apply intent from a
plugins {} block.
|
Resolved before the plugin can be applied. |
| Plugin application | Runs the plugin logic against a Project or Settings object. | May add tasks/configurations/extensions and callbacks. |
| Plugin configuration | Sets values on model elements supplied by the plugin. | Should be deterministic, lazy where appropriate, and scoped to the intended projects. |
3. Mental model: request → resolution → application → configuration
flowchart TD
S["settings.gradle.kts
pluginManagement"]
R["Plugin request
ID + version"]
M["Plugin repository / included build
marker + implementation"]
C["Plugin code on
build classpath"]
A["Apply to Project
or Settings"]
E["Tasks + extensions
+ configurations"]
G["Configured
task / model graph"]
S --> R
R --> M
M --> C
C --> A
A --> E
E --> G
The settings file defines the plugin-resolution boundary before
project build scripts are evaluated. A community plugin request is
mapped to a plugin marker/implementation from configured plugin
repositories; an included build-logic build can
contribute a local plugin instead. Only after code is available can
Gradle apply it and expose tasks/extensions/configurations for
subsequent configuration.
4. Core, community, and local plugins are different provenance classes
| Plugin class | Example | Where code comes from | What should reviewers prove |
|---|---|---|---|
| Core | java |
Pinned Gradle distribution. | Wrapper/distribution identity and Gradle upgrade compatibility. |
| Community | com.diffplug.spotless |
Configured plugin repository; Plugin Portal in this lab. | Exact plugin ID/version, repository policy, source/release provenance, compatibility. |
| Convention/local | academy.java-conventions |
Reviewed source in included build-logic. |
Commit/source review, public API use, deterministic defaults, no secrets/environment coupling. |
| Published internal binary plugin | e.g. company plugin ID | Internal plugin repository. | Published immutable version, repository controls, source/release provenance; administration belongs to the repository-manager course. |
5. The plugins DSL declares identity; it is not arbitrary script code
A project build normally uses a declarative
plugins {} block. Core plugins need no independent
version because they ship with the Gradle distribution. Non-core
plugin requests need a version unless a default version was
established in plugin management or the plugin is supplied locally
by an included plugin build.
plugins {
java // core plugin from Gradle 9.7.1
id("com.diffplug.spotless") version "8.10.0" // community plugin
}
Pinning the Wrapper and pinning an external plugin solve different problems. The Wrapper selects the Gradle engine; the plugin version selects executable build logic loaded into that engine.
6. pluginManagement is an early settings-level resolution policy
pluginManagement {} belongs in
settings.gradle(.kts) and is evaluated early enough to
define plugin sources, default versions, resolution rules, and
included plugin builds. It does not apply a project plugin merely
because a version is listed.
pluginManagement {
includeBuild("build-logic")
repositories {
gradlePluginPortal()
}
plugins {
id("com.diffplug.spotless") version "8.10.0"
}
}
| Statement | Changes | Does not do |
|---|---|---|
repositories { ... } |
Plugin resolution source set. | Does not configure project dependency repositories. |
plugins { id(...) version ... } |
Default version policy for later requests. | Does not apply the plugin. |
includeBuild("build-logic") |
Makes plugins produced by that included build available to plugin resolution. | Does not automatically apply every plugin from that build. |
resolutionStrategy { ... } |
Can rewrite/map plugin requests when an intentional compatibility policy requires it. | Should not become opaque magic that hides origin/version. |
7. Plugin repositories and dependency repositories are separate trust boundaries
A project can use repositories { mavenCentral() } for
application libraries while
pluginManagement.repositories governs plugin
resolution. Tightening one boundary does not automatically tighten
the other. This distinction matters in CI because plugin code
executes during build configuration and execution, often before
application dependencies are even compiled.
Security boundary: changing plugin repositories, resolution rules, or included plugin builds changes executable build code. Treat those diffs like dependency or CI-runtime changes, not cosmetic build-file edits.
8. Plugin IDs resolve through plugin metadata, not project dependency syntax
For a normal community-plugin request, Gradle uses the plugin
ID/version to locate plugin marker metadata and then the
implementation artifact. The Plugin Portal shows the marker
coordinates for com.diffplug.spotless 8.10.0 and the
dependency form used when a precompiled convention-plugin build
needs that external plugin on its own build-logic classpath.
This is why copying a library repository declaration into
dependencies {} is not a substitute for a correct
plugin-resolution policy.
9. Convention plugins turn repeated policy into named, reviewable build logic
A convention plugin normally applies core or community plugins and
sets organization/project defaults: Java toolchain, compiler
encoding, test policy, dependency constraints, publication defaults,
or task conventions. Gradle’s current guidance favors convention
plugins over broad allprojects {}/subprojects {}
blocks because the policy has a name, scope, implementation, and
independent model boundary.
// build-logic/src/main/kotlin/academy.java-conventions.gradle.kts
plugins {
java
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release.set(17)
}
The filename produces the plugin ID
academy.java-conventions. Applying that ID is clearer
than copying the same Java configuration into every module.
10. buildSrc and an included build solve related problems with different boundaries
buildSrc/ is automatically discovered and is convenient
for small builds/prototypes. An explicit included build such as
build-logic/ is a complete Gradle build with a clearer
independent boundary. Current Gradle build-structure guidance
generally favors an included build for scalable shared build logic,
while acknowledging that buildSrc remains useful in
some cases.
| Choice | Strength | Cost / review question |
|---|---|---|
buildSrc |
Simple; automatically available. | Changes can broadly invalidate the main build; classloader/model behavior is more implicit. |
Included build-logic |
Explicit composite-build boundary; independently buildable; can be split into focused plugin projects. | Requires explicit inclusion and its own build files/repositories. |
| Published binary convention plugin | Can be reused across repositories without rebuilding from source each time. | Requires release/version/repository governance beyond this local chapter. |
11. Application and configuration are separate events
Applying java creates Java-related model elements.
Configuring java { toolchain ... } changes those
elements. A robust convention plugin should either apply the plugin
whose extension/tasks it configures or react to that plugin
deliberately; it should not assume a task/extension exists because
another script “usually” applies it first.
That principle prevents order-sensitive build logic and connects directly to Chapter 16’s lazy configuration model.
12. Plugin compatibility depends on supported public APIs
A plugin compiled against Gradle’s documented public API has a much
better upgrade story than one importing internal implementation
types such as packages containing .internal.. Internal
APIs can change without the compatibility guarantees expected for
public API. During a Gradle upgrade, plugin and extension
compatibility should be tested before changing the fleet-wide
Wrapper.
13. Read-only inspection before adding or upgrading a plugin
Start with identity and model evidence from a trusted repository:
export GRADLE_USER_HOME="$PWD/.lab-gradle-home"
./gradlew --version
./gradlew projects
./gradlew tasks --all
./gradlew buildEnvironment
Then inspect settings.gradle(.kts),
pluginManagement, root/subproject
plugins {} blocks, and repository-owned build logic.
tasks --all proves what tasks are currently visible; it
does not by itself prove which repository supplied a plugin or
whether the plugin is safe.
14. DevOps connection: plugins participate in CI trust before artifacts exist
A CI job may expose source code, repository tokens, signing material, proxy credentials, or cloud environment variables to the Gradle process. A plugin can theoretically access all of that if the process can. Production review therefore asks not only “what task does this plugin add?” but also “who publishes this code, which exact version did we resolve, through which repository policy, and where is its shared configuration reviewed?”
Lesson 2 makes those questions observable in a disposable multi-project build.
Knowledge check
Why does a core java plugin normally omit a
version?
It ships with the pinned Gradle distribution; changing Gradle changes the core-plugin implementation baseline.
Does declaring a default version in
pluginManagement.plugins apply that plugin?
No. It supplies version policy for a later plugin request; application is separate.
Why is repositories { mavenCentral() } not enough
to govern community plugins?
Project dependency repositories and plugin-resolution repositories are separate policy surfaces.
What is the main purpose of a convention plugin?
To package reusable, named build policy so projects apply reviewed logic instead of copying configuration everywhere.
Why are org.gradle.*.internal.* APIs risky in
plugin code?
They are implementation details without the same compatibility contract as documented public APIs and may break on Gradle upgrades.
Why should plugin provenance be reviewed like dependency provenance?
Plugin code executes inside the build process and can affect tasks, files, network access, credentials, and produced artifacts.
15. Bridge to the guided workflow
Next you will create a two-subproject JVM build, centralize a pinned
community-plugin version in settings, put Java policy in an included
build-logic convention plugin, and inspect the exact
tasks/extensions each plugin contributes before running the build.
Official references and version notes
- Gradle 9.7.1 Release Notes — pinned Gradle baseline for this chapter.
- Introduction to Plugins and Working with Plugins — core/community/local plugin sources, plugins DSL, plugin management, and resolution.
- Convention Plugins and Precompiled Script Plugins.
-
Best Practices for Structuring Builds
— convention plugins and the current preference for an included
build-logicbuild over repeated cross-project configuration. - Composite Builds — included plugin builds and plugin resolution.
- PluginManagementSpec — plugin repositories, default plugin versions, resolution strategy, and included plugin builds.
- Task Configuration Avoidance — relevant when convention/plugin code contributes tasks.
- Gradle Plugin Portal: com.diffplug.spotless — controlled community-plugin example; version 8.10.0 was published August 17, 2026 and is configuration-cache compatible according to the Portal.
Version snapshot: Generated August 24, 2026 with
Gradle 9.7.1, JDK 21 as the Gradle runtime, Java 17 as the course
JVM target, and com.diffplug.spotless 8.10.0 as the one
external community-plugin example. Re-check plugin versions and
compatibility before adopting them in production.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.