Checkpoint Lab — Webhooks, Jenkins and CI Integrations, Maven Deployments, Build Promotion, and Event Automation
Integrate the chapter into one evidence-driven checkpoint: build once, publish once, observe an event, promote the same bytes, prove identity, then remove only the disposable resources created by the lab.
Learning objectives
- Build one synthetic artifact and preserve source/build/checksum evidence before publication.
- Publish through a least-privilege service identity and independently verify Nexus component/asset state.
- Validate a repository event with HMAC and deduplicate its delivery UUID, using a signed fixture when live webhook capability is unavailable.
- Promote the exact published bytes to a second hosted repository without compiling again.
- Create a complete evidence packet, verify negative authorization tests, and tear down only lab-owned resources.
com.example coordinates, and disposable service identities.1. Checkpoint scenario and success contract
You operate a small internal service that produces com.example:learner-ch21:1.0.0. The build stage may publish only to learner-ch21-build. An event receiver records repository changes. A release controller may promote only an artifact whose published SHA-256 matches the build manifest. The destination is learner-ch21-release. No source rebuild is allowed after the manifest is frozen.
flowchart TD
COMMIT[Source commit] --> BUILD[Build once]
BUILD --> MAN[Manifest: GAV + SHA-256 + build ID]
BUILD --> DEV[learner-ch21-build]
DEV --> EVENT[Signed event / signed fixture]
EVENT --> RX[Validate + dedupe]
RX --> READ[Read Nexus state]
READ --> GATE{Matches manifest?}
GATE -->|yes| PROMOTE[Transfer same bytes]
GATE -->|no| STOP[Stop + diagnose]
PROMOTE --> REL[learner-ch21-release]
REL --> VERIFY[Re-download + SHA-256]2. Preflight and exact assumptions
| Assumption | Checkpoint requirement |
|---|---|
| Nexus | Disposable self-hosted 3.95.2-01 reference; verify local exact version before execution |
| Runtime | Java 21; official bundles include a recommended JVM in current releases |
| Database/blob | H2 + file blob store allowed only for the small archive-installation lab; not a production recommendation |
| Edition | Community mandatory path; Pro Staging/Tagging optional only |
| Network | Nexus and receiver loopback/private; no public endpoint |
| Maven | Current supported local Maven; record mvn --version and avoid global corporate settings |
| Identity | Disposable svc-learner-ch21 with add/read/browse only on lab repositories |
3. Required predictions before execution
- After initial deployment, database/component metadata and blob assets will appear in
learner-ch21-build, butlearner-ch21-releasewill remain empty. - After promotion, the destination repository will gain its own repository metadata/assets while the source artifact remains byte-identical; source compilation/build state will not change.
- A duplicate webhook delivery UUID will not produce a second promotion.
- The service identity will be able to upload/read the two lab repositories but will receive denial for repository/security administration.
4. Setup checklist
- Create/verify the two disposable Maven hosted release repositories on a lab blob store.
- Create a narrow service role/user; record its privilege IDs without secrets.
- Create a temporary Maven local repository and settings file for isolation.
- Start the loopback webhook receiver from Lesson 2.
- If the repository webhook capability exists, configure it with a disposable secret; otherwise prepare the signed fixture path.
- Create a temporary source directory containing the synthetic POM and one harmless class.
5. Build once and create the immutable handoff manifest
export BUILD_ID='local-ch21-001'
export SOURCE_COMMIT='synthetic-no-real-repo'
mvn -B -Dmaven.repo.local=/tmp/ch21-m2 clean package
ARTIFACT='target/learner-ch21-1.0.0.jar'
SHA256=$(sha256sum "$ARTIFACT" | cut -d' ' -f1)
cat > /tmp/ch21-manifest.txt <
This manifest contains no credential. Once created, do not run a build goal again in this checkpoint.
6. Publish and prove repository state
Deploy with the temporary Maven settings whose server ID matches learner-ch21-build. Capture the client HTTP outcome and then verify independently from Nexus: component/asset listing or Browse, repository path, downloaded JAR, and SHA-256.
mvn -B -s "$CH21_SETTINGS" -Dmaven.repo.local=/tmp/ch21-m2 deploy -DskipTests
curl --fail --silent --show-error -u "$NEXUS_SERVICE_USER:$NEXUS_CH21_PASSWORD" -o /tmp/ch21-published.jar "$NEXUS_URL/repository/learner-ch21-build/com/example/learner-ch21/1.0.0/learner-ch21-1.0.0.jar"
test "$SHA256" = "$(sha256sum /tmp/ch21-published.jar | cut -d' ' -f1)"7. Negative authorization test
Using the same service identity, attempt one harmless read-only control-plane endpoint that requires administration or a repository-creation action that you know should be denied. Preserve only HTTP status/path, not credentials. Expected result is 403 (or another documented denial according to endpoint/auth state). If it succeeds, stop and narrow the role before continuing.
8. Event gate: authenticate and deduplicate
Trigger a repository event through the live capability when available, or replay the signed fixture. Record X-Nexus-Webhook-Id, X-Nexus-Webhook-Delivery, signature validation result, expected repository, and event timestamp. Send the exact same delivery a second time; the receiver/controller must report it as duplicate and perform no second promotion.
9. Controller gate: trust Nexus state, not payload claims
The controller extracts/derives the expected coordinate from its own workflow state, downloads the artifact from learner-ch21-build, computes SHA-256, and compares it to the manifest. Only equality opens the promotion gate. A valid webhook with a different coordinate or checksum is rejected and investigated.
10. Promote the same bytes
Use the already-downloaded JAR and POM. Do not invoke clean, compile, package, or another source build. Publish the existing files to learner-ch21-release with a destination-scoped credential context, or use native Pro Staging/Move only as an optional extension on an eligible instance.
# Guard: compare before transfer
EXPECTED=$(awk -F= '$1=="sha256" {print $2}' /tmp/ch21-manifest.txt)
test "$EXPECTED" = "$(sha256sum /tmp/ch21-published.jar | cut -d' ' -f1)"
# Transfer existing files; no build goals.
mvn -B -s "$CH21_RELEASE_SETTINGS" deploy:deploy-file -DrepositoryId=learner-ch21-release -Durl="$NEXUS_URL/repository/learner-ch21-release/" -Dfile=/tmp/ch21-published.jar -DpomFile=/tmp/ch21-published.pom11. Independent destination verification
curl --fail --silent --show-error -u "$NEXUS_SERVICE_USER:$NEXUS_CH21_PASSWORD" -o /tmp/ch21-promoted.jar "$NEXUS_URL/repository/learner-ch21-release/com/example/learner-ch21/1.0.0/learner-ch21-1.0.0.jar"
PROMOTED=$(sha256sum /tmp/ch21-promoted.jar | cut -d' ' -f1)
test "$EXPECTED" = "$PROMOTED"
printf 'promotion_verified sha256=%s
' "$PROMOTED"
Also verify repository/component/asset state shows the coordinate in both expected locations and no unexpected SNAPSHOT/alternate version exists.
12. Inject one safe failure and diagnose it
Change the temporary release settings.xml server ID to a wrong value, then perform a non-destructive authentication/read or deployment attempt against a fresh disposable version such as 1.0.1-failure only if your repository policy permits. Capture the failure, explain the ID mapping problem, restore the correct temporary settings, and do not widen Nexus privileges.
13. Required evidence packet
| Evidence | What it proves |
|---|---|
| Version/edition/runtime + repository config | Compatibility and target scope |
| Source/build manifest without secrets | Build identity and expected checksum |
| Maven server/repository IDs, redacted | Correct client credential mapping |
| Service-role privilege matrix + negative test | Least privilege |
| Build repository component/assets + downloaded SHA-256 | Successful governed publication |
| Webhook event ID/delivery UUID + HMAC result | Authenticated, deduplicated event handling |
| Promotion command showing no build goal | Build-once handoff |
| Release repository re-download SHA-256 | Byte identity preserved |
| Cleanup record | Lab-owned state removed through supported mechanisms |
14. Cleanup / rollback
learner-ch21- and the base URL is the local lab before deletion.- Disable/delete the Chapter 21 webhook capability if created.
- Delete the disposable service user/role after recording non-secret privilege evidence.
- Delete
learner-ch21-buildandlearner-ch21-releasethrough Nexus UI/REST only. - Remove temporary Maven settings, temporary local repository, receiver process, and synthetic source directory.
- Do not delete blob files or database rows directly. Physical reclamation follows the supported cleanup/compaction semantics from Chapters 18–19.
15. Verification checklist
□ Exact coordinate/version published. □ Original, source-repository, and destination-repository SHA-256 values match. □ No rebuild after manifest freeze. □ Service identity denied administration. □ Event HMAC validated or fixture path explicitly labeled. □ Duplicate delivery no-op proven. □ No real secret in files/logs/evidence. □ Community/Pro boundary stated correctly. □ Cleanup limited to lab-owned resources.
16. Knowledge check
What is the strongest evidence that the release artifact is the same artifact that CI published?
A matching cryptographic checksum/digest for the original build output, the Nexus source copy, and the promoted destination copy, tied to the same coordinate and build manifest.
Why does the controller re-read Nexus after receiving a valid webhook?
The event is a notification. Re-reading authoritative repository state verifies the current component/assets and expected identity before action.
A duplicate delivery causes a second upload but produces the same bytes. Is the receiver idempotent?
No. Idempotence means the repeated event should not repeat the state-changing action unnecessarily.
What should you do if the live Community instance does not expose the repository webhook capability?
Use the signed local fixture path and label it as a simulation; do not require a paid/unsupported feature for mandatory learning.
What concept from this chapter carries into Chapter 22?
Artifact identity and event evidence become inputs to policy/component-intelligence decisions; storage/publication and policy evaluation remain distinct trust boundaries.
17. Chapter checkpoint summary
You have a production-grade mental model for the CI/Nexus handoff: build once, publish with a scoped noninteractive identity, authenticate and deduplicate events, re-read authoritative repository state, promote the exact bytes, and preserve evidence across every step. Chapter 22 adds Sonatype IQ/component-intelligence and policy evaluation without changing these repository identity boundaries.
Official references and version notes
- Sonatype: Webhooks — repository/global webhook purpose and capability model.
- Sonatype: Enabling a Repository Webhook Capability — repository-scoped event configuration and shared-secret HMAC behavior.
- Sonatype: Secure Webhook Deliveries — HMAC-SHA1 verification using
X-Nexus-Webhook-Signature. - Sonatype: Example Headers and Payloads — event ID and unique delivery UUID used for receiver routing/idempotence.
- Sonatype: Platform Plugin for Jenkins — Nexus Repository — optional Jenkins publishing integration and Maven release behavior.
- Sonatype: Staging — current Pro-only staging/build-promotion model.
- Sonatype: Nexus Repository Maven Plugin — Pro staging-oriented Maven integration and current Java requirements for plugin versions.
- Sonatype: Self-Hosted Feature Matrix — current Community versus Pro entitlement boundaries.
- Sonatype: System Requirements — Java 21, H2/PostgreSQL, storage, and deployment constraints.
- Apache Maven: Security and Deployment Settings —
distributionManagementtarget and matchingsettings.xmlserver ID/credentials. - Apache Maven: Settings Reference — server credentials and client-local configuration boundary.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.