summaryrefslogtreecommitdiff
path: root/docs/handbook/images4docker/ci-cd-integration.md
blob: c6c617a21f5ef235f6f4c24309b7d403c4dfa5ec (plain)
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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# images4docker — CI/CD Integration

## Overview

images4docker is purpose-built for CI/CD.  The entire repository exists to
produce pre-baked Docker images that other Project Tick repositories consume
as build containers.  This document covers how the images are built, pushed,
and consumed.

---

## GitHub Actions Workflow

### Workflow File

The CI workflow is defined in `.github/workflows/build.yml` (referenced in the
project README as "Repack Existing Images").

### Trigger Conditions

The workflow runs on two triggers:

#### 1. Push to `main`

Triggered when any of these paths change in a push to the `main` branch:

- `dockerfiles/*.Dockerfile` — any Dockerfile modification
- `.github/workflows/build.yml` — workflow changes
- `README.md` — documentation updates

#### 2. Scheduled Cron

```yaml
schedule:
  - cron: '17 3 * * *'   # Daily at 03:17 UTC
```

The daily schedule ensures that:
- Upstream base image security patches are incorporated.
- New upstream package versions are picked up.
- Images stay current even when no Dockerfiles change.

The non-standard minute (`17`) avoids the GitHub Actions "top of the hour"
congestion that causes delayed job starts.

---

## Build Process

### Build Matrix

The workflow defines a build matrix of approximately **35 targets** — the
Qt6-capable subset of the 40 Dockerfiles.  Each target specifies:

- The Dockerfile path
- The target image name and tag
- The `PACKAGES` build argument (distro-specific package list)
- Optionally, the `CUSTOM_INSTALL`, `UPDATE_CMD`, or `CLEAN_CMD` build arguments

### Build Arguments

Each distribution requires a different set of Qt6 package names.  The workflow
injects these via Docker build arguments.

#### apt-based distributions (Debian, Ubuntu, Devuan, Kali)

Typical `PACKAGES` value:

```
qt6-base-dev qt6-tools-dev qmake6 cmake gcc g++ make pkg-config
```

