#!/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