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.
This commit was merged in pull request #2.
This commit is contained in:
2 files changed
+156
-30
No files matched your search
@@ -1,18 +1,43 @@
|
||||
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: true
|
||||
required: false
|
||||
default: 0.0.0
|
||||
type: string
|
||||
version-number-major:
|
||||
required: true
|
||||
required: false
|
||||
default: '0'
|
||||
type: string
|
||||
version-number-minor:
|
||||
required: true
|
||||
required: false
|
||||
default: '0'
|
||||
type: string
|
||||
version-number-patch:
|
||||
required: true
|
||||
required: false
|
||||
default: '0'
|
||||
type: string
|
||||
push:
|
||||
description: >-
|
||||
Whether the built image is pushed. 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: boolean
|
||||
container-name:
|
||||
required: true
|
||||
type: string
|
||||
@@ -22,14 +47,25 @@ on:
|
||||
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.
|
||||
Kept as the default so existing callers stay unchanged.
|
||||
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: suer
|
||||
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.'
|
||||
@@ -54,8 +90,12 @@ on:
|
||||
|
||||
name: build and push Docker Image
|
||||
|
||||
env:
|
||||
REGISTRY: git.jk-dev.de
|
||||
# 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:
|
||||
@@ -80,8 +120,24 @@ jobs:
|
||||
# (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.
|
||||
- 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
|
||||
@@ -104,7 +160,15 @@ jobs:
|
||||
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 }}
|
||||
#
|
||||
# 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.
|
||||
@@ -118,10 +182,15 @@ jobs:
|
||||
- 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: ${{ env.REGISTRY }}
|
||||
registry: ${{ inputs.registry }}
|
||||
username: ${{ steps.op-secrets.outputs.REGISTRY_USER }}
|
||||
password: ${{ steps.op-secrets.outputs.REGISTRY_TOKEN }}
|
||||
- name: Build
|
||||
@@ -132,15 +201,22 @@ jobs:
|
||||
file: ${{ inputs.dockerfile }}
|
||||
platforms: ${{ inputs.platforms }}
|
||||
build-args: ${{ inputs.build-args }}
|
||||
push: true
|
||||
# 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: |
|
||||
${{ 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 }}
|
||||
${{ 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 }}
|
||||
@@ -9,38 +9,55 @@ hier werden nicht selbst ausgelöst, sondern von anderen Repositories über
|
||||
### `.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`.
|
||||
(`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:
|
||||
|
||||
```
|
||||
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>
|
||||
<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 |
|
||||
| ---------------------- | ------- | ------------------------- | --------- |
|
||||
| `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>` |
|
||||
| `image-owner` | ja | — | Eigentümer-Segment des Images, also der mittlere Teil von `<registry>/<owner>/<name>` |
|
||||
| `push` | nein | `true` | 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.
|
||||
|
||||
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
|
||||
@@ -62,12 +79,45 @@ jobs:
|
||||
`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@v1
|
||||
with:
|
||||
container-name: bk-dev-website
|
||||
dockerfile: ./Dockerfile
|
||||
docker-context: .
|
||||
image-owner: bekemeyer-klein
|
||||
push: false
|
||||
```
|
||||
|
||||
> **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 |
|
||||
| `OP_CONNECT_TOKEN` | Secret | Zugang zu diesem Connect-Server |
|
||||
| `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
|
||||
|
||||
Reference in new issue
Block a user