3 Commits
Author SHA1 Message Date
tbekemeyer b82d0a2155 push als String statt Boolean, dazu ein Diagnoseschritt
Eine boolesche workflow_call-Eingabe kommt auf dieser Gitea-Version nicht
im aufgerufenen Workflow an — weder ihre Vorgabe noch ein ausdruecklich
uebergebener Wert. inputs.push blieb leer, der Baustein baute immer im
Pruefmodus und meldete dabei Erfolg. Alle Eingaben vom Typ string kamen
im selben Lauf korrekt an; push war die einzige boolesche und die
einzige, die versagte.

push ist deshalb jetzt type: string mit den Werten 'true' und 'false' und
der Vorgabe 'true'. Die Bedingung inputs.push && inputs.push != 'false'
bleibt unveraendert: sie war von Anfang an fuer Boolean und String
gebaut und traegt die Umstellung ohne Anpassung. Der Kommentar an der
Eingabe haelt den Befund fest, damit die Eingabe nicht beim naechsten
Aufraeumen "korrekt" auf boolean zurueckgestellt wird und den Fehler
still zurueckholt.

Neu ist ein Diagnoseschritt am Anfang des Jobs, der den empfangenen Wert
und den daraus abgeleiteten Modus ausgibt. Der Fehler war nur deshalb so
langlebig, weil nirgends sichtbar war, was tatsaechlich ankommt — und
weil ein Lauf ohne Push genauso gruen aussieht wie einer mit. Der Schritt
folgt dem Muster von "Docker-Socket ermitteln" und kann den Job nicht
scheitern lassen.

Brechende Aenderung gegenueber v1: Aufrufer, die push als YAML-Boolean
uebergeben, muessen auf @v2 wechseln und in Anfuehrungszeichen schreiben.
Das README ist entsprechend nachgezogen.

Fixes #4
2026-09-30 17:30:40 +00:00
tbekemeyer 47b77fac54 Push-Steuerung, Pflicht-Eigentuemer und konfigurierbare Registry
Der Baustein taugt jetzt auch fuer reine Prueflaeufe. Die neue Eingabe push
(boolean, Vorgabe true) steuert, ob gepusht wird. Bei push: false entfaellt der
1Password-Schritt, es findet kein Registry-Login statt und
docker/build-push-action baut mit push: false. Bei push: true bleibt alles wie
bisher.

Damit nutzt die CI eines Projekts dieselbe Bau-Logik wie das Deployment, statt
sie zu duplizieren.

Folge, die man kennen muss: der Checkout-Token stammt aus demselben
1Password-Schritt. Ohne Push faellt der Checkout auf den Standard-Token des Jobs
zurueck, und der kann keine privaten Submodule klonen. Der Modus taugt deshalb
nur fuer Projekte ohne private Submodule. Dokumentiert im Workflow und in der
README.

Die vier Versionsangaben sind nicht mehr Pflicht -- sie dienen ausschliesslich
der Tag-Liste, die ohne Push bedeutungslos ist, und ein Prueflauf muesste sonst
Platzhalter erfinden. required von true auf false zu senken ist
rueckwaertskompatibel. Die Vorgabe ist 0 bzw. 0.0.0 und nicht leer: die
Tag-Liste wird auch ohne Push gebildet und ein leerer Wert ergaebe das
ungueltige ".../<name>:".

Aus dem Review:

- image-owner ist jetzt required ohne Vorgabe. Der Eigentuemer ist je Projekt
  verschieden und soll nie stillschweigend geraten werden -- genau das war der
  Fehler des frueheren, fest verdrahteten "suer". Bestehende Aufrufer muessen
  ihn dadurch ergaenzen; das ist gewollt.
- REGISTRY ist von einem festen env-Eintrag zur Eingabe registry mit Vorgabe
  git.jk-dev.de geworden. Der env-Block auf Workflow-Ebene entfaellt damit
  ersatzlos -- er kannte ohnehin keinen inputs-Kontext. Registry und
  Eigentuemer werden dort gelesen, wo der Kontext sicher vorhanden ist: im
  Login-Schritt und in der Tag-Liste.
2026-09-30 14:01:18 +00:00
tbekemeyer 18f672ed2a Geteilte Docker-Build-and-Push-Pipeline anlegen
Die bisher in einem einzelnen Projekt liegende Pipeline zieht als
wiederverwendbarer Workflow (on: workflow_call) nach
.gitea/workflows/docker-build-push.yml um.