Notes:
- Qt6 packages require `apt-get update` first (injected via `UPDATE_CMD` or
  handled by the Dockerfile's default `apt-get update` prefix).
- `--no-install-recommends` is hardcoded in the Dockerfile.

#### dnf-based distributions (Fedora, RHEL family)

Typical `PACKAGES` value:

```
qt6-qtbase-devel qt6-qttools-devel cmake gcc gcc-c++ make pkgconfig
```

For RHEL-family distros that need CRB/PowerTools, the workflow may use
`CUSTOM_INSTALL`:

```sh
dnf config-manager --enable crb && dnf install -y epel-release && dnf install -y qt6-qtbase-devel ...
```

#### apk-based distributions (Alpine)

Typical `PACKAGES` value:

```
qt6-qtbase-dev qt6-qttools-dev cmake gcc g++ make musl-dev pkgconf
```

Notes:
- No `libsystemd-dev` equivalent (Alpine does not use systemd).
- Uses `musl-dev` instead of `libc6-dev`.

#### zypper-based distributions (openSUSE)

Typical `PACKAGES` value:

```
qt6-base-devel qt6-tools-devel cmake gcc gcc-c++ make pkg-config
```

#### pacman-based (Arch Linux)

Typical `PACKAGES` value:

```
qt6-base qt6-tools cmake gcc make pkgconf
```

#### emerge-based (Gentoo)

Typical `PACKAGES` value:

```
dev-qt/qtbase dev-qt/qttools dev-build/cmake
```

#### nix-env-based (NixOS)

Typical `PACKAGES` value:

```
nixpkgs.qt6.qtbase nixpkgs.qt6.qttools nixpkgs.cmake nixpkgs.gcc nixpkgs.gnumake
```

#### xbps-based (Void Linux)

Typical `PACKAGES` value:

```
qt6-base-devel qt6-tools-devel cmake gcc make pkg-config
```

### Docker Build Command

Each matrix entry runs a Docker build command equivalent to:

```bash
docker build \
  --file dockerfiles/<distro>-<tag>.Dockerfile \
  --build-arg PACKAGES="<package list>" \
  --build-arg CUSTOM_INSTALL="<optional custom command>" \
  --build-arg UPDATE_CMD="<optional pre-install command>" \
  --build-arg CLEAN_CMD="<optional cleanup command>" \
  --tag ghcr.io/project-tick-infra/images/<target_name>:<target_tag> \
  .
```

### BuildKit

Every Dockerfile starts with `# syntax=docker/dockerfile:1.7`, which enables
BuildKit features.  The workflow likely sets `DOCKER_BUILDKIT=1` or uses
`docker buildx build` for:

- Improved build caching
- Better error reporting
- Parallel layer execution

---

## Container Registry

### Registry URL

```
ghcr.io/project-tick-infra/images/
```

All images are pushed to the **GitHub Container Registry** (GHCR), which is
tightly integrated with GitHub Actions.

### Image Naming

The target format for pushed images is:

```
ghcr.io/project-tick-infra/images/<target_name>:<target_tag>
```

Where `<target_name>` and `<target_tag>` are derived from the Dockerfile name.
For example:

| Dockerfile                     | Image reference                                                 |
|--------------------------------|-----------------------------------------------------------------|
| `alma-9.Dockerfile`           | `ghcr.io/project-tick-infra/images/alma:9`                     |
| `alpine-321.Dockerfile`       | `ghcr.io/project-tick-infra/images/alpine:3.21`                |
| `debian-bookworm-slim.Dockerfile` | `ghcr.io/project-tick-infra/images/debian:bookworm-slim`   |
| `ubuntu-2404.Dockerfile`      | `ghcr.io/project-tick-infra/images/ubuntu:24.04`               |
| `fedora-latest.Dockerfile`    | `ghcr.io/project-tick-infra/images/fedora:latest`              |

### Authentication

The workflow authenticates to GHCR using the built-in `GITHUB_TOKEN` provided
by GitHub Actions:

```bash
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
```

### Push

After a successful build (meaning the Qt6 verification gate passed), the
image is pushed:

```bash
docker push ghcr.io/project-tick-infra/images/<target_name>:<target_tag>
```

---

## Consuming the Images

### In GitHub Actions Workflows

Other Project Tick repositories can use these images as build containers:

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/project-tick-infra/images/ubuntu:24.04
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: |
          cmake -B build
          cmake --build build
```

### In Docker Compose

```yaml
services:
  build:
    image: ghcr.io/project-tick-infra/images/fedora:42
    volumes:
      - .:/workspace
    working_dir: /workspace
    command: cmake -B build && cmake --build build
```

### As a FROM Base

Other Dockerfiles can extend these images:

```dockerfile
FROM ghcr.io/project-tick-infra/images/debian:bookworm-slim
RUN apt-get update && apt-get install -y additional-package
```

### Direct Docker Run

```bash
docker run --rm -v $(pwd):/workspace -w /workspace \
  ghcr.io/project-tick-infra/images/alpine:3.22 \
  cmake -B build && cmake --build build
```

---

## Build Lifecycle

```
 ┌────────────────────────────────────────────────────────────────────────┐
 │                     Trigger (push to main or cron)                     │
 └────────────────────────┬───────────────────────────────────────────────┘
                          │
                          ▼
 ┌────────────────────────────────────────────────────────────────────────┐
 │                      Login to GHCR                                     │
 └────────────────────────┬───────────────────────────────────────────────┘
                          │
                          ▼
 ┌────────────────────────────────────────────────────────────────────────┐
 │                     Build matrix (parallel)                            │
 │                                                                        │
 │   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                  │
 │   │  alma:9     │  │  alpine:3.21│  │  fedora:42  │  ...  (×35)      │
 │   │             │  │             │  │             │                    │
 │   │ 1. Pull base│  │ 1. Pull base│  │ 1. Pull base│                  │
 │   │ 2. Install  │  │ 2. Install  │  │ 2. Install  │                  │
 │   │ 3. Clean    │  │ 3. Clean    │  │ 3. Clean    │                  │
 │   │ 4. Qt6 gate │  │ 4. Qt6 gate │  │ 4. Qt6 gate │                  │
 │   │ 5. Tag      │  │ 5. Tag      │  │ 5. Tag      │                  │
 │   │ 6. Push     │  │ 6. Push     │  │ 6. Push     │                  │
 │   └─────────────┘  └─────────────┘  └─────────────┘                  │
 └────────────────────────────────────────────────────────────────────────┘
```

---

## Caching Strategy

### No explicit Docker cache

The current design rebuilds images from scratch on each run.  This is
intentional:

1. **Security** — pulling fresh base images ensures the latest security patches
   are included.
2. **Reproducibility** — no stale cached layers that might mask package changes.
3. **Simplicity** — no cache management, no cache invalidation bugs.

The trade-off is longer build times (~35 parallel builds), which is acceptable
for a daily cron job.

### Layer efficiency

Each Dockerfile produces a single additional layer on top of the base image.
This means:

- No intermediate layers to cache or invalidate.
- The push to GHCR transfers only the diff layer.
- If the base image hasn't changed and the packages are the same, the layer
  content will be identical (content-addressable storage deduplicates it).

---

## Monitoring and Failure Handling

### Build Failures

When a Qt6 verification fails:

1. The Docker build exits with code 1.
2. The GitHub Actions matrix job for that image is marked as failed.
3. Other matrix jobs continue (matrix builds are independent).
4. The workflow summary shows which images succeeded and which failed.

### Upstream Image Disappearance

If an upstream base image tag is removed or renamed:

1. The `FROM` instruction fails with "manifest not found".
2. The build fails for that specific target.
3. The old image remains in GHCR (it is not deleted).
4. Fix: update the Dockerfile to use the new tag, or remove the Dockerfile.

### Monitoring

Build status can be monitored via:

- GitHub Actions workflow run history
- GHCR package page (shows when images were last updated)
- Downstream CI failures (if an image is stale or missing)

---

## Security Considerations

### Image Provenance

- All Dockerfiles use official, well-known upstream base images.
- CentOS Stream images are pulled from `quay.io/centos/centos` (the official
  CentOS mirror), not unofficial sources.
- No third-party or personal Docker Hub repositories are used as bases.

### No Secrets in Images

- The Dockerfiles do not `COPY` any files from the build context.
- No `ARG` or `ENV` values contain secrets.
- The `GITHUB_TOKEN` is only used for GHCR authentication, not passed into builds.

### No Network Access at Runtime

The images are build environments.  They do not expose ports, run daemons,
or listen for connections.  Network access is only used during `docker build`
to download packages from distribution repositories.

### Daily Rebuilds

The daily cron ensures that security patches from upstream distributions are
incorporated within 24 hours.

---

## Related Documentation

- [Overview](overview.md) — project summary and image list
- [Architecture](architecture.md) — Dockerfile structure
- [Base Images](base-images.md) — per-distribution details
- [Qt6 Verification](qt6-verification.md) — the verification gate
- [Creating New Images](creating-new-images.md) — adding new distributions
- [Troubleshooting](troubleshooting.md) — debugging build failures