Chapter 14Lesson 05~165 minutes

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.

CheckpointBuild onceEvidence packetWorkspace lossRetentionPromotion boundary

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
Stop if identity is unclear: do not proceed on a production controller or shared release job. The job must be 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:

  1. Agent A will create one package and one SHA-256 manifest from a known source SHA.
  2. The named stash release-candidate will allow the same bytes to appear on agent B during this run.
  3. archiveArtifacts will retain the package/manifest with the build after workspace cleanup.
  4. The JUnit report will become typed build evidence independent of the final workspace.
  5. The Jenkins fingerprint will record build relationships, but it will not be described as a signature or cryptographic provenance record.
  6. 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, and consumer-proof.txt.
  • Downloading the archived package and running sha256sum matches 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.

Next lesson

Chapter 15 — Credentials Store, Secret Text, Files, SSH Keys, Username/Password, Binding, and Secret Hygiene

Add credentials to the operating model without leaking them into logs, workspaces, archived artifacts, or untrusted Pipeline code.

Knowledge check

What proves the checkpoint package was built once and not reconstructed on agent B?

What survives workspace cleanup in the checkpoint?

Why archive the JUnit XML as well as ingest it?

What does the wrong-glob failure injection demonstrate?

What security concern bridges directly into Chapter 15?

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), 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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.