Gradle Plugins, Core Plugins, Community Plugins, Convention Plugins, and Plugin Management: Diagnostics, Failure Modes, Security, and Performance
Diagnose plugin-resolution and build-logic failures by preserving source/version evidence, separating plugin repositories from dependency repositories, inspecting task/model changes, and repairing the smallest incorrect policy.
Diagnose plugin-resolution and build-logic failures by preserving source/version evidence, separating plugin repositories from dependency repositories, inspecting task/model changes, and repairing the smallest incorrect policy.
Learning objectives
- Diagnose plugin-resolution failures without confusing plugin repositories with project dependency repositories.
- Interpret a missing/mismatched plugin version using preserved Wrapper, settings, repository, and cache evidence.
- Repair eager convention-plugin task access with plugin application plus lazy configuration.
- Recognize unsupported internal Gradle API coupling as an upgrade blocker.
- Detect inconsistent convention/plugin application across subprojects with task/model evidence.
- Use isolated Gradle User Homes instead of blindly deleting normal user cache state.
1. Diagnostic sequence for plugin and build-logic failures
-
Preserve concise evidence: original error, task
path, repository/plugin ID/version, relevant
--infolines. - Confirm identity: Wrapper distribution/Gradle version and Gradle runtime JVM.
-
Inspect declared/effective plugin policy:
settings
pluginManagement, plugins blocks, included build source. - Inspect project/task model: which projects apply the plugin, which tasks/extensions exist.
-
Inspect repository/cache/filesystem state: plugin
repositories and an isolated fresh
GRADLE_USER_HOMEif cache ambiguity matters. - Inspect plugin/build-logic failure: classpath, public/internal API use, eager task access, plugin compatibility.
- Apply the least destructive correction and rerun under controlled state.
- Verify outputs and policy evidence, not only a zero exit code.
2. Failure: plugin repository or version cannot satisfy the request
Start from the Lesson 2 lab and preserve the good settings file. Then create a deliberately impossible version while keeping the repository itself unchanged:
cp settings.gradle.kts settings.good.gradle.kts
python - <<'PY'
from pathlib import Path
p=Path('settings.gradle.kts')
s=p.read_text()
s=s.replace('version "8.10.0"', 'version "99.99.99"')
p.write_text(s)
PY
export GRADLE_USER_HOME="$PWD/.broken-gradle-home"
set +e
./gradlew :app:spotlessCheck --info --console=plain > broken-plugin.log 2>&1
rc=$?
set -e
printf 'exit=%s
' "$rc"
grep -E 'Plugin|com.diffplug.spotless|99.99.99|not found|could not' broken-plugin.log | head -80 || true
Expected failure: the external plugin request cannot resolve. A fresh isolated User Home prevents a warm normal cache from confusing your investigation. The repair is not “delete Gradle”; it is to restore the reviewed version policy.
mv settings.good.gradle.kts settings.gradle.kts
rm -rf .broken-gradle-home
export GRADLE_USER_HOME="$PWD/.lab-gradle-home"
./gradlew :app:spotlessCheck --console=plain
3. Failure: plugin is resolved from an unintended source
In production, pluginManagement.repositories should be
an intentional allow-list/order, often an approved internal mirror
plus carefully chosen public fallback. If a new repository is
inserted ahead of the expected source, do not trust “same
ID/version” as proof of equivalent provenance. Preserve the settings
diff and resolution evidence, verify the
repository/marker/implementation source, then restore policy.
Do not put real repository credentials in a lesson command or URL. Server credentials belong in protected settings/CI secret mechanisms. Repository-manager administration is handled in the Nexus course.
4. Failure: convention plugin eagerly reaches for a task that is not guaranteed to exist
This deliberately broken convention plugin does not apply Java but
eagerly requests compileJava:
// INTENTIONALLY BROKEN build logic
// academy.broken-conventions.gradle.kts
tasks.getByName("compileJava") {
println("configured eagerly")
}
If a project applies this convention before any Java plugin creates
compileJava, configuration fails with an unknown-task
style error. Even when it happens to work, eager lookup realizes the
task unnecessarily.
Repair by making the convention own the prerequisite plugin and using lazy configuration:
import org.gradle.api.tasks.compile.JavaCompile
plugins {
java
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
}
The repair is architectural: the convention declares what it needs and configures matching tasks lazily. Reordering build files until the eager lookup happens to succeed would preserve the coupling.
5. Failure: plugin uses Gradle internal implementation APIs
Search repository-owned plugin/build logic for imports containing
.internal.:
grep -R -n --include='*.kt' --include='*.java' --include='*.gradle.kts' 'org\.gradle\..*\.internal\.' build-logic || true
An internal API is not automatically malicious, but it is an upgrade-risk signal. During a Gradle upgrade, reproduce the failure under the old and new pinned Wrappers, identify the public replacement API, and treat “works only on internal Gradle class X” as a documented blocker rather than using reflection/classpath hacks.
6. Failure: shared configuration is applied inconsistently
Suppose app applies
academy.java-conventions but
library silently duplicates only half the settings.
Compare task/model evidence:
./gradlew :app:conventionReport :library:conventionReport --console=plain
./gradlew :app:tasks --all > app.tasks.txt
./gradlew :library:tasks --all > library.tasks.txt
grep -E 'conventionReport|compileJava' app.tasks.txt library.tasks.txt
If one project lacks the convention-provided task, ask whether that is intentional. The repair may be to apply the convention, or to document that the module belongs to a different policy family. Do not force every project into one convention merely to make task lists symmetrical.
7. Failure pattern: dynamic or unpinned community-plugin versions
A version selector such as 8.+ makes future resolution
depend on repository metadata rather than a reviewed commit. Even if
the resolver accepts it, CI can change behavior without a repository
diff. Production policy should use an exact version and an explicit
dependency-update review process.
// WRONG production default: behavior can drift without a source change.
plugins {
id("com.diffplug.spotless") version "8.+"
}
8. Use --info/-d/--stacktrace selectively; debug logs are data
--info can help show repository and plugin activity.
--stacktrace helps attribute a configuration/runtime
exception to plugin code. Full debug output can include paths,
environment-sensitive values, HTTP information, or configuration
details. Capture only what you need, protect logs as CI artifacts,
and redact secrets before sharing.
./gradlew :app:spotlessCheck --info --console=plain > plugin-info.log 2>&1
# Add --stacktrace only when diagnosing an exception that needs it.
9. Cache diagnosis: isolate first, delete only disposable state
Plugin artifacts and transformed build logic live under Gradle User
Home caches. A stale/corrupt cache can be real, but deleting
~/.gradle destroys evidence and unrelated state. First
reproduce with a second project-local User Home:
GRADLE_USER_HOME="$PWD/.fresh-plugin-home" ./gradlew :app:spotlessCheck --info --console=plain > fresh-home.log 2>&1
If the clean isolated home succeeds while the normal isolated lab home fails, compare resolver logs/files before deciding whether the disposable lab cache should be removed. Normal user/shared CI caches are not cleanup targets in this exercise.
10. Performance: plugin configuration belongs in the measurement boundary
A plugin can make configuration expensive by eagerly realizing tasks, scanning files, starting processes, or reading environment/files before task execution. Compare configuration time and task realization under controlled commands. Do not “optimize” by removing correctness checks or sharing unsafe caches. Chapter 25 will cover profiling/daemon/workers in depth.
11. Security checklist for executable build logic
- Wrapper/Gradle version is pinned and reviewed.
- Community plugin IDs and versions are exact.
- Plugin repositories are intentional and separate from project dependency repositories.
- Convention-plugin source is code-reviewed and contains no real secrets.
- No plugin/configuration log intentionally prints tokens, passwords, signing keys, or protected environment variables.
- Plugin tasks that publish/sign/deploy run only in appropriately privileged CI stages.
- Upgrade tests include plugin/build-logic compatibility and artifact/output comparison.
12. Failure matrix
| Symptom | Evidence to preserve | Likely model error | Least-destructive repair |
|---|---|---|---|
| Plugin not found |
ID/version, plugin repositories, Wrapper,
--info.
|
Wrong version/source policy. | Restore exact version/repository or approved mapping. |
| Unknown task in convention plugin | Stack trace, plugin application order/source. | Eager lookup / missing prerequisite plugin. | Apply prerequisite plugin and use lazy configuration. |
| Breaks only after Gradle upgrade | Old/new Wrapper output, stack trace, plugin source/version. | Internal/deprecated API or incompatible plugin. | Use supported API / compatible plugin version; rollback Wrapper if needed. |
| One module behaves differently | Per-project plugin declarations/task lists. | Convention not applied or inconsistent local override. | Apply correct convention or document separate policy family. |
| Fresh home works, warm home fails | Two isolated User Home logs/checksums. | Cache corruption/stale resolver state possible. | Remove only disposable affected state after evidence; do not nuke normal cache. |
Knowledge check
Why does testing an impossible plugin version with a fresh isolated User Home help?
It makes the resolution failure independent of normal warm-cache state and preserves the user’s real Gradle cache.
Why is reordering scripts not a good repair for
getByName("compileJava") in a convention
plugin?
It preserves order-sensitive coupling; the convention should own/apply the required plugin and use lazy configuration.
What does an .internal. import indicate?
Unsupported implementation API coupling and a likely Gradle-upgrade compatibility risk.
Why are dynamic plugin versions a reproducibility problem?
The resolved executable build code can change when repository metadata changes even if the repository source commit does not.
Why protect verbose Gradle logs?
They can reveal paths, repository/configuration details, environment-derived values, or other sensitive build context.
When should normal ~/.gradle be deleted in this
lab?
Never as the first diagnostic step; use an isolated project-local User Home and delete only disposable lab state.
13. Bridge to the checkpoint
The checkpoint combines all four proof layers: repository-declared plugin source/version policy, convention-plugin source review, task/model evidence, and a controlled failed resolution in a separate User Home. You will finish with a clean rollback and a short production plugin manifest.
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.