Checkpoint Lab — Artifacts, stash/unstash, archiveArtifacts, Fingerprints, Test Reports, Coverage, and Build Evidence
Build one synthetic package exactly once, transfer it between disposable agents, archive and fingerprint it, ingest JUnit evidence, erase workspace state, and prove that retained build evidence still maps to the exact source revision and build.
Learning objectives
- Predict which workspace, stash, artifact, fingerprint, and report states should exist before and after the run.
- Produce one package exactly once and verify the same SHA-256 after cross-agent transfer.
- Retain the package with the producing build, record a Jenkins fingerprint, and ingest a JUnit report.
- Delete workspace state and independently prove retained evidence remains mapped to job/build/source identity.
- Document what Jenkins evidence does and does not prove before bridging to credentials and secret hygiene.
1. Checkpoint scenario
You are preparing a release candidate on a disposable Jenkins controller. The package must be built once, tested on a second agent, retained with the CI build, and traceable to an exact source revision. After the Pipeline completes, both workspaces are intentionally cleaned. Your task is to prove that the retained artifact and test report still exist and still map to the same build/source evidence.
The exercise intentionally stops before real publication. Chapter 15 will add credential handling; this checkpoint therefore uses no repository password, signing key, cloud account, or production target.
2. Assumptions and preflight
| Component | Chapter baseline | Preflight evidence |
|---|---|---|
| Jenkins core | 2.568.3 LTS | Manage Jenkins / system information or container version |
| Java | 21 or 25 for controller | java -version |
| Pipeline: Basic Steps | 1098.v808b_fd7f8cf4 | Installed plugin inventory |
| Pipeline: Job | 1600.v6f36ed83529d | Installed plugin inventory |
| JUnit | 1425.v9c7318dca_96d | Installed plugin inventory |
| Coverage | 3.3358.v9487dde48783 (optional) | Only if optional extension is used |
| Agents | lab-linux-a, lab-linux-b |
Online, one executor each, disposable workspace |
labs/ch14-checkpoint and the repository must contain
only synthetic lab content.
3. Predict state changes before running
Write these predictions into predictions.md before the
build:
- Agent A will create one package and one SHA-256 manifest from a known source SHA.
-
The named stash
release-candidatewill allow the same bytes to appear on agent B during this run. -
archiveArtifactswill retain the package/manifest with the build after workspace cleanup. - The JUnit report will become typed build evidence independent of the final workspace.
- The Jenkins fingerprint will record build relationships, but it will not be described as a signature or cryptographic provenance record.
- Deleting both workspaces will remove transient files but will not erase the retained build artifact/report while the build itself remains retained.
4. Create and pin the synthetic source
mkdir jenkins-ch14-checkpoint && cd jenkins-ch14-checkpoint
git init
git config user.name "Jenkins Chapter 14"
git config user.email "chapter14@example.invalid"
mkdir -p src
echo 'immutable synthetic input' > src/input.txt
git add src/input.txt
git commit -m "chapter14 checkpoint seed"
git rev-parse HEAD > EXPECTED_SOURCE_SHA.txt
cat EXPECTED_SOURCE_SHA.txt
Configure labs/ch14-checkpoint as an SCM-backed
Pipeline. Record repository URL/ref and the expected commit SHA in
your evidence packet before starting the run.
5. Jenkinsfile: build once, transfer, retain, parse, clean
def evidence = [:]
stage('Produce once') {
node('lab-linux-a') {
deleteDir()
checkout scm
evidence.sourceSha = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
evidence.producerNode = env.NODE_NAME
evidence.producerWorkspace = env.WORKSPACE
sh '''
set -eu
mkdir -p out reports
cp src/input.txt out/input.txt
printf 'job=%s\\nbuild=%s\\n' "$JOB_NAME" "$BUILD_NUMBER" > out/build.txt
tar -czf out/release-candidate.tgz out/input.txt out/build.txt
sha256sum out/release-candidate.tgz > out/SHA256SUMS
cat > reports/junit.xml <<'XML'
<testsuite name="chapter14-checkpoint" tests="3" failures="0" errors="0" skipped="0" time="0.03">
<testcase classname="Evidence" name="built_once" time="0.01"/>
<testcase classname="Evidence" name="digest_recorded" time="0.01"/>
<testcase classname="Evidence" name="ready_for_transfer" time="0.01"/>
</testsuite>
XML
'''
evidence.sha256 = sh(script: "awk '{print \\$1}' out/SHA256SUMS", returnStdout: true).trim()
stash name: 'release-candidate', includes: 'out/release-candidate.tgz,out/SHA256SUMS'
archiveArtifacts artifacts: 'out/release-candidate.tgz,out/SHA256SUMS,reports/junit.xml', fingerprint: true
junit testResults: 'reports/junit.xml', allowEmptyResults: false
}
}
stage('Consume exact bytes') {
node('lab-linux-b') {
deleteDir()
evidence.consumerNode = env.NODE_NAME
evidence.consumerWorkspace = env.WORKSPACE
unstash 'release-candidate'
sh 'sha256sum -c out/SHA256SUMS'
def consumerSha = sh(script: "sha256sum out/release-candidate.tgz | awk '{print \\$1}'", returnStdout: true).trim()
if (consumerSha != evidence.sha256) {
error "Artifact digest changed during transfer"
}
writeFile file: 'consumer-proof.txt', text: "sha256=${consumerSha}\\n"
archiveArtifacts artifacts: 'consumer-proof.txt'
deleteDir()
}
}
stage('Record identity') {
echo "job=${env.JOB_NAME} build=${env.BUILD_NUMBER} url=${env.BUILD_URL}"
echo "source=${evidence.sourceSha} artifactSha256=${evidence.sha256}"
echo "producerNode=${evidence.producerNode} consumerNode=${evidence.consumerNode}"
}
The package is created only in Produce once. Agent B
verifies the transferred bytes but does not rebuild. The package and
SHA-256 manifest are archived before the producer workspace is
allowed to disappear.
6. Run and capture first-party evidence
Run once manually. Record the queue item while visible, then the resulting build number/URL and cause. Capture the console sections that show source SHA, producer/consumer node names, workspaces, stash/unstash success, SHA-256 verification, artifact archiving, fingerprint recording, and JUnit ingestion.
Do not rerun merely to make the console cleaner. The first successful run is the evidence-bearing run. If it fails, preserve that failure separately and repair only the failing layer.
7. Prove workspace loss does not erase retained evidence
The Pipeline already deletes the consumer workspace. After the
build, delete the producer workspace through the disposable
agent/workspace UI or by running a guarded cleanup job scoped
specifically to labs/ch14-checkpoint. Do not delete the
build record.
Now verify:
-
Workspace paths no longer contain
release-candidate.tgz. -
The build page still lists
out/release-candidate.tgz,out/SHA256SUMS,reports/junit.xml, andconsumer-proof.txt. -
Downloading the archived package and running
sha256summatches the archived manifest. - The Test Result view still shows three passing tests.
- The fingerprint page still maps the archived package to the producer build.
8. Required evidence packet
| File | Required content |
|---|---|
baseline.txt |
Jenkins/Java/Pipeline/JUnit and optional Coverage versions |
predictions.md |
Pre-run state predictions and later verdicts |
source.txt |
Repository URL/ref + exact source/Jenkinsfile SHA |
build.txt |
Job full name, queue ID if captured, build number/URL/cause/result |
execution.txt |
Producer/consumer node labels, executor/workspace evidence |
artifact.txt |
Artifact paths, size, SHA-256, archived-build mapping |
fingerprint.txt |
Fingerprint relationship and note that it is MD5-based tracking |
reports.txt |
JUnit counts/result and optional coverage summary/plugin version |
workspace-loss.txt |
Proof workspaces were cleaned while retained evidence stayed available |
limitations.md |
No external repository, signature, production credentials, or deployment performed |
9. Verification checklist
| Check | Pass condition |
|---|---|
| Single production | Package built only once on agent A |
| Transfer identity | Agent B verifies the same SHA-256 |
| Retention | Archived package/manifest downloadable after workspace cleanup |
| Typed report | JUnit shows 3 tests, 0 failures for same build |
| Fingerprint | Producer build relationship visible; no authenticity claim made |
| Source attribution | Exact source SHA recorded with build number/URL |
| Security | No real credentials or broad archive patterns used |
10. Controlled failure injection
On a new commit, deliberately change the JUnit pattern to
report/*.xml while keeping the producer path
reports/junit.xml. Predict that package creation and
archival can succeed while report ingestion fails. Run once,
preserve the failure build, interpret the mismatch, then restore the
correct path in another commit.
This proves a central lesson: artifact publication and report ingestion are distinct states. A build can contain valid bytes even when its reporting step fails.
11. Cleanup and rollback
Download the evidence packet first. Delete only the synthetic
repository, labs/ch14-checkpoint when you no longer
need its retained evidence, and any agents created exclusively for
this lab. Do not remove shared Pipeline/JUnit plugins or unrelated
build history. If Coverage was installed solely for this disposable
controller, remove it only after confirming no other lab depends on
it.
12. What Chapter 14 adds to the production model
You can now tell the difference between temporary transfer and durable evidence, prove that a retained artifact maps to an exact build/source, understand the limited but useful role of Jenkins fingerprints, and verify typed test/coverage ingestion independently of workspace state.
Chapter 15 adds the next trust boundary: credentials. You will learn how secret text, files, SSH keys, username/password pairs, credential scope, and Pipeline bindings interact with these same artifact/workspace/evidence rules—especially how to prevent secrets from leaking into logs or archived files.
Knowledge check
What proves the checkpoint package was built once and not reconstructed on agent B?
The Jenkinsfile creates the package only on agent A; agent B only unstashes it and verifies the same SHA-256.
What survives workspace cleanup in the checkpoint?
The build-owned archived artifacts, fingerprint record, JUnit result, console/run metadata, and optional coverage result survive while the retained build exists.
Why archive the JUnit XML as well as ingest it?
The raw report remains portable/debuggable evidence while the parsed JUnit result is plugin-derived typed state.
What does the wrong-glob failure injection demonstrate?
Artifact creation/archival and report ingestion are separate states; one can succeed while the other fails.
What security concern bridges directly into Chapter 15?
Artifacts, stashes, reports, and workspaces can accidentally capture secrets, so credential binding and artifact allowlists must be designed together.
Official references and version notes
-
Pipeline: Basic Steps reference
— current
stash/unstashsemantics and guidance for cross-stage transfer. -
Running Pipelines
— Declarative stage restart and
preserveStashesbehavior. -
Recording tests and artifacts
—
archiveArtifacts, fingerprinting, and JUnit publishing patterns. - Jenkins fingerprints — dependency/use tracking and the MD5-based fingerprint record.
- JUnit plugin — maintained test-result ingestion and build/test history.
- Coverage plugin — maintained coverage ingestion, quality gates, and source-retention choices.
-
Coverage Pipeline step reference
— current
recordCoverageparameters and supported parsers. - Artifact Manager on S3 plugin — optional example of external artifact-manager integration; not required by the labs.
- Jenkins LTS changelog — current LTS and tested Java configurations.
Rechecked on 2026-09-16. Examples assume Jenkins 2.568.3 LTS (tested with Java 21 and 25), Pipeline: Basic Steps 1098.v808b_fd7f8cf4, Pipeline: Job 1600.v6f36ed83529d, and JUnit 1425.v9c7318dca_96d. The optional coverage extension uses Coverage 3.3358.v9487dde48783, which requires Jenkins 2.555.3 or newer and is therefore compatible with this LTS baseline. The mandatory path is free/local/disposable. Record the versions actually installed on your controller before applying the examples.
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.