diff --git a/docker/sync/sync.sh b/docker/sync/sync.sh new file mode 100644 index 0000000..cea4caa --- /dev/null +++ b/docker/sync/sync.sh @@ -0,0 +1,198 @@ +#!/bin/bash +set -euo pipefail + +REPO_PATH="/srv/flatpak-mirror/repo" +CONFIG_SRC="/config/apps.yaml" +GIT_CACHE="/tmp/config-repo" +ARCH="x86_64" + +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } + +# --- 1. apps.yaml besorgen ------------------------------------------------- + +# Setzt HTTP-Basic-Auth fuer git ueber GIT_CONFIG_*-Umgebungsvariablen +# (git >= 2.31). Vorteil gegenueber "https://user:token@host/...": das Token +# steht 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 "${GIT_USERNAME:-}" ] && [ -n "${GIT_TOKEN:-}" ]; then + log "Git: verwende Token-Authentifizierung (Benutzer: ${GIT_USERNAME})" + export GIT_CONFIG_COUNT=1 + export GIT_CONFIG_KEY_0="http.extraHeader" + export GIT_CONFIG_VALUE_0="Authorization: Basic $(printf '%s' "${GIT_USERNAME}:${GIT_TOKEN}" | base64 | tr -d '\n')" + fi +} + +get_config() { + if [ -n "${GIT_REPO_URL:-}" ]; then + configure_git_auth + + if [ -d "$GIT_CACHE/.git" ]; then + log "Git: hole Updates aus ${GIT_REPO_URL}" + git -C "$GIT_CACHE" fetch --quiet origin "$GIT_BRANCH" + git -C "$GIT_CACHE" reset --hard --quiet "origin/${GIT_BRANCH}" + else + log "Git: klone ${GIT_REPO_URL}" + git clone --quiet --branch "$GIT_BRANCH" "$GIT_REPO_URL" "$GIT_CACHE" + fi + CONFIG_FILE="$GIT_CACHE/apps.yaml" + else + log "Kein GIT_REPO_URL gesetzt, nutze lokal gemountete ${CONFIG_SRC}" + CONFIG_FILE="$CONFIG_SRC" + fi + + if [ ! -f "$CONFIG_FILE" ]; then + log "FEHLER: apps.yaml nicht gefunden unter ${CONFIG_FILE}" + exit 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" + curl -sSL -o /tmp/flathub.gpg "$FLATHUB_GPG_URL" + ostree --repo="$REPO_PATH" remote add flathub "$FLATHUB_REMOTE_URL" \ + --gpg-import=/tmp/flathub.gpg + fi +} + +# --- 3. Explizit gewuenschte App-Refs aus apps.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// ---------- +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 -------------------------- +# 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}" + ostree --repo="$REPO_PATH" pull --mirror flathub "$ref" + + local checksum + checksum="$(resolve_pulled_checksum "$ref")" + if [ -z "$checksum" ]; then + log "FEHLER: Konnte Commit-Checksum fuer ${ref} nach dem Pull nicht ermitteln" + exit 1 + fi + ostree --repo="$REPO_PATH" refs --create="$ref" "$checksum" --force +} + +# 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. Gibt am Ende alle tatsaechlich benoetigten Refs aus +# (Apps + alle aufgeloesten Runtimes), damit sync_refs() weiss, was behalten +# werden soll. +resolve_and_pull_all() { + local -A seen=() + local queue=() + local ref dep + + 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 + + pull_one_ref "$ref" + + dep="$(get_runtime_dependency "$ref")" + if [ -n "$dep" ] && [ -z "${seen[$dep]:-}" ]; then + log " -> benoetigt zusaetzlich: ${dep}" + queue+=("$dep") + fi + done + + printf '%s\n' "${!seen[@]}" +} + +# --- 6. Abgleich: pullen was fehlt (inkl. Abhaengigkeiten), loeschen was --- +# --- nicht mehr gewuenscht ist ---------------------------------------------- +sync_refs() { + local wanted current + + log "Loese App-Refs und Abhaengigkeiten auf..." + wanted="$(resolve_and_pull_all)" + current="$(current_refs)" + + log "Verwaltete Refs (Apps + Runtimes):" + echo "$wanted" | sed 's/^/ + /' + + # 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}" + ostree --repo="$REPO_PATH" refs --delete "$ref" + fi + done <<< "$current" +} + +# --- 7. Summary aktualisieren ----------------------------------------------- +update_summary() { + log "Aktualisiere Repo-Summary und Static Deltas" + flatpak build-update-repo --generate-static-deltas "$REPO_PATH" +} + +run_once() { + get_config + init_repo + sync_refs + update_summary + log "Sync abgeschlossen." +} + +# --- 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 \ No newline at end of file