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
|
# 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 * * * *`).
## 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.
|