Chapter 15Lesson 02~190 minutes

Gradle Installation, Wrapper, CLI, Settings, Build Scripts, and Project Bootstrap: Guided Hands-On Workflow and Core Operations

Bootstrap a small Java build once with a trusted Gradle installation, pin and verify Gradle 9.7.1 through the Wrapper, then inspect tasks, projects, properties, tests, generated output, and cache locations through the project-owned entry point.

gradle initWrapper SHA-256TasksProjectsBuild/Test

Bootstrap a small Java build once with a trusted Gradle installation, pin and verify Gradle 9.7.1 through the Wrapper, then inspect tasks, projects, properties, tests, generated output, and cache locations through the project-owned entry point.

Learning objectives

  • Bootstrap a disposable Java application with Gradle 9.7.1 and Java 17 target configuration.
  • Generate a Wrapper pinned to 9.7.1 and configure the official distribution SHA-256.
  • Verify both Wrapper distribution metadata and the Wrapper JAR before first project-owned execution.
  • Use Wrapper-only commands to inspect projects, tasks, selected properties, tests, and build outputs.
  • Observe Gradle User Home, project .gradle/, daemon, dependency-cache, and build/ state separately.
  • Clean up only disposable lab state.

1. Scenario and bootstrap rule

You are creating a new JVM repository. There is no Wrapper yet, so one trusted Gradle installation must create the initial project/Wrapper files. That one-time bootstrap use is different from the repository’s normal operating rule.

Rule: use a reviewed system Gradle only to bootstrap or repair the Wrapper. Once the Wrapper is generated and verified, all build/test/discovery commands use the Wrapper.

The lab uses an isolated GRADLE_USER_HOME inside the disposable project. It never deletes or edits your normal ~/.gradle.

2. Preflight: record the machine and installed bootstrap tool

# POSIX — run outside any untrusted Gradle repository
java -version
gradle --version

# Expected chapter baseline:
# Gradle 9.7.1
# JVM 21.x (or another Gradle-supported JVM 17..26)
# PowerShell
java -version
gradle --version

If the machine has no trusted Gradle installation, install Gradle 9.7.1 through Gradle’s official installation path or your organization’s controlled package channel. Do not borrow an unknown gradle-wrapper.jar from another repository.

3. Create the disposable project with an isolated Gradle User Home

# POSIX
mkdir wrapper-lab
cd wrapper-lab
export GRADLE_USER_HOME="$PWD/.lab-gradle-home"

# Non-interactive stable bootstrap. Build Init also creates Wrapper files.
gradle init \
  --type java-application \
  --dsl kotlin \
  --test-framework junit-jupiter \
  --package dev.academy.bootstrap \
  --project-name wrapper-lab \
  --no-split-project \
  --java-version 17 \
  --use-defaults \
  --no-incubating

--no-incubating keeps the generated baseline on stable features. --no-split-project avoids introducing multi-project structure in the bootstrap lesson. Build Init generates source/build scripts and Wrapper files; inspect what your pinned Gradle generated rather than assuming future init output is byte-for-byte identical.

4. Inspect generated source before executing the Wrapper

printf '%s
' '--- files ---'
find . -maxdepth 3 -type f -not -path './.lab-gradle-home/*' | sort

printf '%s
' '--- settings ---'
sed -n '1,160p' settings.gradle.kts

printf '%s
' '--- build ---'
sed -n '1,220p' build.gradle.kts

printf '%s
' '--- wrapper properties before hardening ---'
cat gradle/wrapper/gradle-wrapper.properties

At this point the files are ordinary inputs you can review. Do not run ./gradlew merely because it exists. First pin the exact distribution and validate the executable Wrapper JAR.

5. Regenerate the Wrapper with exact version and distribution checksum

gradle :wrapper \
  --gradle-version 9.7.1 \
  --distribution-type bin \
  --gradle-distribution-sha256-sum acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a

cat gradle/wrapper/gradle-wrapper.properties

The expected distribution URL is https://services.gradle.org/distributions/gradle-9.7.1-bin.zip and the expected distributionSha256Sum is acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a. The checksum is reviewed data, not something to derive from the downloaded ZIP after trusting that same download.

6. Verify the Wrapper JAR before first Wrapper execution

