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.
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.
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.
Knowledge check
What proves that Jenkins selected the tool you expected?
Agent-side executable/version evidence, not merely the global tool name in controller configuration.
Why capture a source SHA next to wrapper metadata?
The wrapper is source-controlled executable/toolchain input, so its version must be tied to the exact source revision used by the build.
Should the lab archive every environment variable for reproducibility?
No. Blind environment dumps can expose secrets. Record only toolchain-relevant non-secret fields.
What should you record for an optional containerized build besides the tag?
The resolved content digest, plus the tool versions observed inside that image.
What is the purpose of the artifact SHA-256?
It ties a concrete output byte sequence to the recorded source and toolchain evidence.
Official references and version notes
- Jenkins LTS changelog — current LTS line and tested Java configurations.
- Java Support Policy — Java versions supported for running Jenkins controller, agents, and CLI.
- Upgrade to Java 21 — controller/agent runtime inspection and migration guidance.
-
Declarative Pipeline
toolsdirective — preconfigured JDK, Maven, and Gradle tool installations and PATH behavior. - Pipeline examples — selecting named JDK/Maven installations and recording versions.
- Using Docker with Pipeline — containerized execution environment, image selection, caching, and Docker Pipeline prerequisites.
- Docker Pipeline plugin — plugin release and compatibility information for Pipeline container steps.
- Apache Maven Wrapper and Gradle Wrapper — project-owned build-tool version selection.
- Node.js downloads and project lockfile documentation — runtime/package-manager pinning belongs with the project or build image.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.