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.
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.
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
Jenkins core version, Java version, lab date, agent label.
Parameter definitions/defaults, timer specification, concurrency setting.
Queue/build IDs, URLs, causes, parameter sets for Runs A–E.
Node/workspace, validation output, build result, archived checkpoint evidence.
Marker path/content, first-build identity, idempotent-noop evidence.
API user scope, token not disclosed, CSRF left enabled, no real secrets.
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.
Knowledge check
What should differ between the manual and timer runs even if parameters match?
Their actual Jenkins causes. Cause provenance is independent of the parameter set.
What proves idempotency in the repeated API run?
A new Jenkins build exists, but the original marker remains unchanged and the second build reports a no-op.
Why preserve the deliberately failed unsafe-input build?
It is evidence that validation rejected the input before a side effect, which is more valuable than deleting the failure.
What security setting must remain enabled throughout the API lab?
CSRF protection. API-token-authenticated requests do not require disabling it.
Why is the marker not treated as a Jenkins artifact?
It models external target state. Jenkins build artifacts are retained build evidence, while target-side state must be verified separately.
What is the next reproducibility boundary after inputs?
The actual build toolchain and agent execution environment, covered in Chapter 08.
Official references and version notes
- Jenkins LTS changelog — current LTS release and tested Java configurations.
-
Pipeline Syntax
— parameter, environment, trigger, cron, and
triggeredBysemantics. -
Using a Jenkinsfile
—
env, build identity variables, and parameter access. - Using environment variables — global and stage-scoped environment behavior.
- Handling Environment Variables — security risks from untrusted environment names and values.
- Remote Access API — authenticated build submission and programmatic inspection.
- CSRF Protection — crumbs for scripted clients and the API-token exemption.
-
Jenkins
CauseAPI — the build-cause model used by Jenkins core.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.