Source Control Integration, Git Plugin, Credentials, Polling, Webhooks, and Multirepository Checkout: Guided Hands-On Workflow and Core Operations
Use a disposable synthetic Git service to perform Freestyle and Pipeline checkouts, prove immutable source identity, compare polling with token-protected notification, and isolate a second repository.
Learning objectives
- Create two synthetic repositories without using production source or real credentials.
- Checkout source through Freestyle and minimal Pipeline jobs and verify the exact workspace SHA.
- Compare scheduled SCM polling with a token-protected Git plugin notification.
- Capture repository, cause, plugin/tool, agent, and changelog evidence before changing configuration.
- Checkout a second repository into a bounded directory and record a separate provenance identity.
1. Lab boundary and assumptions
This workflow uses a synthetic Git service reachable only inside a disposable lab network or disposable VM. The mandatory path requires no real SCM account and no real secret. If you already have the Chapter 05 disposable controller and agent, reuse them; otherwise create equivalent isolated resources. Routine builds remain off the built-in node.
- Jenkins core: 2.568.3 LTS, Java 21 or 25; examples assume Java 21.
- Git plugin: 5.10.1; Git Client: 6.6.1; Credentials: 1511.v2e3cb_0008ef0.
-
A disposable agent labeled
linuxwith command-line Git installed. -
A synthetic Git endpoint reachable by both Jenkins polling logic
and the build agent. For same-host process labs,
git://127.0.0.1:9418is acceptable; container labs should use a dedicated lab network hostname such asscm-lab. - No production repository, no real token, and no public webhook endpoint.
127.0.0.1 points to different
containers. Use a dedicated disposable network service name instead.
Do not work around connectivity by exposing Jenkins or the Git
service to the public internet.
2. Seed two synthetic repositories
On the disposable Git-service host, create two working repositories and bare remotes. The first represents application source; the second represents shared documentation/configuration. The writer workspace is outside Jenkins so you can create controlled commits and watch Jenkins react.
set -eu
LAB_ROOT="$HOME/jenkins-scm-lab"
rm -rf "$LAB_ROOT"
mkdir -p "$LAB_ROOT"/{writer,remotes}
seed_repo() {
name="$1"
mkdir -p "$LAB_ROOT/writer/$name"
git -C "$LAB_ROOT/writer/$name" init -b main
git -C "$LAB_ROOT/writer/$name" config user.name "SCM Lab Writer"
git -C "$LAB_ROOT/writer/$name" config user.email "scm-lab@example.invalid"
printf '%s\n' "$name version=1" > "$LAB_ROOT/writer/$name/README.txt"
git -C "$LAB_ROOT/writer/$name" add README.txt
git -C "$LAB_ROOT/writer/$name" commit -m "seed $name"
git init --bare "$LAB_ROOT/remotes/$name.git"
git -C "$LAB_ROOT/writer/$name" remote add origin "$LAB_ROOT/remotes/$name.git"
git -C "$LAB_ROOT/writer/$name" push -u origin main
}
seed_repo app
seed_repo docs
printf 'app=%s\n' "$(git -C "$LAB_ROOT/writer/app" rev-parse HEAD)"
printf 'docs=%s\n' "$(git -C "$LAB_ROOT/writer/docs" rev-parse HEAD)"
Start a lab-only Git daemon bound to the disposable interface. On a same-host lab:
git daemon --reuseaddr --export-all --base-path="$LAB_ROOT/remotes" --listen=127.0.0.1 --port=9418 "$LAB_ROOT/remotes"
Leave this terminal open. In a container lab, run an equivalent dedicated Git service on the lab bridge network and use its internal hostname. The daemon path here is intentionally read-only from Jenkins; controlled source changes are pushed from the writer workspace into the bare repositories.
3. Preflight before creating the Jenkins job
From the Jenkins agent context, verify connectivity and Git identity without cloning yet. Use the hostname appropriate to your lab.
APP_URL="git://127.0.0.1:9418/app.git"
DOCS_URL="git://127.0.0.1:9418/docs.git"
git --version
git ls-remote "$APP_URL" refs/heads/main
git ls-remote "$DOCS_URL" refs/heads/main
The output gives a remote SHA for each main branch.
Record those values as pre-build observations, not as proof
of what a later Jenkins build checked out. A commit can change
between preflight and checkout; the workspace SHA after checkout is
the evidence that matters.
4. Freestyle checkout: configuration and evidence
Create a disposable Freestyle project named
scm-freestyle-lab. Restrict it to label
linux. Under Source Code Management choose Git, enter
the synthetic APP_URL, leave Credentials at
none, and set the branch specifier to */main.
The no-credential choice is intentional evidence: this repository is
public only inside the lab and the job has no secret authority.
Add an Execute shell build step:
set -eu
printf 'job=%s build=%s node=%s workspace=%s\n' "$JOB_NAME" "$BUILD_NUMBER" "$NODE_NAME" "$WORKSPACE"
printf 'configured_git_commit=%s\n' "${GIT_COMMIT:-unset}"
printf 'remote=%s\n' "$(git remote get-url origin)"
printf 'head=%s\n' "$(git rev-parse HEAD)"
printf 'branch=%s\n' "$(git symbolic-ref --short -q HEAD || printf detached)"
printf 'git=%s\n' "$(git --version)"
git show -s --format='commit=%H%ncommit_time=%cI%nsubject=%s' HEAD
mkdir -p evidence
git rev-parse HEAD > evidence/app.sha256-input.txt
git remote get-url origin > evidence/app.remote.txt
Archive evidence/* as build artifacts. After the first
run, compare GIT_COMMIT with
git rev-parse HEAD. They should identify the same
checked-out commit. If they do not, preserve the console log and
configuration before changing anything.
5. Create a controlled source change
From the writer workspace, commit and push one change. Capture the new SHA before asking Jenkins to evaluate it.
LAB_ROOT="$HOME/jenkins-scm-lab"
printf '%s\n' "app version=2" > "$LAB_ROOT/writer/app/README.txt"
git -C "$LAB_ROOT/writer/app" add README.txt
git -C "$LAB_ROOT/writer/app" commit -m "app: controlled SCM change"
git -C "$LAB_ROOT/writer/app" push origin main
NEW_APP_SHA="$(git -C "$LAB_ROOT/writer/app" rev-parse HEAD)"
printf 'new_app_sha=%s\n' "$NEW_APP_SHA"
Do not click Build Now yet. The point is to compare Jenkins' SCM-trigger mechanisms against a known repository state change.
6. Observe polling without creating an unbounded schedule
Enable Poll SCM on the Freestyle project. For a bounded
observation you may use a temporary schedule such as
H/5 * * * *, then disable it after one observed polling
cycle. Record the polling log and whether Jenkins schedules a build.
A poll is an evaluation activity; the resulting build is a separate
queue/run state.
If waiting for the schedule is impractical, keep Poll SCM enabled with no schedule and use the token-protected notification in the next section. The Git plugin can use that notification to invoke the polling logic immediately.
7. Simulate a local webhook with token-protected
notifyCommit
On the disposable controller, create a Git plugin notifyCommit access token through the current security configuration. Store the value locally as an environment variable; do not place it in the job, console log, shell history, screenshot, or evidence archive. Keep the plugin's default token protection enabled.
export JENKINS_URL='http://127.0.0.1:8080'
export APP_URL='git://127.0.0.1:9418/app.git'
TOKEN_FILE="$HOME/.jenkins-scm-lab-notify-token"
umask 077
read -rsp 'Lab notifyCommit token: ' token; printf '\n'
printf '%s' "$token" > "$TOKEN_FILE"
unset token
curl --fail --get --data-urlencode "url=$APP_URL" --data-urlencode "token@$TOKEN_FILE" "$JENKINS_URL/git/notifyCommit"
rm -f "$TOKEN_FILE"
A successful notification should cause Jenkins to poll matching jobs that have Poll SCM enabled. If the repository has a new commit that qualifies, Jenkins then schedules a build. Capture the returned project list, polling log, queue/build cause, and resulting workspace SHA. Redact the token from all retained evidence.
8. Perform the same checkout in a minimal Pipeline preview
Pipeline is taught in depth later; here it is used only to compare
SCM evidence. Create a disposable Pipeline job
scm-pipeline-lab and run it on the
linux agent. Use the explicit checkout +
scmGit form because it exposes repository and branch
intent clearly.
node('linux') {
stage('Checkout app') {
deleteDir()
def scmVars = checkout scmGit(
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'git://127.0.0.1:9418/app.git']]
)
echo "plugin_commit=${scmVars.GIT_COMMIT}"
sh '''
set -eu
printf 'remote=%s\n' "$(git remote get-url origin)"
printf 'head=%s\n' "$(git rev-parse HEAD)"
git show -s --format='commit=%H commit_time=%cI subject=%s' HEAD
'''
}
}
The returned scmVars.GIT_COMMIT and workspace
git rev-parse HEAD should agree. This is stronger
evidence than a branch label because both point to an immutable Git
object.
9. Checkout a second repository into a bounded directory
Extend the Pipeline preview so application and documentation sources cannot overwrite each other. Capture each identity independently.
node('linux') {
deleteDir()
dir('app') {
checkout scmGit(
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'git://127.0.0.1:9418/app.git']]
)
sh "git remote get-url origin > ../app.remote && git rev-parse HEAD > ../app.sha"
}
dir('docs') {
checkout scmGit(
branches: [[name: '*/main']],
userRemoteConfigs: [[url: 'git://127.0.0.1:9418/docs.git']]
)
sh "git remote get-url origin > ../docs.remote && git rev-parse HEAD > ../docs.sha"
}
sh '''
printf 'app repo=%s sha=%s\n' "$(cat app.remote)" "$(cat app.sha)"
printf 'docs repo=%s sha=%s\n' "$(cat docs.remote)" "$(cat docs.sha)"
'''
}
Do not use the same directory for two repositories unless replacement is intentional and explicitly cleaned. Separate directories make provenance, cleanup, and trust boundaries reviewable.
10. Credential scope exercise without a real secret
The mandatory local Git daemon needs no authentication. Use that fact to prove a valuable negative: both lab SCM configurations should have no credential selected. Then inspect the Jenkins credential UI from Chapter 04 and answer where a repository-specific credential would live if this were a protected remote. Do not create or export a real token merely to satisfy the exercise.
Optional extension: on a fully disposable local Git service that supports authentication, create a fake lab-only account and folder-scoped credential. Verify that only the intended folder/job can select it. Do not substitute a personal GitHub/GitLab credential for this exercise.
11. Verification and cleanup
- Disable any temporary Poll SCM schedule.
- Preserve build numbers, causes, polling logs, repository URLs, and exact SHAs for the observed runs.
-
Verify
scm-freestyle-labandscm-pipeline-labused thelinuxagent, not the built-in node. - Stop the disposable Git daemon/service.
-
Delete only the synthetic
jenkins-scm-labdirectory and lab jobs after confirming their exact identities. - Do not delete shared plugins or credentials as “cleanup.”
Knowledge check
Why does the mandatory lab use no SCM credential?
The synthetic repository is intentionally unauthenticated inside a disposable lab, so the safest credential is no credential at all.
What is the key evidence after a checkout?
The repository URL plus the immutable workspace commit SHA, correlated with job/build/agent identity.
Why keep Poll SCM enabled for notifyCommit even
with no schedule?
The Git plugin notification triggers the polling logic for matching jobs; polling then decides whether a change merits a build.
Why should app and docs repositories use separate directories?
To prevent one checkout from overwriting the other and to preserve independent source identities and trust boundaries.
May the lab disable notifyCommit access control to simplify curl?
No. Keep token protection enabled and use a disposable local token that is never retained in logs or artifacts.
Official references and version notes
- Jenkins LTS changelog — current LTS release and tested Java configurations.
- Git plugin — repositories, credentials, polling, push notifications, checkout behavior, environment variables, and plugin security notes.
- Git Client plugin — command-line/JGit implementations and SSH host-key verification strategies.
-
scmGitPipeline reference — explicit Git checkout configuration and supported checkout capabilities. - Credentials security — limiting credential access and protecting secrets.
- Using credentials — credential kinds, stores, scope, and Jenkins usage.
- Controller Isolation — why routine checkout/build execution belongs on agents.
- Using a Jenkinsfile — Pipeline checkout and credential-handling patterns.
Rechecked on 2026-09-15. Chapter examples use Jenkins 2.568.3 LTS (tested with Java 21 and 25), Git plugin 5.10.1, Git Client plugin 6.6.1, and Credentials plugin 1511.v2e3cb_0008ef0. The mandatory path assumes a disposable Java 21 controller/agent lab and records the actual command-line Git version from the agent rather than freezing a universal Git binary version. Git/credentials/plugin behavior and security advisories evolve; re-check primary Jenkins/plugin documentation before reusing these exact versions or settings.
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.