Initial commit
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
FROM debian:trixie-slim
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ostree \
|
||||
flatpak \
|
||||
git \
|
||||
openssh-client \
|
||||
curl \
|
||||
ca-certificates \
|
||||
gnupg \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# yq (mikefarah, Go-Binary) zum Parsen von flatpaks.yaml
|
||||
RUN curl -sSL -o /usr/local/bin/yq \
|
||||
https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 \
|
||||
&& chmod +x /usr/local/bin/yq
|
||||
|
||||
# Gemeinsamer Signing-Key: entrypoint.sh kopiert ihn beim Start aus dem
|
||||
# read-only gemounteten /run/secrets/gpg hierher.
|
||||
ENV GNUPGHOME=/etc/mirror-gpg
|
||||
|
||||
WORKDIR /app
|
||||
COPY gpg-import.sh /usr/local/lib/gpg-import.sh
|
||||
COPY entrypoint.sh /app/entrypoint.sh
|
||||
COPY sync.sh /app/sync.sh
|
||||
RUN chmod +x /app/entrypoint.sh /app/sync.sh
|
||||
|
||||
ENTRYPOINT ["/app/entrypoint.sh"]
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
# Uebernimmt den gemeinsamen, EXTERN erzeugten Signing-Key aus dem read-only
|
||||
# gemounteten Verzeichnis /run/secrets/gpg (auf dem Host: ./gpg neben der
|
||||
# docker-compose.yml) in ein containerlokales GNUPGHOME.
|
||||
#
|
||||
# Bewusst eine Kopie statt direkt darauf zu arbeiten: gpg schreibt in seinem
|
||||
# Homedir (trustdb, random_seed, Lockfiles), und derselbe Ordner ist
|
||||
# gleichzeitig im dnf-mirror-Container gemountet. Die Kopie haelt das
|
||||
# Original unveraendert und vermeidet, dass sich beide Container beim
|
||||
# Signieren gegenseitig ins Gehege kommen.
|
||||
set -euo pipefail
|
||||
|
||||
GPG_SOURCE=/run/secrets/gpg
|
||||
export GNUPGHOME="${GNUPGHOME:-/etc/mirror-gpg}"
|
||||
|
||||
: "${GPG_KEY_ID:?GPG_KEY_ID muss gesetzt sein (gemeinsamer Signing-Key, siehe .env)}"
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. /usr/local/lib/gpg-import.sh
|
||||
|
||||
import_shared_gpg
|
||||
require_usable_signing_key
|
||||
|
||||
exec /app/sync.sh
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
# Gemeinsame Hilfsfunktionen beider Spiegel-Container: den EXTERN erzeugten
|
||||
# Signing-Key aus dem read-only gemounteten $GPG_SOURCE uebernehmen und
|
||||
# pruefen, ob damit unbeaufsichtigt signiert werden kann.
|
||||
#
|
||||
# ACHTUNG: identische Kopie in dnf-mirror/gpg-import.sh - Aenderungen bitte in
|
||||
# beiden pflegen. (Getrennte Build-Kontexte je Image; ein gemeinsamer Kontext
|
||||
# wuerde das Schluesselverzeichnis ./gpg in den Build-Kontext ziehen.)
|
||||
|
||||
# Die Dateien, die ein GnuPG-Homedir ausmachen. Bewusst eine feste Liste statt
|
||||
# "cp -a .": im Quellverzeichnis liegen oft noch die Agent-Sockets
|
||||
# (S.gpg-agent & Co.) und Lock-Dateien eines gpg-agent, der auf dem HOST
|
||||
# lief/laeuft. Die haben im Container nichts verloren.
|
||||
GPG_HOME_ENTRIES=(pubring.kbx pubring.gpg secring.gpg trustdb.gpg \
|
||||
private-keys-v1.d openpgp-revocs.d public-keys.d)
|
||||
|
||||
import_shared_gpg() {
|
||||
if [ ! -d "$GPG_SOURCE" ]; then
|
||||
echo "[gpg] FEHLER: $GPG_SOURCE ist nicht gemountet - fehlt der Ordner ./gpg?" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
rm -rf "$GNUPGHOME"
|
||||
mkdir -p "$GNUPGHOME"
|
||||
chmod 700 "$GNUPGHOME"
|
||||
|
||||
local entry copied=0
|
||||
for entry in "${GPG_HOME_ENTRIES[@]}"; do
|
||||
[ -e "$GPG_SOURCE/$entry" ] || continue
|
||||
# Kein "|| true": schlaegt das Kopieren des Schluesselmaterials fehl,
|
||||
# soll das laut und sofort auffallen statt spaeter als "Key nicht
|
||||
# gefunden".
|
||||
cp -a "$GPG_SOURCE/$entry" "$GNUPGHOME/"
|
||||
copied=1
|
||||
done
|
||||
|
||||
if [ "$copied" -eq 0 ]; then
|
||||
echo "[gpg] FEHLER: in $GPG_SOURCE liegt kein GnuPG-Schluesselbund." >&2
|
||||
echo "[gpg] Erwartet wird der INHALT eines GnuPG-Homedirs (pubring.kbx," >&2
|
||||
echo "[gpg] private-keys-v1.d, ...), nicht z.B. eine exportierte .asc-Datei." >&2
|
||||
echo "[gpg] Tatsaechlicher Inhalt:" >&2
|
||||
ls -la "$GPG_SOURCE" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
chmod -R go-rwx "$GNUPGHOME"
|
||||
|
||||
# Signiert wird unbeaufsichtigt, also nie ein Pinentry-Fenster erwarten.
|
||||
echo "allow-loopback-pinentry" > "$GNUPGHOME/gpg-agent.conf"
|
||||
echo "pinentry-mode loopback" > "$GNUPGHOME/gpg.conf"
|
||||
}
|
||||
|
||||
# Listet die im Homedir tatsaechlich vorhandenen geheimen Schluessel als
|
||||
# "<fingerprint> <uid>" - genau das, was als GPG_KEY_ID in die .env gehoert.
|
||||
list_available_secret_keys() {
|
||||
gpg --batch --list-secret-keys --with-colons 2>/dev/null | awk -F: '
|
||||
$1 == "sec" { want = 1; fpr = ""; next }
|
||||
$1 == "fpr" && want { fpr = $10; want = 0; next }
|
||||
$1 == "uid" && fpr != "" { print " " fpr " " $10; fpr = ""; next }
|
||||
'
|
||||
}
|
||||
|
||||
# Prueft nicht nur, OB der Key da ist, sondern ob damit auch wirklich
|
||||
# unbeaufsichtigt signiert werden kann - ein passphrasegeschuetzter Key faellt
|
||||
# sonst erst mitten im ersten Sync-Lauf auf.
|
||||
require_usable_signing_key() {
|
||||
if ! gpg --batch --list-secret-keys "$GPG_KEY_ID" >/dev/null 2>&1; then
|
||||
echo "[gpg] FEHLER: kein geheimer Schluessel '$GPG_KEY_ID' in ./gpg gefunden." >&2
|
||||
local available
|
||||
available="$(list_available_secret_keys)"
|
||||
if [ -n "$available" ]; then
|
||||
echo "[gpg] Vorhanden sind:" >&2
|
||||
echo "$available" >&2
|
||||
echo "[gpg] Einen dieser Fingerprints als GPG_KEY_ID in die .env eintragen." >&2
|
||||
else
|
||||
echo "[gpg] Der Schluesselbund enthaelt ueberhaupt keinen GEHEIMEN Schluessel" >&2
|
||||
echo "[gpg] (nur oeffentliche?). private-keys-v1.d/ pruefen - dort muss" >&2
|
||||
echo "[gpg] mindestens eine .key-Datei liegen." >&2
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
local err
|
||||
err="$(printf 'signing-selftest' \
|
||||
| gpg --batch --yes --local-user "$GPG_KEY_ID" --detach-sign --output /dev/null - 2>&1)" && {
|
||||
echo "[gpg] Gemeinsamen Signing-Key '$GPG_KEY_ID' uebernommen, Testsignatur ok."
|
||||
return 0
|
||||
}
|
||||
|
||||
echo "[gpg] FEHLER: Schluessel '$GPG_KEY_ID' ist da, aber Signieren schlaegt fehl:" >&2
|
||||
echo "$err" >&2
|
||||
echo "[gpg] Haeufigste Ursache: der Key hat eine Passphrase. Unbeaufsichtigtes" >&2
|
||||
echo "[gpg] Signieren braucht einen Key OHNE Passphrase (siehe README)." >&2
|
||||
return 1
|
||||
}
|
||||
Executable
+348
@@ -0,0 +1,348 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# OSTree-Repo = Wurzel des Ausgabe-Volumes. nginx mountet genau dieses Volume
|
||||
# nach /usr/share/nginx/html/flatpak und liefert es unter /flatpak/ aus.
|
||||
REPO_PATH="/srv/flatpak"
|
||||
CACHE_DIR="/var/cache/flatpak-mirror"
|
||||
GIT_CACHE="$CACHE_DIR/sources-git"
|
||||
CONFIG_SRC="/config/flatpaks.yaml"
|
||||
ARCH="x86_64"
|
||||
|
||||
: "${MIRROR_DOMAIN:?MIRROR_DOMAIN muss gesetzt sein}"
|
||||
: "${GPG_KEY_ID:?GPG_KEY_ID muss gesetzt sein (gemeinsamer Signing-Key, siehe .env)}"
|
||||
: "${MIRROR_REPO_ID:=company-mirror}"
|
||||
: "${SOURCES_GIT_REF:=main}"
|
||||
: "${SYNC_INTERVAL_SECONDS:=900}"
|
||||
export GNUPGHOME="${GNUPGHOME:-/etc/mirror-gpg}"
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
|
||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
|
||||
|
||||
# --- 1. flatpaks.yaml besorgen ---------------------------------------------
|
||||
|
||||
# Setzt die Git-Authentifizierung fuer das gemeinsame Freigabe-Repo. Bei
|
||||
# HTTPS+Token ueber GIT_CONFIG_*-Umgebungsvariablen (git >= 2.31): das Token
|
||||
# steht so weder in der URL (die in Logs auftauchen koennte) noch als
|
||||
# Kommandozeilenargument (das per "ps aux" auf dem Host sichtbar waere).
|
||||
configure_git_auth() {
|
||||
unset GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0
|
||||
if [ -n "${SOURCES_GIT_USERNAME:-}" ] && [ -n "${SOURCES_GIT_TOKEN:-}" ]; then
|
||||
log "Git: verwende Token-Authentifizierung (Benutzer: ${SOURCES_GIT_USERNAME})"
|
||||
export GIT_CONFIG_COUNT=1
|
||||
export GIT_CONFIG_KEY_0="http.extraHeader"
|
||||
export GIT_CONFIG_VALUE_0="Authorization: Basic $(printf '%s' "${SOURCES_GIT_USERNAME}:${SOURCES_GIT_TOKEN}" | base64 | tr -d '\n')"
|
||||
fi
|
||||
if [ -n "${SOURCES_GIT_SSH_KEY:-}" ] && [ -f "${SOURCES_GIT_SSH_KEY}" ]; then
|
||||
log "Git: verwende SSH-Deploy-Key ${SOURCES_GIT_SSH_KEY}"
|
||||
export GIT_SSH_COMMAND="ssh -i ${SOURCES_GIT_SSH_KEY} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${CACHE_DIR}/known_hosts"
|
||||
fi
|
||||
}
|
||||
|
||||
get_config() {
|
||||
if [ -n "${SOURCES_GIT_URL:-}" ]; then
|
||||
configure_git_auth
|
||||
|
||||
if [ -d "$GIT_CACHE/.git" ]; then
|
||||
log "Git: hole Updates aus ${SOURCES_GIT_URL} (@ ${SOURCES_GIT_REF})"
|
||||
if ! git -C "$GIT_CACHE" fetch --quiet origin "$SOURCES_GIT_REF" \
|
||||
|| ! git -C "$GIT_CACHE" reset --hard --quiet "origin/${SOURCES_GIT_REF}"; then
|
||||
log "FEHLER: git fetch fehlgeschlagen."
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
log "Git: klone ${SOURCES_GIT_URL} (@ ${SOURCES_GIT_REF})"
|
||||
rm -rf "$GIT_CACHE"
|
||||
if ! git clone --quiet --branch "$SOURCES_GIT_REF" "$SOURCES_GIT_URL" "$GIT_CACHE"; then
|
||||
log "FEHLER: git clone fehlgeschlagen."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
CONFIG_FILE="$GIT_CACHE/flatpaks.yaml"
|
||||
else
|
||||
log "Kein SOURCES_GIT_URL gesetzt, nutze lokal gemountete ${CONFIG_SRC}"
|
||||
CONFIG_FILE="$CONFIG_SRC"
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
log "FEHLER: flatpaks.yaml nicht gefunden unter ${CONFIG_FILE}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 2. Repo initialisieren, falls noch nicht vorhanden --------------------
|
||||
init_repo() {
|
||||
mkdir -p "$REPO_PATH"
|
||||
if [ ! -f "$REPO_PATH/config" ]; then
|
||||
log "Initialisiere neues OSTree-Repo unter ${REPO_PATH}"
|
||||
ostree --repo="$REPO_PATH" init --mode=archive-z2
|
||||
fi
|
||||
|
||||
if ! ostree --repo="$REPO_PATH" remote list | grep -q '^flathub$'; then
|
||||
log "Richte Flathub als Remote ein"
|
||||
if ! curl -sSL -o /tmp/flathub.gpg "$FLATHUB_GPG_URL"; then
|
||||
log "FEHLER: Flathub-GPG-Key konnte nicht von ${FLATHUB_GPG_URL} geladen werden."
|
||||
return 1
|
||||
fi
|
||||
if ! ostree --repo="$REPO_PATH" remote add flathub "$FLATHUB_REMOTE_URL" \
|
||||
--gpg-import=/tmp/flathub.gpg; then
|
||||
log "FEHLER: Flathub-Remote konnte nicht eingerichtet werden."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 3. Explizit gewuenschte App-Refs aus flatpaks.yaml --------------------
|
||||
explicit_app_refs() {
|
||||
yq -r '.apps[] | "app/" + .id + "/'"$ARCH"'/" + .branch' "$CONFIG_FILE"
|
||||
}
|
||||
|
||||
# --- Hilfsfunktion: Runtime-Abhaengigkeit eines bereits gepullten Refs -----
|
||||
# --- aus der eingebetteten "metadata"-Datei auslesen (Zeile runtime=...) ---
|
||||
get_runtime_dependency() {
|
||||
local ref="$1"
|
||||
local meta dep
|
||||
meta="$(ostree --repo="$REPO_PATH" cat "$ref" /metadata 2>/dev/null || true)"
|
||||
[ -z "$meta" ] && return 0
|
||||
dep="$(echo "$meta" | grep -E '^runtime=' | head -n1 | cut -d= -f2-)"
|
||||
[ -z "$dep" ] && return 0
|
||||
echo "runtime/${dep}"
|
||||
}
|
||||
|
||||
# --- Hilfsfunktion: Commit-Checksum eines Refs finden, egal ob unter -------
|
||||
# --- refs/heads/ oder (Collection-ID-gebunden) refs/mirrors/<id>/ ----------
|
||||
resolve_pulled_checksum() {
|
||||
local ref="$1"
|
||||
if [ -f "$REPO_PATH/refs/heads/$ref" ]; then
|
||||
cat "$REPO_PATH/refs/heads/$ref"
|
||||
return 0
|
||||
fi
|
||||
find "$REPO_PATH/refs/mirrors" -type f -path "*/$ref" 2>/dev/null | head -n1 | xargs -r cat
|
||||
}
|
||||
|
||||
# --- 4. Aktuell lokal verwaltete Refs ermitteln (Apps + Runtimes) ----------
|
||||
current_refs() {
|
||||
ostree --repo="$REPO_PATH" refs | grep -E "^(app|runtime)/" || true
|
||||
}
|
||||
|
||||
# --- 5. Pull + rekursive Abhaengigkeitsaufloesung --------------------------
|
||||
# ACHTUNG: run_once() wird in der Hauptschleife als if-Bedingung aufgerufen.
|
||||
# Damit ist "set -e" fuer den gesamten Aufrufbaum darunter ausgehebelt (Bash
|
||||
# unterdrueckt den Abbruch in jedem Kontext, in dem der Rueckgabewert
|
||||
# ausgewertet wird - und zwar rekursiv). Fehler muessen hier deshalb
|
||||
# ausdruecklich geprueft und weitergereicht werden, sonst laeuft der Sync
|
||||
# stillschweigend weiter.
|
||||
|
||||
# Pullt einen einzelnen Ref und legt den passenden heads/-Ref an
|
||||
# (siehe Kommentar bei resolve_pulled_checksum weiter oben).
|
||||
pull_one_ref() {
|
||||
local ref="$1"
|
||||
log "Pull: ${ref}"
|
||||
if ! ostree --repo="$REPO_PATH" pull --mirror flathub "$ref"; then
|
||||
log "FEHLER: Pull von ${ref} fehlgeschlagen."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local checksum
|
||||
checksum="$(resolve_pulled_checksum "$ref")"
|
||||
if [ -z "$checksum" ]; then
|
||||
log "FEHLER: Konnte Commit-Checksum fuer ${ref} nach dem Pull nicht ermitteln."
|
||||
return 1
|
||||
fi
|
||||
if ! ostree --repo="$REPO_PATH" refs --create="$ref" "$checksum" --force; then
|
||||
log "FEHLER: Konnte heads/-Ref fuer ${ref} nicht anlegen."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ref hat die Form "app/ID/ARCH/BRANCH" oder "runtime/ID/ARCH/BRANCH".
|
||||
# Signiert wird mit dem gemeinsamen Key aus ./gpg - demselben, mit dem der
|
||||
# DNF-Spiegel seine RPMs signiert.
|
||||
local ref_id ref_arch ref_branch
|
||||
ref_id="$(echo "$ref" | cut -d/ -f2)"
|
||||
ref_arch="$(echo "$ref" | cut -d/ -f3)"
|
||||
ref_branch="$(echo "$ref" | cut -d/ -f4)"
|
||||
|
||||
# "flatpak build-sign" sucht die ID per Default unter app/ - eine Runtime
|
||||
# muss ausdruecklich als solche gekennzeichnet werden, sonst scheitert es
|
||||
# mit "Refspec 'app/<ID>/<ARCH>/<BRANCH>' not found" und die Runtime bleibt
|
||||
# unsigniert (Clients mit GPG-Pruefung koennen sie dann nicht installieren).
|
||||
local -a sign_args=(
|
||||
--arch="$ref_arch"
|
||||
--gpg-sign="$GPG_KEY_ID"
|
||||
--gpg-homedir="$GNUPGHOME"
|
||||
)
|
||||
[ "${ref%%/*}" = "runtime" ] && sign_args+=(--runtime)
|
||||
|
||||
log "Signiere ${ref} mit dem Spiegel-Key"
|
||||
if ! flatpak build-sign "$REPO_PATH" "$ref_id" "$ref_branch" "${sign_args[@]}"; then
|
||||
log "FEHLER: Signieren von ${ref} fehlgeschlagen."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Ergebnis von resolve_and_pull_all(): alle Refs, die der Spiegel fuehren soll
|
||||
# (freigegebene Apps + alle aufgeloesten Runtimes).
|
||||
#
|
||||
# Bewusst eine globale Variable statt einer Ausgabe auf stdout: in der
|
||||
# Funktion laufen ostree und flatpak, deren eigene Ausgabe sonst ueber die
|
||||
# Kommandosubstitution mit ins "Ergebnis" geraet - im Log tauchten dann
|
||||
# saemtliche Fortschrittszeilen als vermeintliche Refs auf.
|
||||
WANTED_REFS=()
|
||||
|
||||
# Nimmt die explizit freigegebenen App-Refs, pullt sie, liest jeweils die
|
||||
# Runtime-Abhaengigkeit aus und haengt sie hinten an die Warteschlange an -
|
||||
# so werden auch mehrstufige Abhaengigkeiten (App -> Runtime -> Basis-Runtime)
|
||||
# vollstaendig aufgeloest.
|
||||
#
|
||||
# Ein einzelner fehlgeschlagener Ref bricht den Lauf nicht ab: die uebrigen
|
||||
# Apps werden trotzdem aktualisiert, der Ref behaelt seinen letzten guten
|
||||
# Stand (er bleibt in WANTED_REFS und wird deshalb auch nicht geloescht) und
|
||||
# der Rueckgabewert markiert den Lauf als fehlgeschlagen.
|
||||
resolve_and_pull_all() {
|
||||
local -A seen=()
|
||||
local queue=()
|
||||
local ref dep status=0
|
||||
|
||||
WANTED_REFS=()
|
||||
|
||||
while IFS= read -r ref; do
|
||||
[ -z "$ref" ] && continue
|
||||
queue+=("$ref")
|
||||
done < <(explicit_app_refs)
|
||||
|
||||
while [ "${#queue[@]}" -gt 0 ]; do
|
||||
ref="${queue[0]}"
|
||||
queue=("${queue[@]:1}")
|
||||
|
||||
[ -n "${seen[$ref]:-}" ] && continue
|
||||
seen["$ref"]=1
|
||||
|
||||
if ! pull_one_ref "$ref"; then
|
||||
log "${ref} wird uebersprungen, letzter guter Stand bleibt erhalten."
|
||||
status=1
|
||||
fi
|
||||
|
||||
# Die Runtime-Abhaengigkeit auch dann aufloesen, wenn der Ref gerade
|
||||
# fehlgeschlagen ist: sonst fehlte sie in WANTED_REFS und der Abgleich
|
||||
# in sync_refs() wuerde eine laengst gespiegelte Runtime loeschen, die
|
||||
# von der App weiterhin gebraucht wird.
|
||||
dep="$(get_runtime_dependency "$ref")"
|
||||
if [ -n "$dep" ] && [ -z "${seen[$dep]:-}" ]; then
|
||||
log " -> benoetigt zusaetzlich: ${dep}"
|
||||
queue+=("$dep")
|
||||
fi
|
||||
done
|
||||
|
||||
[ "${#seen[@]}" -gt 0 ] && WANTED_REFS=("${!seen[@]}")
|
||||
return "$status"
|
||||
}
|
||||
|
||||
# --- 6. Abgleich: pullen was fehlt (inkl. Abhaengigkeiten), loeschen was ---
|
||||
# --- nicht mehr gewuenscht ist ----------------------------------------------
|
||||
sync_refs() {
|
||||
local wanted current ref status=0
|
||||
|
||||
log "Loese App-Refs und Abhaengigkeiten auf..."
|
||||
resolve_and_pull_all || status=1
|
||||
|
||||
# Schutz gegen eine leere/kaputte flatpaks.yaml (oder ein fehlgeschlagenes
|
||||
# yq): ohne diese Pruefung waere die Liste der gewuenschten Refs leer und
|
||||
# der Abgleich unten wuerde JEDEN Ref aus dem Spiegel loeschen.
|
||||
if [ "${#WANTED_REFS[@]}" -eq 0 ]; then
|
||||
log "FEHLER: flatpaks.yaml ergibt keinen einzigen App-Ref - es wird nichts"
|
||||
log "geloescht, der bisherige Stand des Repos bleibt unveraendert."
|
||||
return 1
|
||||
fi
|
||||
|
||||
wanted="$(printf '%s\n' "${WANTED_REFS[@]}")"
|
||||
current="$(current_refs)"
|
||||
|
||||
log "Verwaltete Refs (Apps + Runtimes):"
|
||||
printf ' + %s\n' "${WANTED_REFS[@]}"
|
||||
|
||||
# Refs entfernen, die weder explizit freigegeben noch als Abhaengigkeit
|
||||
# noch benoetigt werden
|
||||
while IFS= read -r ref; do
|
||||
[ -z "$ref" ] && continue
|
||||
if ! grep -qxF "$ref" <<< "$wanted"; then
|
||||
log "Entferne nicht mehr benoetigten Ref: ${ref}"
|
||||
if ! ostree --repo="$REPO_PATH" refs --delete "$ref"; then
|
||||
log "FEHLER: Ref ${ref} konnte nicht geloescht werden."
|
||||
status=1
|
||||
fi
|
||||
fi
|
||||
done <<< "$current"
|
||||
|
||||
return "$status"
|
||||
}
|
||||
|
||||
# --- 7. Summary aktualisieren -----------------------------------------------
|
||||
update_summary() {
|
||||
log "Aktualisiere Repo-Summary und Static Deltas"
|
||||
if ! flatpak build-update-repo \
|
||||
--generate-static-deltas \
|
||||
--gpg-sign="$GPG_KEY_ID" \
|
||||
--gpg-homedir="$GNUPGHOME" \
|
||||
"$REPO_PATH"; then
|
||||
log "FEHLER: Signierte Summary konnte nicht erstellt werden"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# --- 8. Client-Dateien schreiben --------------------------------------------
|
||||
# Public Key + fertige .flatpakrepo-Datei, damit Clients den Spiegel mit
|
||||
# einem Kommando und MIT Signaturpruefung einbinden koennen. Beides liegt im
|
||||
# Repo-Verzeichnis und ist damit unter https://<domain>/flatpak/... abrufbar
|
||||
# (Flathub macht es mit flathub.gpg genauso).
|
||||
publish_client_files() {
|
||||
local keyfile="$REPO_PATH/${MIRROR_REPO_ID}.gpg"
|
||||
|
||||
if ! gpg --batch --yes --export --output "$keyfile" "$GPG_KEY_ID"; then
|
||||
log "FEHLER: Public Key konnte nicht exportiert werden."
|
||||
return 1
|
||||
fi
|
||||
|
||||
cat > "$REPO_PATH/${MIRROR_REPO_ID}.flatpakrepo" <<EOF
|
||||
[Flatpak Repo]
|
||||
Title=${MIRROR_REPO_ID}
|
||||
Url=https://${MIRROR_DOMAIN}/flatpak/
|
||||
Homepage=https://${MIRROR_DOMAIN}/
|
||||
Comment=Kuratierter Flatpak-Spiegel
|
||||
GPGKey=$(base64 -w0 < "$keyfile")
|
||||
EOF
|
||||
log "Client-Dateien geschrieben: ${MIRROR_REPO_ID}.gpg, ${MIRROR_REPO_ID}.flatpakrepo"
|
||||
}
|
||||
|
||||
run_once() {
|
||||
local status=0
|
||||
|
||||
get_config || return 1
|
||||
init_repo || return 1
|
||||
|
||||
# Ein einzelner nicht spiegelbarer Ref soll die uebrigen Apps nicht
|
||||
# aufhalten: Summary und Client-Dateien werden trotzdem geschrieben, der
|
||||
# Lauf gilt am Ende aber als fehlgeschlagen und wird wiederholt.
|
||||
sync_refs || status=1
|
||||
|
||||
update_summary || return 1
|
||||
publish_client_files || return 1
|
||||
|
||||
if [ "$status" -eq 0 ]; then
|
||||
log "Sync abgeschlossen."
|
||||
else
|
||||
log "Sync abgeschlossen, aber mit Fehlern - siehe oben."
|
||||
fi
|
||||
return "$status"
|
||||
}
|
||||
|
||||
# --- Hauptschleife ----------------------------------------------------------
|
||||
log "Starte Sync-Loop (Intervall: ${SYNC_INTERVAL_SECONDS}s)"
|
||||
while true; do
|
||||
if run_once; then
|
||||
log "Naechster Lauf in ${SYNC_INTERVAL_SECONDS}s"
|
||||
else
|
||||
log "Sync-Lauf fehlgeschlagen, versuche es beim naechsten Intervall erneut"
|
||||
fi
|
||||
sleep "$SYNC_INTERVAL_SECONDS"
|
||||
done
|
||||
Reference in New Issue
Block a user