Ein Weg vom Quelltext in die Registry — und von dort aufs NAS

Damit das Image dort landet, wo das NAS es holen kann.

**`.gitea/workflows/image.yml`** — Gitea baut selbst. Bei jedem Push auf
`main` entsteht das Image für **beide** Architekturen und landet in der
Registry desselben Gitea. Niemand muss daran denken, niemand muss angemeldet
sein, und niemand kann die Architektur vergessen.

Voraussetzung ist ein registrierter Act-Runner. Hat deiner keinen, läuft die
Datei nie an — dafür gibt es den zweiten Weg.

**`Tools/image-veroeffentlichen.sh`** — ein Befehl vom Arbeitsrechner, baut
amd64 und arm64 und lädt hoch. Angemeldet wird einmalig von Hand; das
Passwort tippt nur, wer es kennt.

Beides legt `:latest` ab, dazu den Kurz-Hash und auf einem Git-Tag dessen
Namen — damit sich eine Fassung festnageln lässt, statt immer „latest" zu
ziehen.

Die Anleitung bekommt vier neue Abschnitte: Gitea baut selbst, von Hand
bauen, das Paket öffentlich stellen, und **die Registry im NAS eintragen** —
also genau der Dialog „Quellname des Images", mit Alias `gitea` und Link
`https://git.scarriffle.com/`. Für ein privates Paket ein Zugriffstoken mit
`read:package`, nicht das Kontopasswort: ein Token lässt sich einzeln
widerrufen.

Die Beispiel-Compose zeigte auf `ghcr.io/scarriffle/flaro` — dort liegt
nichts und lag nie etwas. Jetzt auf die Gitea-Registry.

---

