Checkpoint Lab — Artifact Repositories, Nexus/Artifactory Integration, Package Promotion, Build Metadata, and Release Traceability
Publish one uniquely versioned candidate, verify repository digest and metadata, promote the same bytes, and produce an evidence packet tracing release back to Jenkins build/source.
Learning objectives
- Predict state changes before mutating repository state.
- Publish a unique candidate from a disposable agent.
- Verify repository bytes independently from workspace bytes.
- Promote without rebuilding and prove digest continuity.
- Produce evidence and explicit limitations.
1. Scenario
A synthetic service named trace-widget must create a
candidate, publish it into a local candidate repository, attach
build metadata, verify it, promote the exact bytes into release
state and prove the release maps to one Jenkins build/source SHA. No
external account or paid plugin is required.
2. Predict before you run
- A new candidate coordinate will appear; nothing existing will be overwritten.
- Workspace and candidate SHA-256 will match.
- Promotion will not invoke a build tool.
- Candidate and release SHA-256 will match.
- Metadata will name exact job/build/source.
- A second attempt at the same coordinate will fail safely.
3. Preflight inventory
set -euo pipefail
printf 'date_utc='; date -u +%FT%TZ
printf 'source_sha='; git rev-parse HEAD
printf 'job=%s build=%s\n' "${JOB_NAME:-local-checkpoint}" "${BUILD_NUMBER:-0}"
printf 'build_url=%s\n' "${BUILD_URL:-local://build/0}"
printf 'node=%s workspace=%s\n' "${NODE_NAME:-local}" "${WORKSPACE:-$PWD}"
printf 'java='; java -version 2>&1 | head -1 || true
python3 --version
git --version
Also record Jenkins core/LTS and relevant plugin inventory from approved read-only views/API if your permissions allow. Script Console is not required.
4. Checkpoint script
Store as ci/repository-checkpoint.sh. It fails closed
when a coordinate exists.
#!/usr/bin/env bash
set -euo pipefail
mkdir -p dist evidence lab-repo/candidates lab-repo/releases
SOURCE_SHA="$(git rev-parse HEAD)"
SHORT_SHA="$(printf '%s' "$SOURCE_SHA" | cut -c1-8)"
BUILD_NO="${BUILD_NUMBER:-0}"
VERSION="1.0.${BUILD_NO}-${SHORT_SHA}"
NAME="trace-widget"
ARTIFACT="dist/${NAME}-${VERSION}.txt"
printf 'name=%s\nversion=%s\nsource=%s\nbuild=%s\n' "$NAME" "$VERSION" "$SOURCE_SHA" "$BUILD_NO" > "$ARTIFACT"
WORKSPACE_SHA="$(sha256sum "$ARTIFACT" | awk '{print $1}')"
printf '%s\n' "$WORKSPACE_SHA" > evidence/workspace.sha256
python3 -c 'import json,os,sys,pathlib; p,d,s=sys.argv[1:]; pathlib.Path("evidence/build-metadata.json").write_text(json.dumps({"artifact":pathlib.Path(p).name,"sha256":d,"job":os.getenv("JOB_NAME","local-checkpoint"),"buildNumber":os.getenv("BUILD_NUMBER","0"),"buildUrl":os.getenv("BUILD_URL","local://build/0"),"sourceSha":s,"node":os.getenv("NODE_NAME","local")},indent=2)+"\\n")' "$ARTIFACT" "$WORKSPACE_SHA" "$SOURCE_SHA"
CAND="lab-repo/candidates/com/example/${NAME}/${VERSION}"
REL="lab-repo/releases/com/example/${NAME}/${VERSION}"
test ! -e "$CAND" || { echo "candidate exists: $CAND" >&2; exit 31; }
test ! -e "$REL" || { echo "release exists: $REL" >&2; exit 32; }
mkdir -p "$CAND"
cp "$ARTIFACT" evidence/build-metadata.json "$CAND/"
CAND_FILE="$CAND/$(basename "$ARTIFACT")"
CAND_SHA="$(sha256sum "$CAND_FILE" | awk '{print $1}')"
test "$CAND_SHA" = "$WORKSPACE_SHA"
mkdir -p "$REL"
cp "$CAND"/* "$REL/"
REL_FILE="$REL/$(basename "$ARTIFACT")"
REL_SHA="$(sha256sum "$REL_FILE" | awk '{print $1}')"
test "$REL_SHA" = "$CAND_SHA"
printf 'candidate=%s\nrelease=%s\nsha256=%s\n' "$CAND" "$REL" "$REL_SHA" > evidence/traceability.txt
5. Checkpoint Jenkinsfile
pipeline {
agent { label 'repo-lab' }
options { timestamps(); disableConcurrentBuilds(); buildDiscarder(logRotator(numToKeepStr: '20')) }
stages {
stage('Preflight') { steps { sh 'git rev-parse HEAD && java -version && python3 --version' } }
stage('Publish + verify + promote') { steps { sh 'bash ci/repository-checkpoint.sh' } }
stage('Negative overwrite test') {
steps {
sh '''set -euo pipefail
set +e
bash ci/repository-checkpoint.sh > evidence/second-run.txt 2>&1
rc=$?
set -e
printf 'second_run_exit=%s\n' "$rc" >> evidence/second-run.txt
test "$rc" -ne 0
'''
}
}
}
post { always { archiveArtifacts artifacts: 'evidence/**', fingerprint: true, allowEmptyArchive: true } }
}
The negative test is intentional. If it can overwrite the same coordinate, the checkpoint fails.
6. Required evidence packet
| Evidence | Capture |
|---|---|
| Controller/runtime | Jenkins LTS/core, Java, advisory date, repository plugin versions if installed |
| Build | Job full name, build number/URL/cause, source SHA, Jenkinsfile/library refs |
| Agent | Node/label/executor/workspace and tool versions |
| Artifact | Filename, size, workspace SHA-256 |
| Candidate | Exact coordinate and repository SHA-256 |
| Metadata | Job/build/source/digest mapping |
| Promotion | Source/target coordinate and actor/response if external |
| Release | Release SHA-256 equal to candidate SHA-256 |
| Negative test | Safe non-zero result for duplicate coordinate |
| Limitations | What local simulation does not prove about real Nexus/Artifactory auth, HA, retention or format metadata |
7. Verification checklist
- Job ran on
repo-lab, not the built-in node. - Source SHA was captured before artifact creation.
- Candidate coordinate is unique.
- Workspace SHA-256 equals candidate SHA-256.
- No build command runs between candidate verification and promotion.
- Release SHA-256 equals candidate SHA-256.
- Metadata ties release evidence to one job/build/source.
- Duplicate-coordinate attempt fails without deleting/modifying release.
- No real secret appears in logs or evidence.
8. Optional real-server extension
If you already have a disposable local Nexus or Artifactory lab, repeat the same identity chain with fake/scoped credentials. Nexus raw: unique PUT + GET + digest. Artifactory/JFrog: publish Build-Info and perform build promotion or copy/move, then verify digest. Cloud/paid services are not required.
9. Cleanup / rollback
set -euo pipefail
test -d lab-repo
case "$(pwd)" in /|/home|/var|/tmp) echo 'unsafe working directory' >&2; exit 70;; esac
rm -rf -- lab-repo dist
# Keep archived Jenkins evidence while reviewing the checkpoint.
Remove a disposable credential/job/agent only by exact ID/name after confirming no other lab uses it. Never delete repository objects by “latest.”
10. Claims and limits
| Supported claim | Not supported |
|---|---|
| Release bytes equal verified candidate bytes by SHA-256. | The artifact is vulnerability-free or safe for production. |
| Coordinates/metadata trace object to one Jenkins build/source SHA. | Every repository automatically preserves equivalent metadata. |
| The script refuses an existing coordinate. | All Nexus/Artifactory policies behave exactly like the local simulation. |
| This promotion did not rebuild. | Future promotion implementations will preserve bytes without independent verification. |
11. Production model and bridge to Chapter 33
You now have durable artifact identity and lifecycle: unique coordinates, least-privilege publication, build metadata, retrieval verification, promotion without rebuild and consumer traceability. Chapter 33 adds independent testing and quality evidence—JUnit, coverage, SonarQube, Selenium/JMeter and quality gates—without confusing tool execution, report ingestion and policy outcome.
Knowledge check
Answer before revealing the explanation.
1. Candidate and release coordinates differ but SHA-256 matches. Is that valid?
Yes. Promotion may change repository location/lifecycle state while preserving bytes; also verify actor/policy/metadata.
2. The duplicate-coordinate negative test exits 0. What next?
Fail the checkpoint and repair the no-overwrite guard. Preserve evidence; do not delete the release and rerun.
3. Why is metadata not a substitute for digest?
Metadata describes expected identity; the digest cryptographically identifies the actual bytes.
4. What bridges repository state to deployment traceability?
Release coordinates plus immutable digest and producer job/build/source metadata recorded again by the consumer/deployment.
Official references and version notes
Repository formats, Jenkins plugins, credentials models and promotion APIs evolve; prefer current primary documentation.
- Jenkins LTS changelog
- Jenkins Java Support Policy
- Jenkins Security Advisories
- Nexus Artifact Uploader plugin
- JFrog Jenkins plugin
- Sonatype Nexus Repository — Raw repositories
- Sonatype Nexus Repository — Components API
- Sonatype Nexus Repository — Uploading components
- JFrog Artifactory — Build-Info and build promotion
- JFrog — Jenkins integration
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.