actual=$(sha256sum gradle/wrapper/gradle-wrapper.jar | awk '{print $1}')
expected="7a9ce74cff467ca1bf60a4fcd9f05185acceda4d0f382434d393e17864262c5d"
printf 'expected=%s
actual=%s
' "$expected" "$actual"
test "$actual" = "$expected"
$expected = "7a9ce74cff467ca1bf60a4fcd9f05185acceda4d0f382434d393e17864262c5d"
$actual = (Get-FileHash gradle\wrapper\gradle-wrapper.jar -Algorithm SHA256).Hash.ToLower()
"expected=$expected"
"actual=$actual"
if ($actual -ne $expected) { throw "Wrapper JAR checksum mismatch" }

If the checksum differs, stop. Determine whether the JAR came from a different known Gradle version; do not “fix” the expected hash to match an unexplained binary.

7. Switch to the project-owned entry point

# POSIX
./gradlew --version
./gradlew -q projects
./gradlew tasks --all
./gradlew help --task build

The first Wrapper invocation may download and unpack Gradle 9.7.1 into $GRADLE_USER_HOME/wrapper/dists. The configured SHA-256 is checked before the distribution is trusted. Subsequent invocations can reuse that verified distribution from this isolated home.

8. Inspect selected project properties without dumping secret-bearing state into logs

# Disposable lab contains no secrets; filter to known benign properties.
./gradlew properties --console=plain   | grep -E '^(name|group|version):' || true

# Prefer focused task help when you only need task configuration/usage.
./gradlew help --task test

properties can reveal project properties supplied from build logic, command line, or user state. In real CI, do not upload an unreviewed full property dump if credentials or tokens may have been represented as Gradle properties.

9. Run tests and build through the Wrapper; inspect evidence

./gradlew clean test build --console=plain

find build -maxdepth 3 -type f | sort | sed -n '1,120p'
find build/test-results -type f -name '*.xml' -print
find build/libs -type f -print

For a single-project Java application, test reports normally appear below build/test-results/test/ and HTML reports below build/reports/tests/test/; archives appear below build/libs/. These paths are generated outputs, not dependency caches.

10. Prove where Gradle stored each kind of state

printf '%s
' '--- project-local state ---'
find .gradle -maxdepth 2 -type d 2>/dev/null | sort | sed -n '1,80p'

printf '%s
' '--- isolated user home ---'
find "$GRADLE_USER_HOME" -maxdepth 2 -type d | sort | sed -n '1,100p'

printf '%s
' '--- wrapper distributions ---'
find "$GRADLE_USER_HOME/wrapper/dists" -maxdepth 3 -type d 2>/dev/null | sort | sed -n '1,80p'

./gradlew --status

You should be able to point to separate directories for the Wrapper distribution, dependency/cache state, daemon state, project-local Gradle state, and build outputs. That separation is the basis for later cache and performance chapters.

11. Challenge: choose the correct control

A teammate says, “CI already has Gradle 9.8 installed, so I changed the job from ./gradlew build to gradle build to avoid downloading 9.7.1.” What should you change?

Expected reasoning: restore Wrapper invocation. If downloading is the concern, cache/provision the reviewed Wrapper distribution or upgrade the Wrapper through a reviewed change. Do not silently substitute the runner’s tool version for the repository contract.

12. Verification checklist and cleanup

  • ./gradlew --version reports Gradle 9.7.1 and the expected runtime JVM.
  • gradle-wrapper.properties contains the exact distribution URL and reviewed SHA-256.
  • gradle-wrapper.jar matches the official 9.7.1 Wrapper-JAR checksum.
  • test and build succeeded through the Wrapper.
  • Generated outputs are under build/; user-home state is under the isolated .lab-gradle-home.
# Still inside wrapper-lab
./gradlew --stop || true
cd ..
rm -rf wrapper-lab

Only the disposable project and its embedded Gradle User Home are removed. Your normal Gradle User Home is untouched.

Knowledge check

Why does this lab use system Gradle at all?

Why pin both the distribution version and SHA-256?

Why verify gradle-wrapper.jar separately?

What is wrong with uploading a full ./gradlew properties log from a real CI job?

Why does cleanup avoid deleting the normal ~/.gradle directory?

13. Bridge to design choices

The bootstrap works. Lesson 3 asks which choices belong in a long-lived repository: Kotlin versus Groovy DSL, wrapper-only policy, shared developer caches versus isolated CI homes, and how much of Build Init’s generated shape should become an organizational convention.

Official references and version notes

Version snapshot: These lessons were generated for August 24, 2026 with Gradle 9.7.1 as the pinned lab release. Re-check the official release and compatibility pages before copying version-specific pins into a future production repository.

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.