Chapter 05Lesson 02~120 minutes

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.

Freestyle labParametersTimer triggerArtifactsMigration sketch

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.

Assumptions: Jenkins 2.568.3 LTS; Java 21/25 supported; lab examples assume Java 21 on the controller and a disposable agent labeled linux. The built-in node has zero executors. The lab uses a synthetic account and local/disposable controller only. If your exact agent or image differs, record its identity instead of pretending the environment is identical.

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

  1. From the Jenkins dashboard choose New Item.
  2. Name the project legacy-demo, choose Freestyle project, then create it.
  3. Enable This project is parameterized.
  4. Add a string parameter MESSAGE with default hello-from-freestyle.
  5. Add a choice parameter TARGET_ENV with only dev and test.
  6. Add a string parameter SOURCE_SHA whose lab default is 1111111111111111111111111111111111111111. This is synthetic provenance, not an SCM checkout.
  7. 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.txt and 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.

Do not use timer frequency as a production default. The five-minute schedule exists only so a learner can observe a timer cause quickly on a disposable controller. Production cadence should match workload needs and capacity.

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-demo only if it was created solely for this lab, and record that cleanup in the evidence packet.
Next lesson

Configuration, Design Choices, and Tradeoffs

Evaluate whether a legacy Freestyle job should remain controller-configured, move into SCM, be decomposed, or be migrated to Pipeline, with explicit plugin and rollback constraints.

Knowledge check

Why does the lab delete and recreate out/ before producing files?

Why are the manual and timer runs useful to compare?

Does archiving out/* prove an external deployment succeeded?

Why is the synthetic SOURCE_SHA intentionally called weak provenance?

What should happen to the H/5 * * * * timer after the observation?

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.