**Ein Fehler im eigenen Skript, gefunden beim ersten Lauf.** Die Prüfung
„läuft Docker?" war `docker info | grep -q .` — und `grep -q` beendet die
Pipe beim ersten Treffer, Docker bekommt SIGPIPE, und mit `set -o pipefail`
gilt das als Fehlschlag. Das Skript meldete „Docker läuft nicht", während
Docker lief und gerade ein Image gebaut hatte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-04 08:08:22 +02:00
co-authored by Claude Opus 5
parent f2b5ee633b
commit c12748e262
4 changed files with 191 additions and 19 deletions
+75
View File
@@ -0,0 +1,75 @@
# Baut das Flaro-Image und legt es in die Registry dieses Gitea.
#
# **Warum hier und nicht auf dem Arbeitsrechner.** Ein Bau von Hand heisst:
# jemand muss daran denken, muss die richtige Architektur angeben und muss
# angemeldet sein. Hier passiert es bei jedem Push von selbst, immer für
# beide Architekturen, und die Anmeldung besorgt Gitea.
#
# **Voraussetzung ist ein Runner.** Ohne einen registrierten Act-Runner
# läuft diese Datei nie an — dann bleibt der Weg von Hand aus
# `Tools/image-veroeffentlichen.sh`.
name: Image bauen und veröffentlichen
on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:
jobs:
image:
runs-on: ubuntu-latest
steps:
- name: Quelltext holen
uses: actions/checkout@v4
# **buildx, nicht das eingebaute `docker build`.** Nur damit lassen
# sich zwei Architekturen in einem Manifest ablegen; ohne das bekommt
# ein Intel-NAS ein arm64-Image und meldet „exec format error".
- name: buildx vorbereiten
uses: docker/setup-buildx-action@v3
- name: QEMU für die fremde Architektur
uses: docker/setup-qemu-action@v3
- name: An der Registry anmelden
uses: docker/login-action@v3
with:
registry: ${{ vars.REGISTRY || 'git.scarriffle.com' }}
username: ${{ gitea.actor }}
password: ${{ secrets.GITEA_TOKEN }}
- name: Marken bestimmen
id: marken
run: |
REGISTRY="${{ vars.REGISTRY || 'git.scarriffle.com' }}"
# Gitea verlangt Kleinbuchstaben im Pfad; der Besitzername kann
# gross geschrieben sein.
PFAD=$(echo "${{ gitea.repository }}" | tr '[:upper:]' '[:lower:]')
BILD="$REGISTRY/$PFAD"
MARKEN="$BILD:latest,$BILD:${GITHUB_SHA::7}"
# Auf einem Git-Tag zusätzlich dessen Namen — damit sich eine
# Fassung festnageln lässt, statt immer „latest" zu ziehen.
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
MARKEN="$MARKEN,$BILD:${GITHUB_REF_NAME#v}"
fi
echo "marken=$MARKEN" >> "$GITHUB_OUTPUT"
echo "Gebaut wird: $MARKEN"
- name: Bauen und hochladen
uses: docker/build-push-action@v5
with:
context: .
file: server/Dockerfile
# **Beide Architekturen.** amd64 für die Intel-NAS, arm64 für
# ARM-Geräte und für den Apple-Rechner.
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.marken.outputs.marken }}
# Zwischenergebnisse behalten: ohne das lädt jeder Lauf alle
# Pakete neu, und ein Bau dauert statt zwei Minuten zwölf.
cache-from: type=gha
cache-to: type=gha,mode=max
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
# Baut das Flaro-Image für beide Architekturen und legt es in die Registry.
#
# **Der Weg von Hand** — für den Fall, dass kein Gitea-Runner läuft. Sonst
# macht das `.gitea/workflows/image.yml` bei jedem Push von selbst.
#
# Tools/image-veroeffentlichen.sh → :latest
# Tools/image-veroeffentlichen.sh 0.2.0 → :latest und :0.2.0
#
# Vorher einmalig anmelden — das Passwort tippst du selbst ein:
#
# docker login git.scarriffle.com
set -euo pipefail
REGISTRY="${FLARO_REGISTRY:-git.scarriffle.com}"
PFAD="${FLARO_IMAGE_PATH:-scarriffle/flaro}"
BILD="$REGISTRY/$PFAD"
FASSUNG="${1:-}"
cd "$(dirname "$0")/.."
command -v docker >/dev/null || { echo "docker fehlt."; exit 1; }
# **Ohne Anmeldung bricht der Push erst nach dem ganzen Bau ab.** Zwei
# Minuten Arbeit für eine Fehlermeldung, die am Anfang gestanden hätte.
# Kein `docker info | grep -q`: `grep -q` beendet die Pipe beim ersten
# Treffer, Docker bekommt SIGPIPE, und mit `set -o pipefail` gilt das als
# Fehlschlag. Die Prüfung meldete dann „Docker läuft nicht", während es lief.
if ! docker info >/dev/null 2>&1; then
echo "Docker läuft nicht."; exit 1
fi
if ! grep -q "$REGISTRY" ~/.docker/config.json 2>/dev/null; then
echo "Nicht an $REGISTRY angemeldet. Erst:"
echo
echo " docker login $REGISTRY"
exit 1
fi
MARKEN=(-t "$BILD:latest")
[ -n "$FASSUNG" ] && MARKEN+=(-t "$BILD:$FASSUNG")
# **Ein eigener Builder.** Der Vorgabe-Builder von Docker Desktop kann kein
# Manifest mit zwei Architekturen bauen; `docker buildx create` schon.
docker buildx inspect flaro-bauer >/dev/null 2>&1 \
|| docker buildx create --name flaro-bauer --use >/dev/null
echo "Baue $BILD für amd64 und arm64 — das dauert, arm64 läuft unter Emulation."
docker buildx build \
--builder flaro-bauer \
--platform linux/amd64,linux/arm64 \
-f server/Dockerfile \
"${MARKEN[@]}" \
--push .
echo
echo "Fertig. Auf dem NAS:"
echo " image: $BILD:${FASSUNG:-latest}"
+55 -18
View File
@@ -49,38 +49,75 @@ Drei Wege. Der erste ist der bequemste, der zweite braucht keine Registry.
Dein Gitea unter `git.scarriffle.com` bringt eine Container-Registry mit. Dein Gitea unter `git.scarriffle.com` bringt eine Container-Registry mit.
Einmal einrichten, danach ist jedes Update ein `docker compose pull`. Einmal einrichten, danach ist jedes Update ein `docker compose pull`.
Auf dem Arbeitsrechner: #### A1 · Gitea baut selbst
`.gitea/workflows/image.yml` liegt im Projekt. Bei jedem Push auf `main`
baut Gitea das Image für **beide** Architekturen und legt es ab — ohne dass
jemand daran denken muss, und ohne Anmeldung am Arbeitsrechner.
**Voraussetzung ist ein registrierter Act-Runner.** Hat dein Gitea keinen,
passiert nichts; dann gilt A2. Nachsehen unter
*Einstellungen → Actions → Runner*. Ein Runner lässt sich als Container
neben Gitea betreiben:
```bash
docker run -d --restart unless-stopped --name gitea-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
-e GITEA_INSTANCE_URL=https://git.scarriffle.com \
-e GITEA_RUNNER_REGISTRATION_TOKEN=<Token aus den Einstellungen> \
gitea/act_runner:latest
```
#### A2 · Von Hand vom Arbeitsrechner
Einmalig anmelden — das Passwort tippst du selbst ein:
```bash ```bash
docker login git.scarriffle.com docker login git.scarriffle.com
``` ```
Danach genügt ein Befehl; er baut amd64 **und** arm64 und lädt hoch:
```bash ```bash
cd ~/Projects_Coding/Flixy Tools/image-veroeffentlichen.sh
docker buildx build --platform linux/amd64 \
-f server/Dockerfile \
-t git.scarriffle.com/scarriffle/flaro:latest \
--push .
``` ```
`--push` lädt direkt hoch; ein getrenntes `docker push` entfällt. Mit einer Fassungsnummer, wenn du sie festnageln willst:
Auf dem NAS dann in der Compose-Datei: ```bash
Tools/image-veroeffentlichen.sh 0.2.0
```
#### A3 · Das Paket öffentlich stellen
Sonst muss sich auch das NAS anmelden. In Gitea unter
*dein Profil → Pakete → flaro → Einstellungen* auf öffentlich stellen. Der
Quelltext ist ohnehin offen; das Image enthält nichts, was er nicht auch
enthält.
#### A4 · Die Registry im NAS eintragen
In der Docker-App des UGREEN: **Repository → Zahnrad → Quellname des
Images → Hinzufügen**. Dort stehen schon DockerHub und `ghcr.io`; daneben
kommt:
| Feld | Wert |
|---|---|
| Alias | `gitea` |
| Link | `https://git.scarriffle.com/` |
**Bestätigen** — danach findet die Suche in der Docker-App auch
`scarriffle/flaro`. Ist das Paket privat, trägst du unter
*Registierungseinstellungen* Benutzername und ein Gitea-Zugriffstoken ein
(nicht dein Kontopasswort: ein Token lässt sich einzeln widerrufen, unter
*Einstellungen → Anwendungen → Zugriffstoken*, Berechtigung `read:package`).
In der Compose-Datei steht dann:
```yaml ```yaml
image: git.scarriffle.com/scarriffle/flaro:latest image: git.scarriffle.com/scarriffle/flaro:latest
``` ```
Ist das Paket in Gitea **privat**, muss sich auch das NAS anmelden:
```bash
docker login git.scarriffle.com
```
Einfacher ist, das Paket in Gitea auf öffentlich zu stellen — unter
*Pakete → flaro → Einstellungen*. Der Quelltext ist ohnehin offen; das Image
enthält nichts, was es nicht auch tut.
### Weg B — als Datei hochladen ### Weg B — als Datei hochladen
Ohne Registry, dafür bei jedem Update von Hand. Ohne Registry, dafür bei jedem Update von Hand.
+3 -1
View File
@@ -5,7 +5,9 @@
services: services:
flaro: flaro:
image: ghcr.io/scarriffle/flaro:latest # Die Registry des eigenen Gitea. Wer woanders veröffentlicht, trägt
# hier etwas anderes ein — siehe docs/NAS-EINRICHTEN.md.
image: git.scarriffle.com/scarriffle/flaro:latest
container_name: flaro container_name: flaro
restart: unless-stopped restart: unless-stopped