Chapter 07Lesson 05~150 minutes

Checkpoint Lab — Build Parameters, Environment Variables, Build Causes, Schedules, Remote Triggers, and Parameterized Automation

Build a disposable maintenance simulation triggered manually, by hashed schedule, and through the authenticated API; prove causes and inputs, reject unsafe data, and demonstrate idempotent repeated execution.

CheckpointThree causesValidationIdempotencyEvidence packetCleanup

Learning objectives

  • Predict and verify cause, parameter, queue/build, agent, and target-state transitions for three trigger modes.
  • Reject an unsafe string parameter before it influences a path or side effect.
  • Use a dedicated API-token identity without weakening CSRF or exposing the token in a URL.
  • Demonstrate that repeating the same maintenance request creates a new Jenkins build but not a duplicate target transition.
  • Produce a reviewable evidence packet and clean up only the disposable resources created by the lab.

1. Mission and acceptance criteria

Create a Freestyle job named ch07-checkpoint on the disposable lab-linux agent. It simulates maintenance under /tmp/jenkins-ch07-checkpoint. You must produce three successful trigger modes—manual, timer, and authenticated API—plus one deliberately rejected unsafe input. Finally repeat a valid API request and prove that the simulated target changes only once.

Success is an evidence chain, not a green icon. For each run record cause, parameters, queue/build ID, node, validation result, Jenkins result, and target-side marker state.

2. Preflight and assumptions

  • Disposable Jenkins 2.568.3 LTS controller; Java 21 lab runtime.
  • Routine builds run on a disposable agent labelled lab-linux, not on the built-in controller node.
  • No real secret, repository, cloud account, or production endpoint is used.
  • A dedicated lab API user has only read/build access to this job and a disposable API token.
  • The exact target root is /tmp/jenkins-ch07-checkpoint.

Before configuring anything, write down two predictions: (1) a manual build and a timer build should show different Jenkins causes even with default parameters; (2) two API builds with the same maintenance key should create two Jenkins build records but only one marker transition.

3. Parameter contract

Create ch07-checkpoint, restrict it to lab-linux, keep concurrent builds disabled, and define:

Name Type Default / choices Validation
TARGET Choice dev, staging Allowlist again in shell.
MODE Choice inspect, mark-maintenance Allowlist again in shell.
MAINTENANCE_KEY String scheduled-smoke 1–24 characters: letters, digits, _, -.
DRY_RUN Boolean checked Mutation occurs only when false.

4. Build step: validation + idempotent target transition

set -eu
ROOT=/tmp/jenkins-ch07-checkpoint

case "$TARGET" in dev|staging) ;; *) exit 64 ;; esac
case "$MODE" in inspect|mark-maintenance) ;; *) exit 64 ;; esac
case "$MAINTENANCE_KEY" in
  ''|*[!A-Za-z0-9_-]*)
    printf 'Rejected MAINTENANCE_KEY\n' >&2
    exit 64
    ;;
esac
[ "${#MAINTENANCE_KEY}" -le 24 ] || { printf 'Key too long\n' >&2; exit 64; }

TARGET_DIR="$ROOT/$TARGET"
mkdir -p -- "$TARGET_DIR"
MARKER="$TARGET_DIR/$MAINTENANCE_KEY.done"

{
  printf 'job=%s\n' "$JOB_NAME"
  printf 'build=%s\n' "$BUILD_NUMBER"
  printf 'url=%s\n' "$BUILD_URL"
  printf 'node=%s\n' "$NODE_NAME"
  printf 'target=%s\nmode=%s\nkey=%s\ndry_run=%s\n' \
    "$TARGET" "$MODE" "$MAINTENANCE_KEY" "$DRY_RUN"
} | tee "$WORKSPACE/checkpoint-evidence.txt"

if [ "$MODE" = inspect ]; then
  find "$TARGET_DIR" -maxdepth 1 -type f -printf '%f\n' | sort || true
elif [ "$DRY_RUN" = true ]; then
  printf 'DRY RUN: would create %s\n' "$MARKER"
elif [ -f "$MARKER" ]; then
  printf 'IDEMPOTENT_NOOP marker=%s\n' "$MARKER"
