1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
|
# Building a C++ App in Jenkins (Docker Agent)
Step-by-step guide to set up a Jenkins pipeline that builds a C++ project
using a Docker container as the build agent (DooD).
## 1. Push your C++ project to the git server
On the server, create a bare repo:
```bash
cd /var/git/repos
git init --bare my-cpp-app.git
chown -R 1000:1000 my-cpp-app.git
```
From your local machine, push your code:
```bash
cd /path/to/my-cpp-app
git init
git remote add origin ssh://git@<server-ip>/repos/my-cpp-app.git
git add .
git commit -m "Initial commit"
git push -u origin master
```
## 2. Add a Jenkinsfile to your project
Create a file called `Jenkinsfile` in the root of your C++ project.
### Simple build (g++ directly)
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Build') {
steps {
sh 'g++ -o my_app main.cpp'
}
}
stage('Test') {
steps {
sh './my_app'
}
}
}
}
```
Adjust the `g++` command to match your source files.
### CMake build
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Configure') {
steps {
sh 'cmake -B build -S .'
}
}
stage('Build') {
steps {
sh 'cmake --build build'
}
}
stage('Test') {
steps {
sh 'cd build && ctest --output-on-failure'
}
}
}
}
```
> Note: The `gcc:latest` image includes `g++`, `gcc`, `make`, and `cmake`.
> If you need extra libraries (e.g., Boost), you can use a custom image or
> add `apt-get install` steps.
Commit and push the Jenkinsfile:
```bash
git add Jenkinsfile
git commit -m "Add Jenkinsfile for CI"
git push
```
## 3. Create the Jenkins pipeline job
1. Open Jenkins at `https://jenkins.swave.lol` (or `https://swave.lol/jenkins`)
2. Click **New Item** (top-left)
3. Enter a name, e.g. `my-cpp-app`
4. Select **Pipeline**, then click **OK**
## 4. Configure the pipeline source
On the job configuration page:
1. Scroll down to the **Pipeline** section
2. Change **Definition** from "Pipeline script" to **Pipeline script from SCM**
3. Set **SCM** to **Git**
4. In **Repository URL**, enter: `git://git-server/my-cpp-app.git`
- This works because Jenkins and git-server are on the same `jenkins_network` (172.23.0.0/16)
- `git-server` resolves to `172.23.0.3` via Docker DNS
- The git daemon protocol (`git://`) on port 9418 requires no credentials
- Alternative: `ssh://git@git-server/repos/my-cpp-app.git` (requires SSH key setup in Jenkins)
5. Set **Branch Specifier** to `*/master` (or `*/main`, whatever your default branch is)
6. **Script Path**: leave as `Jenkinsfile` (default)
7. Click **Save**
### Large repositories
For large repos (e.g., the Linux kernel), the default 10-minute fetch timeout
is too short. In the SCM config, click **Additional Behaviours** > **Advanced
clone behaviours** and set:
- **Fetch tags**: uncheck
- **Timeout (in minutes) for clone and fetch**: `120` (or more)
- **Shallow clone**: check
- **Shallow clone depth**: `1`
Shallow clone fetches only the latest commit, reducing clone size significantly.
### Automatic SCM checkout
When using **Pipeline script from SCM**, Jenkins automatically clones the
repository before running the Jenkinsfile (the "Declarative: Checkout SCM"
step). The workspace already contains the code, so you do **not** need a
`checkout` or `git clone` stage in your Jenkinsfile — adding one would clone
the repo a second time. Just use the files directly in your build stages:
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Build') {
steps {
// Source code is already in the workspace from SCM checkout
sh 'g++ -o my_app main.cpp'
}
}
}
}
```
## 5. Run the build
1. On the job page, click **Build Now** (left sidebar)
2. A build number will appear under **Build History** — click it
3. Click **Console Output** to watch the build live
You will see Jenkins:
- Pull the `gcc:latest` Docker image (first time only)
- Spin up a temporary container on the host (DooD via `/var/run/docker.sock`)
- Clone your repo inside the container
- Run each stage (`Build`, `Test`)
- Destroy the container when done
**Important:** The build container is ephemeral — everything inside it
(including compiled binaries) is destroyed when the pipeline finishes.
To keep build artifacts, see the next section.
## 6. Saving build artifacts
By default the Docker build container is destroyed after the pipeline runs,
taking all compiled binaries with it. Use `archiveArtifacts` to copy files
into Jenkins' permanent storage before the container is removed.
### Simple build with artifacts
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Build') {
steps {
sh 'g++ -o my_app main.cpp'
}
}
stage('Test') {
steps {
sh './my_app'
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'my_app', fingerprint: true
}
}
}
}
```
### CMake build with artifacts
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Configure') {
steps {
sh 'cmake -B build -S .'
}
}
stage('Build') {
steps {
sh 'cmake --build build'
}
}
stage('Test') {
steps {
sh 'cd build && ctest --output-on-failure'
}
}
stage('Archive') {
steps {
archiveArtifacts artifacts: 'build/my_app', fingerprint: true
}
}
}
}
```
Archived files are stored in `/var/jenkins_home/jobs/<job>/builds/<number>/archive/`
and can be downloaded from the build page in the Jenkins web UI under **Build Artifacts**.
You can use glob patterns to archive multiple files, e.g.:
- `archiveArtifacts artifacts: 'build/**/*.so'` — all shared libraries
- `archiveArtifacts artifacts: 'build/bin/*'` — everything in a bin directory
## 7. Automatic builds (optional)
### Option A — Poll SCM
1. Go to job config (**Configure**)
2. Under **Build Triggers**, check **Poll SCM**
3. Set schedule, e.g. `H/5 * * * *` (check every 5 minutes)
4. Click **Save**
### Option B — Git post-receive hook
Create a hook that notifies Jenkins immediately on push:
```bash
cat > /var/git/repos/my-cpp-app.git/hooks/post-receive << 'HOOK'
#!/bin/bash
curl -s "http://jenkins:8080/jenkins/git/notifyCommit?url=git://git-server/my-cpp-app.git" > /dev/null 2>&1 &
HOOK
chmod +x /var/git/repos/my-cpp-app.git/hooks/post-receive
```
This requires the **Git plugin** and **Poll SCM** to be enabled in the job
config (the schedule can be empty, e.g. just `H * * * *`).
## Incremental builds and cleaning the workspace
After the first successful build, Jenkins keeps the workspace directory
(`/var/jenkins_home/jobs/<job>/workspace/`) between builds. On subsequent
runs it does a `git fetch` + `git checkout` instead of a full clone, only
downloading new commits. This makes builds much faster, especially for
large repositories like the Linux kernel.
The same applies to Docker images: Jenkins pulls the image (e.g.,
`kernel-builder` or `gcc:latest`) once and caches it locally. Future builds
reuse the cached image. However, the Docker **container** is always created
fresh for each build and destroyed when done — only the workspace persists.
Sometimes you need a completely clean build (e.g., to rule out stale object
files causing issues). Pipeline jobs do not have a "Wipe Out Workspace"
button in the UI (only Freestyle jobs do). There are two ways to clean it:
### Option A — Delete from host
```bash
rm -rf /var/jenkins_home/jobs/<job-name>/workspace
```
The next build will do a fresh clone.
### Option B — Add cleanWs() to Jenkinsfile
Requires the **Workspace Cleanup** plugin. Add a `post` block to
automatically wipe the workspace after every build:
```groovy
pipeline {
agent {
docker { image 'gcc:latest' }
}
stages {
stage('Build') {
steps {
sh 'g++ -o my_app main.cpp'
}
}
}
post {
always {
cleanWs()
}
}
}
```
Note: using `cleanWs()` means every build will do a full clone, which is
slow for large repos. Use it only when you actually need clean builds.
## How DooD works
Jenkins is configured with Docker-out-of-Docker: it mounts the host's
`/var/run/docker.sock` into the Jenkins container. When the pipeline runs
`docker { image 'gcc:latest' }`, Jenkins creates a **sibling container** on
the host (not nested), runs the build steps inside it, and removes it after.
The Jenkins container never needs compilers installed — everything runs in the
ephemeral `gcc` container.
|