Freestyle Projects, Build Steps, Post-Build Actions, Parameters, Triggers, and Legacy Job Maintenance: Guided Hands-On Workflow and Core Operations
Build and inspect a disposable parameterized Freestyle project, trigger it manually and by timer, archive evidence, compare causes, and map the behavior toward Pipeline.
Learning objectives
- Create a disposable Freestyle project with safe non-secret parameters and an explicit agent label.
- Generate deterministic build evidence without relying on workspace leftovers.
- Archive and fingerprint artifacts separately from the build step that creates them.
- Compare manual and timer build causes through Jenkins build history and the Remote API.
- Export a sanitized job configuration and map the behavior into a Pipeline migration sketch.
1. Lab scenario and safety boundary
You will create one disposable Freestyle project named
legacy-demo. It will run only on an existing disposable
agent labeled linux, accept non-secret parameters,
generate a small evidence file, archive that file, and be triggered
once manually and once by a temporary timer. Nothing is published
outside Jenkins.
2. Preflight: prove the controller and agent state
Before creating the job, record the controller version and verify
that at least one agent carrying the linux label is
online. Do not proceed by enabling a controller executor as a
shortcut.
# Controller identity (read-only)
curl -fsSI "$JENKINS_URL/login" | grep -i '^X-Jenkins:' || true
# API inspection with a synthetic lab account
curl -fsS --user "$JENKINS_USER:$JENKINS_API_TOKEN" "$JENKINS_URL/computer/api/json?tree=computer[displayName,offline,numExecutors,assignedLabels[name]]"
Expected evidence: X-Jenkins: 2.568.3 for this chapter
baseline, built-in node executor count 0, and an online agent whose
labels include linux. If the controller has moved to a
newer supported LTS by the time you run the lab, use that exact
version and update the evidence packet.
3. Create the Freestyle project
- From the Jenkins dashboard choose New Item.
-
Name the project
legacy-demo, choose Freestyle project, then create it. - Enable This project is parameterized.
-
Add a string parameter
MESSAGEwith defaulthello-from-freestyle. -
Add a choice parameter
TARGET_ENVwith onlydevandtest. -
Add a string parameter
SOURCE_SHAwhose lab default is1111111111111111111111111111111111111111. This is synthetic provenance, not an SCM checkout. -
Enable Restrict where this project can be run and
enter
linux.
The synthetic SOURCE_SHA is intentionally imperfect. It
teaches that a human-supplied revision label is weaker evidence than
an SCM plugin actually checking out and recording an immutable
commit. Chapter 06 fixes that boundary.
4. Add a deterministic build step
Add Execute shell and use a script that validates the control parameter, recreates its output directory, records Jenkins build identity, and hashes the evidence file. The script never executes parameter text as a command.
set -eu
case "$TARGET_ENV" in
dev|test) ;;
*) echo "Unsupported TARGET_ENV=$TARGET_ENV" >&2; exit 2 ;;
esac
rm -rf out
mkdir -p out
{{
printf 'job=%s\n' "$JOB_NAME"
printf 'build=%s\n' "$BUILD_NUMBER"
printf 'node=%s\n' "$NODE_NAME"
printf 'workspace=%s\n' "$WORKSPACE"
printf 'target_env=%s\n' "$TARGET_ENV"
printf 'source_sha=%s\n' "$SOURCE_SHA"
printf 'message=%s\n' "$MESSAGE"
}} > out/build-evidence.txt
sha256sum out/build-evidence.txt > out/build-evidence.sha256
cat out/build-evidence.txt
cat out/build-evidence.sha256
Recreating out/ at the start is important. A legacy job
that succeeds only because a previous build left files behind is not
reproducible.
5. Archive the output as a post-build action
Add the core Archive the artifacts post-build
action. Set the pattern to out/* and enable
fingerprinting if the UI offers the option in your current
core/plugin baseline. Save the job.
At this point there are two separate configured behaviors: the shell step produces files in the workspace, and the artifact archiver copies matching files into the build record. A missing file should be diagnosed at the producer before weakening the archive pattern.
6. Trigger a manual parameterized build
Choose Build with Parameters, keep
TARGET_ENV=dev, change MESSAGE to
manual-run, and start the build. Open the resulting
build page and preserve:
- build number and URL;
- build cause showing the user/manual origin;
- agent/node and workspace path;
- console output;
- archived
build-evidence.txtand checksum; - parameter values that are not secret.
Download the archived evidence and verify its checksum outside the workspace if practical. This proves the retained artifact matches the generated bytes; it does not prove an external deployment because there is none in this lab.
7. Add one temporary timer trigger
Return to Configure → Build Triggers, enable Build periodically, and temporarily use:
H/5 * * * *
The H token spreads jobs across the interval instead of
synchronizing all controllers at minute zero. Wait for one
timer-caused build, inspect its cause, then immediately disable the
periodic trigger and save the job. This bounded step avoids leaving
a noisy recurring schedule behind.
8. Compare run causes through the API
curl -fsS --user "$JENKINS_USER:$JENKINS_API_TOKEN" "$JENKINS_URL/job/legacy-demo/api/json?tree=builds[number,result,url,actions[causes[*]],actions[parameters[name,value]]]" > legacy-demo.builds.json
Compare the manual run and timer run. They share the same job configuration but have different causes and may have different parameter values. Keep the API token out of logs and course artifacts; only the JSON response belongs in the evidence packet after review.
9. Export and compare the job configuration
Fetch a read-only copy of config.xml, disable the timer
if it is still enabled, fetch another copy, and compare them. This
demonstrates that a UI checkbox is durable controller-side
configuration.
curl -fsS --user "$JENKINS_USER:$JENKINS_API_TOKEN" "$JENKINS_URL/job/legacy-demo/config.xml" > config.after-lab.xml
# Review before saving elsewhere; plugin configuration can reveal implementation details.
git diff --no-index -- config.before.xml config.after-lab.xml || true
If you did not capture config.before.xml, repeat the
exercise with one harmless change such as the job description. The
point is to connect a UI mutation to persisted configuration, not to
hand-edit internal XML while Jenkins is running.
10. Translate behavior into a Pipeline sketch
Do not migrate the job yet. Instead, describe how the behavior would
map to Pipeline concepts: parameters become a
parameters block, the linux restriction
becomes an agent label, the shell becomes an sh step,
archiving becomes archiveArtifacts, and the timer
becomes a cron trigger. This behavior inventory is more
valuable than a mechanical XML-to-Jenkinsfile conversion.
// Migration sketch only — Chapter 09+ teaches Pipeline in depth.
pipeline {
agent { label 'linux' }
parameters {
string(name: 'MESSAGE', defaultValue: 'hello-from-pipeline')
choice(name: 'TARGET_ENV', choices: ['dev', 'test'])
}
stages {
stage('Build evidence') {
steps { sh './ci/build-evidence.sh' }
}
}
post {
success { archiveArtifacts artifacts: 'out/*', fingerprint: true }
}
}
The sketch deliberately moves the shell body into a versioned script. A migration should reduce hidden controller-side behavior, not merely recreate it in a larger inline Groovy file.
11. Verification and cleanup
- Confirm the timer trigger is disabled.
- Confirm the two observed causes are preserved in build history.
-
Confirm archived files are available from the build record even
after deleting
out/from the workspace. - Save a sanitized configuration snapshot and migration sketch.
-
Delete
legacy-demoonly if it was created solely for this lab, and record that cleanup in the evidence packet.
Knowledge check
Why does the lab delete and recreate out/ before
producing files?
To prove the build does not depend on leftovers from an earlier workspace state.
Why are the manual and timer runs useful to compare?
They execute the same saved job configuration with different causes, proving trigger state and job definition are distinct.
Does archiving out/* prove an external deployment
succeeded?
No. It only proves matching files were retained by Jenkins for that build.
Why is the synthetic SOURCE_SHA intentionally
called weak provenance?
It is a user-supplied label, not proof that Jenkins actually checked out that commit. Chapter 06 adds SCM evidence.
What should happen to the H/5 * * * * timer after
the observation?
Disable it immediately; it is a bounded lab mechanism, not a production cadence recommendation.
Official references and version notes
- Jenkins LTS changelog — current LTS release and tested Java configurations.
- Working with projects — current project/job types, including Freestyle.
- Controller Isolation — why routine builds should execute on agents instead of the built-in node.
- Handling Environment Variables — security implications of build parameters and environment values.
- Remote Access API — build triggering and read-only evidence retrieval.
- Pipeline — first-class Jenkins model for versioned delivery workflows and the migration target used in this chapter.
Rechecked on 2026-09-15. The current Jenkins LTS baseline used by this chapter is 2.568.3, tested on Java 21 and 25. Mandatory labs assume Java 21 and use only core Freestyle/parameter/timer/artifact capabilities unless your controller already requires additional dependencies. Builds must execute on a disposable agent rather than the built-in node. If a future Jenkins LTS or plugin baseline differs, revalidate behavior before copying these exact steps.
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.