else
  {
    printf 'key=%s\n' "$MAINTENANCE_KEY"
    printf 'first_build=%s\n' "$BUILD_NUMBER"
    printf 'created_utc=%s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  } > "$MARKER"
  printf 'CREATED marker=%s\n' "$MARKER"
fi

Archive checkpoint-evidence.txt. Do not archive the temporary target root as if it were a release artifact; it represents external target state in this simulation.

5. Run A — manual user cause

Start a manual build with TARGET=dev, MODE=inspect, MAINTENANCE_KEY=manual-check, and DRY_RUN=true. Capture the exact build number, cause, parameters, node, console result, and archived evidence. Confirm no marker was created for manual-check.

6. Run B — timer cause

Temporarily set Build periodically to:

H/2 * * * *

This aggressive cadence is acceptable only in the disposable lab and should be removed immediately after one observed timer run. Defaults keep the timer run in DRY_RUN=true, so it performs no target mutation. Record the timer cause and default parameter set. Then disable/remove the schedule so it cannot keep firing while you work.

7. Run C — authenticated API cause and bounded side effect

Use the disposable API user/token. Keep the token out of the URL and do not disable CSRF.

export JENKINS_URL='http://127.0.0.1:8080'
export JENKINS_USER='ch07-checkpoint-api'
read -r -s -p 'API token: ' JENKINS_API_TOKEN; export JENKINS_API_TOKEN; printf '\n'

curl --fail-with-body --silent --show-error --request POST \
  --user "$JENKINS_USER:$JENKINS_API_TOKEN" \
  --data-urlencode 'TARGET=dev' \
  --data-urlencode 'MODE=mark-maintenance' \
  --data-urlencode 'MAINTENANCE_KEY=change-0007' \
  --data-urlencode 'DRY_RUN=false' \
  "$JENKINS_URL/job/ch07-checkpoint/buildWithParameters"

Observe the queue/build transition, then verify the marker /tmp/jenkins-ch07-checkpoint/dev/change-0007.done. Record the marker contents and the Jenkins build that first created it.

8. Run D — repeat the same API request

Send the exact same valid request again. A new Jenkins build must exist, but the console should report IDEMPOTENT_NOOP and the marker's first_build value must remain unchanged. This proves that rerun identity and external transition identity are different concepts.

9. Run E — intentionally unsafe input

Submit MAINTENANCE_KEY=../../prod while keeping the rest of the request harmless. The build may be scheduled, but the validation step must fail before deriving a marker path or mutating the target. Preserve this failed build's cause, parameters, and first validation error.

curl --fail-with-body --silent --show-error --request POST \
  --user "$JENKINS_USER:$JENKINS_API_TOKEN" \
  --data-urlencode 'TARGET=dev' \
  --data-urlencode 'MODE=mark-maintenance' \
  --data-urlencode 'MAINTENANCE_KEY=../../prod' \
  --data-urlencode 'DRY_RUN=false' \
  "$JENKINS_URL/job/ch07-checkpoint/buildWithParameters"

Do not “fix” the evidence by deleting the failed build. It is the proof that invalid input was rejected.

10. Cause/input inspection for every run

For each known build number, query a narrow API view:

BUILD=1
curl --silent --show-error \
  --user "$JENKINS_USER:$JENKINS_API_TOKEN" \
  "$JENKINS_URL/job/ch07-checkpoint/$BUILD/api/json?tree=number,url,queueId,result,actions[causes[*],parameters[name,value]]"

Do not assume all cause objects export the same fields. Record the class/short description and any safe identity fields Jenkins actually returns.

11. Required evidence packet

Controller/runtime

Jenkins core version, Java version, lab date, agent label.

Job contract

Parameter definitions/defaults, timer specification, concurrency setting.

Run identities

Queue/build IDs, URLs, causes, parameter sets for Runs A–E.

Execution

Node/workspace, validation output, build result, archived checkpoint evidence.

Target state

Marker path/content, first-build identity, idempotent-noop evidence.

Security

API user scope, token not disclosed, CSRF left enabled, no real secrets.

Limitations

Disposable file marker simulates an external maintenance target; no production authorization model is implied.

12. Acceptance checklist

  • Manual and timer runs show different actual Jenkins causes.
  • API trigger uses Basic auth with a disposable API token; token never appears in the URL.
  • Unsafe key fails before target-path mutation.
  • First valid API run creates exactly one marker.
  • Repeated API run creates a new Jenkins build but leaves the marker unchanged.
  • All runs execute on lab-linux, not the controller built-in node.
  • Timer is removed after observation.
  • Evidence distinguishes Jenkins result from simulated target state.

13. Cleanup and rollback

First remove the timer and revoke the disposable API token. Preserve your evidence packet. Then clean only the guarded temporary root on the disposable agent:

ROOT=/tmp/jenkins-ch07-checkpoint
[ "$ROOT" = /tmp/jenkins-ch07-checkpoint ] || exit 70
find "$ROOT" -maxdepth 2 -type f -print 2>/dev/null || true
rm -rf -- "$ROOT"

Delete the lab job only after evidence is saved. Do not generalize this cleanup command to any other path.

14. What this chapter adds to a production Jenkins operating model

You can now treat trigger and input provenance as first-class operational evidence. A production-quality Jenkins job does not merely accept parameters: it defines ownership, validates unsafe values, attributes the request to a cause/identity, controls scheduling/concurrency, makes repeated execution safe, and verifies the external outcome separately.

Chapter 08 builds on this by making the toolchain itself reproducible—JDKs, Maven, Gradle, Node.js, Docker CLI, and the agent execution environment.

Next lesson

Chapter 08 — Reproducible Build Toolchains

Move from trustworthy inputs to trustworthy execution environments by controlling JDKs, build tools, Docker CLI versions, and agent toolchain state.

Knowledge check

What should differ between the manual and timer runs even if parameters match?

What proves idempotency in the repeated API run?

Why preserve the deliberately failed unsafe-input build?

What security setting must remain enabled throughout the API lab?

Why is the marker not treated as a Jenkins artifact?

What is the next reproducibility boundary after inputs?

Official references and version notes

Version and compatibility note

Rechecked on 2026-09-16. Examples target Jenkins 2.568.3 LTS, which is tested with Java 21 and 25; the disposable lab uses Java 21. The chapter intentionally relies on Jenkins core/Freestyle parameter, timer, build-cause, environment, and Remote API behavior so mandatory completion does not depend on a commercial service or a specialized trigger plugin. Exact cause payloads and environment variables can vary by job type and installed plugins, so inspect the controller's own .../api/ output rather than assuming a fixed payload.

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.