Geteilte Docker-Build-and-Push-Pipeline anlegen #2

Merged
jklein merged 2 commits from docker-build-push into main 2026-09-30 16:11:05 +02:00
2 changed files with 238 additions and 1 deletions
Showing only changes of commit 18f672ed2a - Show all commits

No files matched your search

+146
View File
@@ -0,0 +1,146 @@
on:
workflow_call:
inputs:
version-number:
required: true
type: string
version-number-major:
required: true
type: string
version-number-minor:
required: true
type: string
version-number-patch:
required: true
type: string
container-name:
required: true
type: string
dockerfile:
required: true
type: string
docker-context:
required: true
type: string
image-owner:
description: >-
Owner segment of the pushed image, i.e. the middle part of
<registry>/<owner>/<container-name>. Used to be hardcoded to "suer",
which sent every image of every calling project to the same account.
Kept as the default so existing callers stay unchanged.
required: false
jklein marked this conversation as resolved Outdated
Outdated
Review

ist required, kein default

ist required, kein default
default: suer
type: string
timeout-minutes:
description: 'Hard limit for the build job. Without one a wedged build runs into the 6h default.'
required: false
default: 45
type: number
platforms:
description: >-
Target platforms of the pushed manifest list. The build stages of all
dockerfiles are pinned to the build platform, so an extra platform only
adds its runtime stage, not another compile run.
required: false
default: linux/amd64,linux/arm64
type: string
build-args:
description: >-
Extra build arguments for the image, one NAME=value per line. The docs
image uses it to stamp the released version into writerside.cfg.
required: false
default: ''
type: string
name: build and push Docker Image
env:
REGISTRY: git.jk-dev.de
jklein marked this conversation as resolved
Review

soll konfigurierbar sein mit default auf git.jk-dev.de