Inhaltlich geaendert ist genau eine Stelle: Der Eigentuemer des Images
stand als IMAGE_OWNER fest im env-Block des Workflows, wodurch jedes
Image jedes aufrufenden Projekts unter git.jk-dev.de/suer/... gelandet
waere. Er ist jetzt die Eingabe image-owner mit suer als Vorgabewert,
sodass bestehende Aufrufer unveraendert weiterlaufen.

Der Wert kann nicht im env-Block bleiben: env auf Workflow-Ebene wird
ausgewertet, bevor es einen inputs-Kontext gibt. Die Tag-Liste liest ihn
deshalb unmittelbar aus inputs.

Alles Uebrige ist unveraendert uebernommen, samt der Kommentare, die das
Warum erklaeren: 1Password Connect, runs-on, DOCKER_HOST, Diagnoseschritt,
Multi-Arch und Tag-Schema.

Die README dokumentiert Eingaben, ein vollstaendiges Aufrufbeispiel, die
Voraussetzungen im aufrufenden Repository und den Hinweis, auf einen Tag
statt auf @main zu zeigen.
2026-09-30 12:16:34 +00:00
2 changed files with 438 additions and 1 deletions

No files matched your search

+267
View File
@@ -0,0 +1,267 @@
on:
workflow_call:
inputs:
# Die vier Versionsangaben dienen ausschliesslich der Tag-Liste. Ohne Push
# gibt es nichts zu taggen, deshalb sind sie nicht mehr Pflicht — ein
# reiner Prueflauf muesste sonst Platzhalter erfinden. Bestehende Aufrufer
# uebergeben sie unveraendert weiter; required von true auf false zu
# senken ist rueckwaertskompatibel.
#
# Die Vorgabe ist 0 bzw. 0.0.0 und nicht etwa leer: docker/build-push-action
# gibt die Tag-Liste auch ohne Push als --tag an den Build weiter. Ein
# leerer Wert ergaebe "<registry>/<owner>/<name>:" und liesse den Build an
# "invalid reference format" scheitern. 0.0.0 ist ein gueltiger, sichtbar
# unechter Wert.
version-number:
required: false
default: 0.0.0
type: string
version-number-major:
required: false
default: '0'
type: string
version-number-minor:
required: false
default: '0'
type: string
version-number-patch:
required: false
default: '0'
type: string
# Bewusst `type: string` und nicht `boolean` — siehe Issue #4. Auf dieser
# Gitea-Version (gemessen an 1.27.0) kommt eine boolesche
# workflow_call-Eingabe im aufgerufenen Workflow gar nicht an: weder ihre
# Vorgabe noch ein ausdruecklich uebergebenes `push: true`. `inputs.push`
# war in beiden Faellen leer, der Baustein baute immer im Pruefmodus und
# meldete dabei Erfolg. Alle Eingaben vom Typ string desselben Laufs
# (platforms, registry, image-owner, container-name) kamen korrekt an;
# push war die einzige boolesche und die einzige, die versagte.
#
# Wer das beim naechsten Aufraeumen "korrekt" auf boolean zurueckstellt,
# holt den Fehler still zurueck: nichts wird veroeffentlicht, und der
# Lauf bleibt gruen.
#
# Die Bedingung `inputs.push && inputs.push != 'false'` musste dafuer
# nicht angefasst werden. Sie wurde seinerzeit ausdruecklich so gebaut,
# dass sie Boolean und String vertraegt (Begruendung am
# 1Password-Schritt), und traegt die Umstellung unveraendert: 'true' ist
# truthy und ungleich 'false', 'false' ist truthy aber gleich 'false'.
push:
description: >-
Whether the built image is pushed, as the string 'true' or 'false'.
Deliberately a string, not a boolean: boolean workflow_call inputs do
not reach the called workflow on this Gitea version (see issue #4).
With push: 'false' no credentials are fetched from 1Password and no
registry login happens, so the workflow doubles as a build check.
Caveat: the checkout token comes from the same 1Password step, so
without push the checkout falls back to the job token — which cannot
clone private submodules.
required: false
default: 'true'
type: string
container-name:
required: true
type: string
dockerfile:
required: true
type: string
docker-context:
required: true
type: string
# image-owner und registry bilden zusammen das Praefix des Images. Dass
# das eine Pflicht ist und das andere nicht, ist Absicht: der Eigentuemer
# ist je Projekt verschieden und soll nie stillschweigend geraten werden —
# genau das war der Fehler des frueheren, fest verdrahteten "suer", unter
# dem die Images aller Projekte landeten. Die Registry ist dagegen
# praktisch immer dieselbe und darf deshalb eine Vorgabe haben.
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.
Deliberately has no default: every caller states its owner explicitly.
required: true
type: string
registry:
description: >-
Registry the image is tagged for and, when pushing, logged in to.
required: false
default: git.jk-dev.de
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
# Einen env-Block auf Workflow-Ebene gibt es nicht mehr: sein einziger Eintrag
# war REGISTRY, und der ist jetzt eine Eingabe. Dort haette er auch nicht
# bleiben koennen — env auf Workflow-Ebene wird ausgewertet, bevor es einen
# inputs-Kontext gibt. Registry und Eigentuemer werden deshalb an den beiden
# Stellen gelesen, an denen der Kontext sicher vorhanden ist: im
# Login-Schritt und in der Tag-Liste.
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:
# Issue #4 blieb so lange unbemerkt, weil nirgends sichtbar war, was
# ueberhaupt ankommt — und weil ein Lauf ohne Push genauso gruen aussieht
# wie einer mit. Der Schritt macht beides sichtbar: den rohen Wert der
# Eingabe und den daraus abgeleiteten Modus. Gleiches Muster wie
# "Docker-Socket ermitteln": rein informativ, mit `|| true` abgesichert,
# damit er den Job unter keinen Umstaenden scheitern laesst.
#
# Der Wert wird ueber die Umgebung gereicht statt in die Kommandozeile
# interpoliert; so bleibt er auch dann harmlos, wenn ein Aufrufer etwas
# Unerwartetes uebergibt.
- name: Eingaben melden
env:
PUSH_ROH: ${{ inputs.push }}
PUSH_MODUS: ${{ (inputs.push && inputs.push != 'false') && 'veroeffentlichen' || 'nur bauen' }}
run: |
echo "push (roh): '${PUSH_ROH}'" || true
echo "Modus: ${PUSH_MODUS}" || true
# 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.
#
# Ohne Push werden gar keine Zugangsdaten geholt. Das hat eine Folge, die
# man kennen muss: der Checkout-Token faellt damit ebenfalls weg, der
# Checkout nutzt dann den Standard-Token des Jobs — und der kann die
# privaten Submodul-Repositories nicht klonen. Projekte MIT privaten
# Submodulen koennen den Modus push: false daher nicht nutzen; ihr Klon
# scheitert an "Repository not found". Fuer Projekte ohne Submodule ist
# der Modus unproblematisch.
#
# Zur Bedingung: ein boolescher workflow_call-Input kann je nach
# Schreibweise des Aufrufers als echter Boolean oder als String ankommen.
# `inputs.push` allein waere bei dem String 'false' wahr (nichtleere
# Strings sind truthy), `inputs.push == 'true'` waere beim echten Boolean
# true falsch (ein Vergleich ueber Typgrenzen laeuft ueber Zahlen, 'true'
# wird dabei zu NaN). Die Kombination aus beidem deckt alle vier Faelle ab.
#
# Genau deshalb blieb sie bei der Umstellung von push auf `type: string`
# (Issue #4) unveraendert: sie war von Anfang an fuer beide Schreibweisen
# gebaut. 'true' ergibt wahr, 'false' ergibt falsch, ein leerer Wert
# ergibt falsch — und die Boolean-Faelle gelten unveraendert weiter,
# falls ein Aufrufer doch einmal ein YAML-Boolean uebergibt.
- name: Zugangsdaten aus 1Password laden
id: op-secrets
if: ${{ inputs.push && inputs.push != 'false' }}
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.
#
# Ist der 1Password-Schritt uebersprungen (push: false), sind seine
# Outputs leer. Ein leer uebergebenes token wuerde die Vorgabe der
# Action (github.token) ueberschreiben und den Checkout ohne jeden
# Zugang laufen lassen — er scheiterte schon am eigenen Repository.
# Der Fallback macht daraus den regulaeren Job-Token. Der genuegt
# fuer das ausfuehrende Repository, nicht fuer private Submodule
# (siehe Kommentar am 1Password-Schritt).
token: ${{ steps.op-secrets.outputs.CHECKOUT_TOKEN || github.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
# Ohne Push gibt es nichts anzumelden — und die Zugangsdaten waeren
# ohnehin leer, weil der 1Password-Schritt uebersprungen wurde. Ein
# Login-Versuch mit leerem Benutzer scheiterte mit einer nichtssagenden
# Meldung; dieselbe Bedingung wie oben verhindert das.
- name: Authenticate To Registry
if: ${{ inputs.push && inputs.push != 'false' }}
uses: docker/login-action@v3
with:
registry: ${{ inputs.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 }}
# Dieselbe Normalisierung wie bei den if:-Bedingungen. Das Ergebnis
# ist immer genau "true" oder "false" — die Action liest den Wert als
# Boolean und akzeptiert nichts dazwischen.
push: ${{ inputs.push && inputs.push != 'false' }}
# 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.
#
# Ohne Push bleibt die Liste rein lokal: die Action gibt sie als --tag
# an den Build weiter, veroeffentlicht aber nichts. Die Versionen
# tragen dann ihre Vorgabe 0.0.0.
tags: |
${{ inputs.registry }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:latest
${{ inputs.registry }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number }}
${{ inputs.registry }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}
${{ inputs.registry }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}.${{ inputs.version-number-minor }}
${{ inputs.registry }}/${{ inputs.image-owner }}/${{ inputs.container-name }}:${{ inputs.version-number-major }}.${{ inputs.version-number-minor }}.${{ inputs.version-number-patch }}
+171 -1
View File
@@ -1 +1,171 @@
# 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 eine Registry, vorgabegemäß `git.jk-dev.de`.
Läuft auf einem Runner mit dem Label `ubuntu-docker`.
Gepusht wird unter fünf Tags:
```
<registry>/<image-owner>/<container-name>:latest
<registry>/<image-owner>/<container-name>:<version-number>
<registry>/<image-owner>/<container-name>:<major>
<registry>/<image-owner>/<container-name>:<major>.<minor>
<registry>/<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.
Mit `push: 'false'` entfällt der Push — der Baustein baut dann nur. Damit nutzt
die CI eines Projekts dieselbe Bau-Logik wie das Deployment, statt sie zu
duplizieren. Siehe [Prüfmodus](#prüfmodus-ohne-push).
#### Eingaben
| Eingabe | Pflicht | Vorgabe | Bedeutung |
| ---------------------- | ------- | ------------------------- | --------- |
| `container-name` | ja | — | Name des Images |
| `dockerfile` | ja | — | Pfad zum Dockerfile |
| `docker-context` | ja | — | Build-Kontext |
| `image-owner` | ja | — | Eigentümer-Segment des Images, also der mittlere Teil von `<registry>/<owner>/<name>` |
| `push` | nein | `'true'` | Zeichenkette `'true'` oder `'false'`. Ob das Image gepusht wird. Bei `'false'` werden keine Zugangsdaten aus 1Password geholt und es findet kein Registry-Login statt — siehe [Prüfmodus](#prüfmodus-ohne-push) |
| `registry` | nein | `git.jk-dev.de` | Registry, für die getaggt und — beim Push — bei der angemeldet wird |
| `version-number` | nein | `0.0.0` | Vollständige Version, wird als Tag gesetzt |
| `version-number-major` | nein | `0` | Major-Anteil für die mitwandernden Tags |
| `version-number-minor` | nein | `0` | Minor-Anteil |
| `version-number-patch` | nein | `0` | Patch-Anteil |
| `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 |
Dass `image-owner` Pflicht ist und `registry` nicht, ist Absicht: Der Eigentümer
ist je Projekt verschieden und soll nie stillschweigend geraten werden — genau
das war der Fehler des früher fest verdrahteten `suer`, unter dem die Images
aller Projekte landeten. Die Registry ist dagegen praktisch immer dieselbe.
`push` ist eine Zeichenkette und kein Boolean — und das ist Absicht. Auf dieser
Gitea-Version (gemessen an 1.27.0) werden **boolesche `workflow_call`-Eingaben
nicht an den aufgerufenen Workflow durchgereicht**: Weder ihre Vorgabe noch ein
ausdrücklich übergebenes `push: true` kamen an, `inputs.push` blieb leer, und
der Baustein baute immer im Prüfmodus — bei grünem Lauf. Alle Eingaben vom Typ
`string` kamen im selben Lauf korrekt an. Details in
[Issue #4](https://git.jk-dev.de/workflow-util/pipelines/issues/4).
Deshalb bitte immer in Anführungszeichen schreiben: `push: 'true'` bzw.
`push: 'false'`. Ein unquotiertes `push: false` ist im YAML ein Boolean und
wird verschluckt.
> **Brechende Änderung gegenüber `v1`.** Aufrufer, die `push` als YAML-Boolean
> übergeben, müssen auf `@v2` wechseln und dabei auf die Schreibweise mit
> Anführungszeichen umstellen. Wer `@v1` weiterverwendet, behält das alte
> Verhalten samt Fehler.
Die vier Versionsangaben dienen ausschließlich der Tag-Liste. Sie sind keine
Pflicht mehr, damit ein Prüflauf keine Platzhalter erfinden muss; wer pusht,
übergibt sie wie bisher. Ihre Vorgabe ist bewusst `0.0.0` und nicht leer — die
Tag-Liste wird auch ohne Push gebildet, und ein leerer Wert ergäbe das ungültige
`…/<name>:`.
#### Aufrufbeispiel
```yaml
jobs:
image:
uses: workflow-util/pipelines/.gitea/workflows/docker-build-push.yml@v2
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
push: 'true'
secrets: inherit
```
`secrets: inherit` ist notwendig — der Workflow braucht `OP_CONNECT_TOKEN` aus
dem aufrufenden Repository.
#### Prüfmodus ohne Push
Mit `push: 'false'` baut der Baustein nur und veröffentlicht nichts. Es werden
dann keine Zugangsdaten aus 1Password geholt und es findet kein Registry-Login
statt. Die CI eines Projekts nutzt damit dieselbe Bau-Logik wie das Deployment,
statt sie zu duplizieren.
Weder Secrets noch Versionsangaben sind dafür nötig:
```yaml
jobs:
build-check:
uses: workflow-util/pipelines/.gitea/workflows/docker-build-push.yml@v2
with:
container-name: bk-dev-website
dockerfile: ./Dockerfile
docker-context: .
image-owner: bekemeyer-klein
push: 'false'
```
Die Anführungszeichen sind nicht kosmetisch: `push: false` ohne sie ist ein
YAML-Boolean und kommt im Baustein nicht an (siehe oben). Es griffe dann die
Vorgabe `'true'`, der Lauf versuchte also trotz `push: false` zu
veröffentlichen — sofern Gitea die Typabweichung nicht ohnehin als Fehler
zurückweist.
> **Private Submodule funktionieren in diesem Modus nicht.**
> Der Checkout-Token stammt aus demselben 1Password-Schritt, der ohne Push
> übersprungen wird. Der Checkout fällt deshalb auf den Standard-Token des Jobs
> zurück. Der genügt für das ausführende Repository, nicht aber für die
> Submodule in eigenen privaten Repositories — deren Klon scheitert mit
> `Repository not found`. Projekte **mit** privaten Submodulen können
> `push: false` daher nicht verwenden; für Projekte ohne Submodule ist der
> Modus unproblematisch.
`secrets: inherit` kann hier entfallen. Es zu belassen schadet nicht — der
Workflow greift ohne Push auf kein Secret zu.
#### Voraussetzungen im aufrufenden Repository
| Name | Art | Zweck |
| ------------------ | -------- | ----- |
| `OP_CONNECT_HOST` | Variable | Adresse des 1Password-Connect-Servers im Cluster. Nur beim Push nötig |
| `OP_CONNECT_TOKEN` | Secret | Zugang zu diesem Connect-Server. Nur beim Push nötig |
| `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@v2 # 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.
`v2` ist genau so ein Fall: Die Umstellung von `push` auf `type: string` bricht
Aufrufe, die den Wert als YAML-Boolean übergeben. `v1` bleibt unverändert
stehen; wer umstellt, wechselt auf `@v2` und schreibt `push: 'true'` bzw.
`push: 'false'`.