Checkpoint Lab — Gradle Plugins, Core Plugins, Community Plugins, Convention Plugins, and Plugin Management
Create a multi-project build whose Java policy comes from reviewed convention logic and whose external plugin resolution is pinned and inspectable, then break and repair one plugin-resolution assumption without touching shared Gradle state.
Create a multi-project build whose Java policy comes from reviewed convention logic and whose external plugin resolution is pinned and inspectable, then break and repair one plugin-resolution assumption without touching shared Gradle state.
Learning objectives
- Create a multi-project Gradle checkpoint with repository-owned Java convention logic and one pinned external plugin.
- Record Wrapper/JVM/plugin repository/plugin version evidence before executing build tasks.
- Predict which projects gain Java, application, Spotless, and convention-provided model elements.
- Prove plugin-provided tasks and project scope independently.
- Inject and diagnose one plugin-version/repository-resolution failure using a separate isolated Gradle User Home.
- Audit convention logic for secret/environment assumptions and cleanly restore all experimental changes.
1. Checkpoint objective and acceptance criteria
You are preparing a small repository for CI onboarding. The
app and library projects share Java policy
from reviewed convention logic. Spotless is a pinned external plugin
applied only to app. The build must prove its plugin
source/version policy and remain recoverable if plugin resolution
breaks.
Acceptance: exact Wrapper/runtime evidence, exact external plugin version/repository, convention source under version control, scoped tasks/extensions, clean build/format check, preserved broken-resolution evidence, no secrets/environment-specific assumptions in convention source, and deletion of only disposable state.
2. Preflight and version assumptions
mkdir gradle-plugin-checkpoint
cd gradle-plugin-checkpoint
export GRADLE_USER_HOME="$PWD/.checkpoint-gradle-home"
# Copy the verified Gradle 9.7.1 Wrapper files from Chapter 15.
chmod +x gradlew
./gradlew --version | tee gradle-version.txt
java -version 2> java-version.txt
mkdir -p app/src/main/java/dev/academy/app
mkdir -p library/src/main/java/dev/academy/lib
mkdir -p build-logic/src/main/kotlin
mkdir -p evidence
Assumptions: Gradle 9.7.1, supported JVM runtime (JDK 21 in the course), Java 17 target, and Spotless 8.10.0 from the public Gradle Plugin Portal. If external network access is blocked, do not weaken repository policy; record that resolution cannot be exercised in that environment and use an approved mirror/cached CI environment rather than adding arbitrary repositories.
3. Create the complete repository-owned plugin model
cat > settings.gradle.kts <<'EOF'
pluginManagement {
includeBuild("build-logic")
repositories {
gradlePluginPortal()
}
plugins {
id("com.diffplug.spotless") version "8.10.0"
}
}
rootProject.name = "gradle-plugin-checkpoint"
include("app", "library")
EOF
cat > build.gradle.kts <<'EOF'
plugins {
id("com.diffplug.spotless") apply false
}
EOF
cat > build-logic/settings.gradle.kts <<'EOF'
rootProject.name = "build-logic"
EOF
cat > build-logic/build.gradle.kts <<'EOF'
plugins { `kotlin-dsl` }
repositories { mavenCentral() }
EOF
cat > build-logic/src/main/kotlin/academy.java-conventions.gradle.kts <<'EOF'
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.toolchain.JavaLanguageVersion
plugins { java }
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.release.set(17)
}
tasks.register("conventionReport") {
group = "academy"
doLast {
println("plugin=academy.java-conventions")
println("release=17")
println("encoding=UTF-8")
}
}
EOF
cat > app/build.gradle.kts <<'EOF'
plugins {
id("academy.java-conventions")
application
id("com.diffplug.spotless")
}
application { mainClass.set("dev.academy.app.Main") }
spotless {
format("misc") {
target("*.md")
trimTrailingWhitespace()
endWithNewline()
}
}
EOF
cat > library/build.gradle.kts <<'EOF'
plugins {
id("academy.java-conventions")
`java-library`
}
EOF
cat > app/src/main/java/dev/academy/app/Main.java <<'EOF'
package dev.academy.app;
public final class Main {
public static void main(String[] args) {
System.out.println("checkpoint ok");
}
}
EOF
cat > library/src/main/java/dev/academy/lib/LibraryValue.java <<'EOF'
package dev.academy.lib;
public final class LibraryValue {
private LibraryValue() {}
public static int answer() { return 42; }
}
EOF
printf '%s
' '# Plugin Checkpoint' '' 'Reviewed plugin policy.' > app/README.md
4. Predict model changes before the first build
Write these predictions before running Gradle:
Prediction A — both app and library:
academy.java-conventions is resolvable from build-logic
Java plugin/model exists
compileJava and conventionReport tasks exist
Java release convention is 17
Prediction B — app only:
Application plugin contributes run/distribution-related model
Spotless plugin contributes spotlessCheck/spotlessApply-style tasks
Prediction C — root project:
Spotless is resolved with apply false, but root should not gain app Spotless configuration/tasks from application
Prediction D — plugin resolution:
com.diffplug.spotless uses exact version 8.10.0 from configured plugin repository policy
5. Record plugin source/version policy before execution
grep -n -E 'pluginManagement|includeBuild|gradlePluginPortal|com.diffplug.spotless|8.10.0' settings.gradle.kts build.gradle.kts app/build.gradle.kts library/build.gradle.kts > evidence/plugin-policy.txt
cat evidence/plugin-policy.txt
This file is a review artifact: it shows what the repository declares. Pair it with runtime identity and execution evidence; do not treat logs alone as the source of truth.
6. Prove plugin-provided tasks and application scope
./gradlew :app:tasks --all > evidence/app-tasks.txt
./gradlew :library:tasks --all > evidence/library-tasks.txt
grep -E 'conventionReport|compileJava|spotless|run' evidence/app-tasks.txt || true
grep -E 'conventionReport|compileJava|spotless|run' evidence/library-tasks.txt || true
./gradlew :app:conventionReport :library:conventionReport --console=plain | tee evidence/convention-report.log
Verify the predictions independently: convention and Java tasks appear in both projects; Spotless/application tasks belong only to app. If library unexpectedly gains formatting tasks, inspect its plugin declarations and convention logic before continuing.
7. Execute the controlled plugin work and capture evidence
./gradlew :app:spotlessCheck --info --console=plain > evidence/spotless-info.log 2>&1
./gradlew clean build --console=plain | tee evidence/build.log
./gradlew :app:run --console=plain | tee evidence/run.log
grep -E 'com.diffplug.spotless|spotless' evidence/spotless-info.log | head -40 || true
Expected application output: checkpoint ok. A clean
build is useful after plugin resolution succeeds because it proves
plugin/configuration compatibility with the JVM project, but it is
not provenance proof by itself.
8. Audit convention logic for secrets and environment coupling
Repository-owned convention logic should not depend on workstation-specific paths or secrets. Scan its source:
grep -R -n -E 'System\.getenv|environmentVariable|password|token|secret|/Users/|/home/|[A-Za-z]:\\' build-logic/src || true
Expected result for this lab: no matches. A match would require review, not automatic deletion—some environment access can be intentional when modeled through Providers, but secret values must never be printed or committed.
9. Inject a plugin-version failure without polluting good state
Preserve the known-good settings, change only the external plugin version, and execute with a separate User Home:
cp settings.gradle.kts evidence/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
set +e
GRADLE_USER_HOME="$PWD/.checkpoint-broken-home" ./gradlew :app:spotlessCheck --info --console=plain > evidence/broken-plugin-resolution.log 2>&1
rc=$?
set -e
printf 'broken_exit=%s
' "$rc" | tee evidence/broken-exit.txt
grep -E 'Plugin|com.diffplug.spotless|99.99.99|not found|could not' evidence/broken-plugin-resolution.log | head -100 || true
Expected: non-zero exit and a plugin-resolution error. This is a deliberate supply-chain/configuration failure, not a formatting/test failure. Preserve the log before repair.
10. Restore reviewed policy and verify recovery
cp evidence/settings.good.gradle.kts settings.gradle.kts
rm -rf .checkpoint-broken-home
export GRADLE_USER_HOME="$PWD/.checkpoint-gradle-home"
./gradlew :app:spotlessCheck build --console=plain | tee evidence/repaired-build.log
Recovery proves that the smallest policy correction—restoring the reviewed version—repairs the build. No normal Gradle cache was deleted, no extra plugin repository was added, and no version loosened to a dynamic selector.
11. Optional second fault: missing plugin repository
If you want one additional diagnostic exercise, create another
disposable copy of settings and remove
gradlePluginPortal(), then use a fresh isolated User
Home. The expected failure is inability to resolve the external
community plugin. Restore the repository allow-list afterward. Do
not add arbitrary repositories until one happens to
work.
12. Produce a small plugin manifest for code review
Record a human-readable manifest:
Gradle engine: 9.7.1 (project Wrapper)
Gradle runtime JVM: JDK 21 course baseline
Local convention plugin: academy.java-conventions
source: build-logic/src/main/kotlin/academy.java-conventions.gradle.kts
external version: none; repository-owned source in this build
Core plugins:
java / application / java-library -> supplied by Gradle 9.7.1
Community plugin:
id: com.diffplug.spotless
version: 8.10.0
source policy: gradlePluginPortal()
Scope:
app -> convention + application + Spotless
library -> convention + java-library
In a production repository, this information may live in architecture/build-governance documentation or be generated by policy tooling. The important property is that reviewers can answer “what executable build logic do we trust?” without reverse-engineering every build script.
13. Verification checklist
- Exactly one verified Gradle 9.7.1 Wrapper is used.
- All checkpoint Gradle state is under project-local User Homes.
- Spotless ID/version and plugin repository are exact and reviewable.
-
academy.java-conventionssource is repository-owned and reviewable. - Both JVM subprojects expose the convention task and Java model.
- Only app exposes Spotless/application-specific tasks.
- Convention source contains no secret/workstation assumption in this lab.
- Broken 99.99.99 resolution produced a preserved non-zero failure.
- Repair restored exact 8.10.0 rather than loosening repository/version policy.
- Clean build and formatting gate succeed after repair.
14. Cleanup and rollback
./gradlew --stop || true
cd ..
rm -rf gradle-plugin-checkpoint
This removes only disposable checkpoint state. In a real repository, rollback means restoring reviewed settings/build-logic commits and preserving CI logs—not deleting shared caches or hiding plugin-resolution evidence.
15. What Chapter 18 adds to the production build-engineering model
You can now treat plugins as a formal trust graph: the Gradle engine supplies core plugins; settings defines where non-core plugins may come from and which versions are acceptable; projects decide application scope; convention plugins package repository-owned policy; and task/model evidence proves what those plugins contribute.
Chapter 19 moves from plugin code to dependency resolution semantics: configurations, variants, attributes, capabilities, and metadata rules. The same discipline continues—inspect the declared model, resolved graph, source metadata, and trust boundary before changing resolution behavior.
Knowledge check
What two independent pins protect this checkpoint?
The Gradle Wrapper pins the build engine, while pluginManagement pins the external Spotless plugin version/source policy.
Why is the broken test run under a separate Gradle User Home?
It isolates resolver/cache state and preserves the known-good checkpoint User Home and normal user cache.
What evidence proves local convention logic is applied to both modules?
Both project task listings contain conventionReport/Java tasks and both conventionReport tasks print the same reviewed policy identity.
Why does a successful clean build not prove plugin provenance?
It proves compatibility/execution, not who supplied plugin code or whether the repository/version policy is trustworthy.
What is the correct repair for version 99.99.99?
Restore the reviewed exact 8.10.0 version policy; do not add random repositories or a dynamic version.
What is the bridge to Chapter 19?
Plugins create configurations and resolution behavior; the next chapter inspects how Gradle selects dependency variants/capabilities/metadata inside those configurations.
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.