soll konfigurierbar sein mit default auf git.jk-dev.de
jobs:
build-image:
name: Build ${{ inputs.container-name }} Container
permissions:
packages: write
contents: read
runs-on: [ ubuntu-docker ]
timeout-minutes: ${{ inputs.timeout-minutes }}
env:
# Ohne Angabe suchen Buildx und die Docker-CLI unter
# unix:///var/run/docker.sock. Laeuft der Daemon des Runners rootless,
# liegt sein Socket stattdessen unter /run/user/<uid>/docker.sock. Der
# Pfad kommt deshalb aus einer Repository-Variablen; ist sie nicht
# gesetzt, bleibt die Variable leer und Docker nutzt seinen Default.
DOCKER_HOST: ${{ vars.DOCKER_HOST }}
steps:
# Der Token, den Gitea jedem Job stellt, taugt fuer zwei Dinge nicht:
# Er gilt nur fuer das ausfuehrende Repository, waehrend die Submodule in
# eigenen, privaten Repositories liegen — deren Klon scheitert an
# "Repository not found". Und die Container-Registry lehnt ihn ab
# (container.Authenticate antwortet mit 401). Beides deckt je ein eigener
# Zugang ab, gepflegt in 1Password und hier zur Laufzeit ueber den
# Connect-Server im Cluster geholt; in Gitea liegt nur dessen Zugang.
- name: Zugangsdaten aus 1Password laden
id: op-secrets
uses: 1password/load-secrets-action@v2
with:
# Die Werte sollen nur als Step-Outputs entstehen und nicht in die
# Umgebung aller folgenden Schritte wandern.
export-env: false
env:
OP_CONNECT_HOST: ${{ vars.OP_CONNECT_HOST }}
OP_CONNECT_TOKEN: ${{ secrets.OP_CONNECT_TOKEN }}
# Klont die privaten Submodul-Repositories.
CHECKOUT_TOKEN: op://GitHub-Actions/jlab-git-checkout-token/credential
# Eigener Zugang fuer die Registry — der Checkout-Token darf dort
# nicht schreiben. Benutzername und Token kommen aus demselben Item,
# damit sie zwangslaeufig zusammenpassen; die Registry prueft den
# Token gegen genau das Konto aus `username`.
REGISTRY_USER: op://GitHub-Actions/jlab-registry-access/username
REGISTRY_TOKEN: op://GitHub-Actions/jlab-registry-access/password
- uses: actions/checkout@v4
with:
submodules: true
# Die Action legt je Referenz einen Output an, benannt wie die
# Umgebungsvariable, in der die op://-Referenz stand.
token: ${{ steps.op-secrets.outputs.CHECKOUT_TOKEN }}
# Schlaegt der Buildx-Start fehl, ist die erste Frage immer, welcher
# Socket im Job-Container ueberhaupt ankommt. Der Schritt beantwortet sie,
# ohne den Job scheitern zu lassen.
- name: Docker-Socket ermitteln
run: |
echo "DOCKER_HOST=${DOCKER_HOST:-<leer, Default wird genutzt>}"
ls -l /var/run/docker.sock /run/docker.sock 2>&1 || true
ls -l /run/user/*/docker.sock 2>&1 || true
docker version 2>&1 || true
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@master
- name: Authenticate To Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ steps.op-secrets.outputs.REGISTRY_USER }}
password: ${{ steps.op-secrets.outputs.REGISTRY_TOKEN }}
- name: Build
uses: docker/build-push-action@v5
with:
builder: ${{ steps.buildx.outputs.name }}
context: ${{ inputs.docker-context }}
file: ${{ inputs.dockerfile }}
platforms: ${{ inputs.platforms }}
build-args: ${{ inputs.build-args }}
push: true
# Der Eigentümer stand frueher als IMAGE_OWNER im env-Block des
# Workflows. Dort kann er nicht bleiben: env auf Workflow-Ebene wird
# ausgewertet, bevor es einen inputs-Kontext gibt. Der Wert wird
# deshalb hier unmittelbar aus inputs gelesen — an dieser Stelle ist
# der Kontext sicher vorhanden, anders als in jedem env-Block.
tags: |
${{ env.REGISTRY }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:latest
${{ env.REGISTRY }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number }}
${{ env.REGISTRY }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}
${{ env.REGISTRY }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}.${{ inputs.version-number-minor }}
${{ env.REGISTRY }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}.${{ inputs.version-number-minor }}.${{ inputs.version-number-patch }}
+92 -1
View File
@@ -1 +1,92 @@
# Hier stehen wichtige Infos!
# pipelines
Geteilte CI/CD-Bausteine für die Gitea-Instanz `git.jk-dev.de`. Die Workflows
hier werden nicht selbst ausgelöst, sondern von anderen Repositories über
`uses:` aufgerufen (`on: workflow_call`).
## Bausteine
### `.gitea/workflows/docker-build-push.yml`
Baut ein Docker-Image und schiebt es als Multi-Arch-Manifest
(`linux/amd64,linux/arm64`) in die Registry `git.jk-dev.de`. Läuft auf einem
Runner mit dem Label `ubuntu-docker`.
Gepusht wird unter fünf Tags:
```
git.jk-dev.de/<image-owner>/<container-name>:latest
git.jk-dev.de/<image-owner>/<container-name>:<version-number>
git.jk-dev.de/<image-owner>/<container-name>:<major>
git.jk-dev.de/<image-owner>/<container-name>:<major>.<minor>
git.jk-dev.de/<image-owner>/<container-name>:<major>.<minor>.<patch>
```
Der Checkout holt Submodule mit; die Zugangsdaten dafür und für die Registry
kommen zur Laufzeit aus 1Password Connect.
#### Eingaben
| Eingabe | Pflicht | Vorgabe | Bedeutung |
| ---------------------- | ------- | ------------------------- | --------- |
| `version-number` | ja | — | Vollständige Version, wird als Tag gesetzt |
| `version-number-major` | ja | — | Major-Anteil für die mitwandernden Tags |
| `version-number-minor` | ja | — | Minor-Anteil |
| `version-number-patch` | ja | — | Patch-Anteil |
| `container-name` | ja | — | Name des Images |
| `dockerfile` | ja | — | Pfad zum Dockerfile |
| `docker-context` | ja | — | Build-Kontext |
| `image-owner` | nein | `suer` | Eigentümer-Segment des Images, also der mittlere Teil von `<registry>/<owner>/<name>` |
| `timeout-minutes` | nein | `45` | Harte Grenze für den Build-Job |
| `platforms` | nein | `linux/amd64,linux/arm64` | Zielplattformen der Manifest-Liste |
| `build-args` | nein | — | Zusätzliche Build-Argumente, ein `NAME=wert` je Zeile |
#### Aufrufbeispiel
```yaml
jobs:
image:
uses: workflow-util/pipelines/.gitea/workflows/docker-build-push.yml@v1
with:
version-number: 2026.09.1
version-number-major: '2026'
version-number-minor: '09'
version-number-patch: '1'
container-name: bk-dev-website
dockerfile: ./Dockerfile
docker-context: .
image-owner: bekemeyer-klein
secrets: inherit
```
`secrets: inherit` ist notwendig — der Workflow braucht `OP_CONNECT_TOKEN` aus
dem aufrufenden Repository.
#### Voraussetzungen im aufrufenden Repository
| Name | Art | Zweck |
| ------------------ | -------- | ----- |
| `OP_CONNECT_HOST` | Variable | Adresse des 1Password-Connect-Servers im Cluster |
| `OP_CONNECT_TOKEN` | Secret | Zugang zu diesem Connect-Server |
| `DOCKER_HOST` | Variable | Nur nötig, wenn der Runner-Daemon rootless läuft und sein Socket nicht unter `/var/run/docker.sock` liegt (dann z. B. `unix:///run/user/1000/docker.sock`). Ist die Variable nicht gesetzt, nutzt Docker seinen Default. |
Die eigentlichen Zugänge für Checkout und Registry liegen in 1Password
(`GitHub-Actions/jlab-git-checkout-token`, `GitHub-Actions/jlab-registry-access`)
und müssen in Gitea nicht hinterlegt werden.
## Auf einen Tag zeigen, nicht auf `@main`
Aufrufe bitte immer an einen Tag binden:
```yaml
uses: workflow-util/pipelines/.gitea/workflows/docker-build-push.yml@v1 # so
uses: workflow-util/pipelines/.gitea/workflows/docker-build-push.yml@main # nicht so
```
Mit `@main` ändert jede Anpassung hier unangekündigt das Verhalten sämtlicher
Projekte — und der Fehler zeigt sich dann dort, wo niemand etwas geändert hat.
Ein Tag macht das Übernehmen einer neuen Fassung zu einer bewussten Entscheidung
im jeweiligen Projekt.
Änderungen hier bekommen deshalb einen neuen Tag. Bricht eine Änderung
bestehende Aufrufe, wird der Major-Anteil erhöht.