Chapter 18Lesson 04~205 minutes

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.

DiagnosticsPlugin repositoriesEager configurationInternal APIsSecurity

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

  1. Preserve concise evidence: original error, task path, repository/plugin ID/version, relevant --info lines.
  2. Confirm identity: Wrapper distribution/Gradle version and Gradle runtime JVM.
  3. Inspect declared/effective plugin policy: settings pluginManagement, plugins blocks, included build source.
  4. Inspect project/task model: which projects apply the plugin, which tasks/extensions exist.
  5. Inspect repository/cache/filesystem state: plugin repositories and an isolated fresh GRADLE_USER_HOME if cache ambiguity matters.
  6. Inspect plugin/build-logic failure: classpath, public/internal API use, eager task access, plugin compatibility.
  7. Apply the least destructive correction and rerun under controlled state.
  8. 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?

Why is reordering scripts not a good repair for getByName("compileJava") in a convention plugin?

What does an .internal. import indicate?

Why are dynamic plugin versions a reproducibility problem?

Why protect verbose Gradle logs?

When should normal ~/.gradle be deleted in this lab?

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

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.