Chapter 08Lesson 02~130 minutes

Tool Installations, JDKs, Maven, Gradle, Node.js, Docker CLI, and Reproducible Build Environments: Guided Hands-On Workflow and Core Operations

Build a synthetic Java project on a disposable agent, prove JDK and build-tool identity, compare Jenkins-managed/native and project-wrapper/container strategies, and archive an evidence file beside the artifact.

Hands-onTool evidenceMaven WrapperGradle WrapperPATHArtifact SHA-256

Learning objectives

  • Inspect agent OS/architecture, PATH, JAVA_HOME, and resolved tool versions before building.
  • Configure or select a named Jenkins tool and prove what executable Jenkins exposes.
  • Use a project wrapper as a source-controlled version contract and compare it with a global tool definition.
  • Record an artifact checksum together with exact toolchain evidence.
  • Use an optional isolated container comparison without requiring a host Docker socket for core completion.

1. Lab scenario and safety boundary

Use a disposable Jenkins controller and a disposable Linux agent labelled lab-linux. The synthetic repository contains only a tiny Java class and build metadata. No real credentials, proprietary source, package publication, or production registry is involved.

Core completion does not require Docker. If Docker is used for the optional comparison, use a purpose-built disposable Docker-capable agent. Do not mount a production host socket into arbitrary repository-controlled jobs.

2. Preflight: record the runtime before configuring tools

Run a read-only job on lab-linux and record:

set -eu
printf 'node=%s\n' "$NODE_NAME"
printf 'workspace=%s\n' "$WORKSPACE"
uname -m
java -version
printf 'JAVA_HOME=%s\n' "${JAVA_HOME:-unset}"
printf 'PATH=%s\n' "$PATH"
command -v javac || true
command -v mvn || true
command -v gradle || true
command -v node || true
command -v docker || true

This is your baseline. A later PATH change is meaningful only if you can compare it with the preflight state.

3. Create the synthetic source and immutable build target

Create a training repository with a simple Java class and a small Maven project. Commit the source, pom.xml, Maven Wrapper files, and wrapper properties. Record the source SHA before the build.

package academy;
public final class ToolchainProof {
  public static void main(String[] args) {
    System.out.println("toolchain-proof-v1");
  }
}

The output JAR is only a teaching artifact. The important result is the chain from source SHA and wrapper metadata to tool versions and artifact checksum.

4. Strategy A: Jenkins-managed or agent-preinstalled tool

Under Manage Jenkins → Tools, define a JDK named jdk21-build and, if your lab intentionally uses a Jenkins-managed Maven installation, a Maven tool named maven-3.9. Prefer a deliberately installed path/image in a training environment over an opaque “whatever latest is today” auto-download.

A compact Declarative Pipeline can request the named tools:

pipeline {
  agent { label 'lab-linux' }
  tools {
    jdk 'jdk21-build'
    maven 'maven-3.9'
  }
  stages {
    stage('Evidence') {
      steps {
        sh '''
          set -eu
          java -version
          javac -version
          mvn -B -V --version
          printf 'JAVA_HOME=%s\\n' "$JAVA_HOME"
          command -v java
          command -v mvn
        '''
      }
    }
    stage('Build') {
      steps { sh 'mvn -B -DskipTests package' }
    }
  }
}

The first build proves the Jenkins name resolved to a concrete toolchain on this agent. Save those version lines with the build.

5. Strategy B: project-owned Maven Wrapper

Now remove the Maven tools requirement from the build stage and execute the committed wrapper instead. Keep the JDK explicit.

stage('Wrapper build') {
  steps {
    sh '''
      set -eu
      test -x ./mvnw
      ./mvnw -B -V --version
      ./mvnw -B -DskipTests package
    '''
  }
}

The tool version intent now lives in the source tree. Capture the wrapper properties file, its source revision, and the effective Maven version. Review wrapper changes like executable dependency changes.

6. Produce a toolchain evidence file and artifact checksum

set -eu
ARTIFACT=$(find target -maxdepth 1 -type f -name '*.jar' | head -n 1)
test -n "$ARTIFACT"
{
  printf 'job=%s\n' "$JOB_NAME"
  printf 'build=%s\n' "$BUILD_NUMBER"
  printf 'node=%s\n' "$NODE_NAME"
  printf 'source_sha=%s\n' "$(git rev-parse HEAD)"
  printf 'arch=%s\n' "$(uname -m)"
  printf 'java_home=%s\n' "${JAVA_HOME:-unset}"
  javac -version 2>&1
  ./mvnw -B -V --version 2>&1 | sed -n '1,8p'
  sha256sum "$ARTIFACT"
} | tee toolchain-evidence.txt

Archive the JAR and toolchain-evidence.txt. This is build evidence, not a general-purpose artifact repository; later chapters will separate durable package publication from Jenkins build archives.

7. Add a Node.js observation without confusing ecosystems

If the disposable agent has Node.js, capture node --version and npm --version. For a synthetic Node subproject, commit a lockfile and use npm ci. Do not let a Java build’s JDK pin create the false impression that Node.js is also pinned.

if command -v node >/dev/null 2>&1; then
  node --version
  npm --version
fi

8. Optional strategy C: isolated container toolchain

On an explicitly Docker-capable disposable agent with the Docker Pipeline plugin, run a comparison stage in a pinned image. Current Jenkins documentation demonstrates Maven/Java and Node containers for this purpose. Resolve and record the image digest before treating it as reproducible evidence.

docker pull maven:3.9.16-eclipse-temurin-21-alpine
docker image inspect --format '{{index .RepoDigests 0}}' maven:3.9.16-eclipse-temurin-21-alpine

Use the resulting digest in your evidence packet. If your environment cannot safely provide Docker, skip this optional branch; the learning goal is the ownership/evidence model, not Docker administration.

9. Verification checklist

  • Source SHA recorded before build.
  • Agent name, OS/architecture, PATH/JAVA_HOME evidence captured.
  • Effective Java and Maven versions captured from the executing agent.
  • Wrapper metadata tied to the same source revision.
  • Artifact SHA-256 recorded.
  • Optional container image resolved to a digest.
  • No secret/environment dump or production Docker socket exposure.

10. Cleanup

Delete only the disposable job, temporary repository, and lab agent/container resources created for this chapter. Keep the evidence packet if you want to compare Chapter 08 experiments. Do not remove shared Jenkins tool definitions unless you created them solely for this lab and have confirmed no other job references them.

Next lesson

Configuration, Design Choices, and Tradeoffs

Compare global tools, project wrappers, pre-baked agents, automatic installers, and container images by ownership, reproducibility, trust, and rollback.

Knowledge check

What proves that Jenkins selected the tool you expected?

Why capture a source SHA next to wrapper metadata?

Should the lab archive every environment variable for reproducibility?

What should you record for an optional containerized build besides the tag?

What is the purpose of the artifact SHA-256?

Official references and version notes

Version and compatibility note

Rechecked on 2026-09-16. Examples assume Jenkins 2.568.3 LTS, tested with Java 21 and 25. The lab uses Java 21 for the Jenkins controller/agent runtime, but deliberately treats the application build JDK as separate state. Declarative tools supports Jenkins-configured jdk, maven, and gradle tools. Docker-based Pipeline examples are optional and require the Docker Pipeline plugin plus a deliberately isolated Docker-capable agent; the mandatory path does not mount a host Docker socket into untrusted builds.

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.