159 lines
5.3 KiB
Bash
159 lines
5.3 KiB
Bash
#!/bin/bash
|
|
# Synct die Paket-Whitelist aus Git und laedt die selektierten Pakete
|
|
# aus den jeweils angegebenen Fremd-Repos.
|
|
# Erzeugt anschliessend RPM- und AppStream-Metadaten.
|
|
set -euo pipefail
|
|
|
|
CONFIG_DIR=/etc/mirror
|
|
CONFIG_REPO_URL="${CONFIG_REPO_URL:?CONFIG_REPO_URL ist nicht gesetzt}"
|
|
CONFIG_REPO_BRANCH="${CONFIG_REPO_BRANCH:-main}"
|
|
PACKAGES_FILE="$CONFIG_DIR/packages.yaml"
|
|
DEST=/data/repo/rpms
|
|
TMP_REPO_DIR=/etc/yum.repos.d
|
|
LOCKFILE=/tmp/sync.lock
|
|
APPSTREAM_GEN=/usr/local/bin/appstream-gen.py
|
|
|
|
exec 200>"$LOCKFILE"
|
|
if ! flock -n 200; then
|
|
echo "[sync] Ein anderer Sync-Lauf ist bereits aktiv, breche ab."
|
|
exit 0
|
|
fi
|
|
|
|
echo "[sync] $(date -Iseconds) Starte Sync-Lauf"
|
|
|
|
# --- 1. Whitelist aus Git holen ---------------------------------------
|
|
mkdir -p "$CONFIG_DIR"
|
|
|
|
if [ -n "${GIT_SSH_KEY_PATH:-}" ] && [ -f "${GIT_SSH_KEY_PATH:-}" ]; then
|
|
export GIT_SSH_COMMAND="ssh -i ${GIT_SSH_KEY_PATH} -o StrictHostKeyChecking=accept-new"
|
|
fi
|
|
|
|
if [ -d "$CONFIG_DIR/.git" ]; then
|
|
git -C "$CONFIG_DIR" fetch --depth=1 origin "$CONFIG_REPO_BRANCH"
|
|
git -C "$CONFIG_DIR" reset --hard "origin/$CONFIG_REPO_BRANCH"
|
|
else
|
|
git clone --depth=1 --branch "$CONFIG_REPO_BRANCH" "$CONFIG_REPO_URL" "$CONFIG_DIR"
|
|
fi
|
|
|
|
if [ ! -f "$PACKAGES_FILE" ]; then
|
|
echo "[sync] FEHLER: $PACKAGES_FILE nicht gefunden."
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$DEST"
|
|
|
|
# --- 2. Pro Quelle: temporaeres Repo aktivieren, Pakete gezielt ziehen --
|
|
source_count=$(yq -o=json '.sources | length' "$PACKAGES_FILE")
|
|
download_failed=0
|
|
|
|
for i in $(seq 0 $((source_count - 1))); do
|
|
src=$(yq -o=json ".sources[$i]" "$PACKAGES_FILE")
|
|
name=$(echo "$src" | jq -r '.name')
|
|
baseurl=$(echo "$src" | jq -r '.baseurl')
|
|
gpgkey=$(echo "$src" | jq -r '.gpgkey // empty')
|
|
|
|
# Standard: false. Nur bei echten Fremd-Repos aktivieren, deren
|
|
# Abhaengigkeiten die Clients nicht ueber ihre Basis-Repos bekommen.
|
|
resolve_deps=$(echo "$src" | jq -r '.resolve_deps // false')
|
|
|
|
# Paketeintraege koennen Strings ODER Mappings mit Metadaten sein.
|
|
mapfile -t packages < <(
|
|
echo "$src" | jq -r '.packages[] | if type == "string" then . else .name end'
|
|
)
|
|
|
|
if [ "${#packages[@]}" -eq 0 ]; then
|
|
echo "[sync] Quelle '$name': keine Pakete gelistet, ueberspringe."
|
|
continue
|
|
fi
|
|
|
|
echo "[sync] Quelle '$name': ${#packages[@]} Paket(e) -> $baseurl"
|
|
|
|
repofile="$TMP_REPO_DIR/tmp-${name}.repo"
|
|
{
|
|
echo "[tmp-${name}]"
|
|
echo "name=${name}"
|
|
echo "baseurl=${baseurl}"
|
|
echo "enabled=0"
|
|
if [ -n "$gpgkey" ]; then
|
|
echo "gpgcheck=1"
|
|
echo "gpgkey=${gpgkey}"
|
|
else
|
|
echo "gpgcheck=0"
|
|
fi
|
|
} > "$repofile"
|
|
|
|
# Die Basis-Repos des Containers bleiben aktiviert, damit dnf
|
|
# Abhaengigkeiten wie glibc oder libstdc++ ueberhaupt aufloesen kann.
|
|
# Ohne sie scheitert bereits die Aufloesung, nicht erst der Download.
|
|
dl_args=(--destdir="$DEST" --enablerepo="tmp-${name}")
|
|
|
|
if [ "$resolve_deps" = "true" ]; then
|
|
echo "[sync] Quelle '$name': Abhaengigkeiten werden mitgespiegelt"
|
|
# Bewusst OHNE --alldeps: bereits im Container installierte Pakete
|
|
# (glibc & Co.) haben die Clients ohnehin und muessen nicht ins Repo.
|
|
dl_args+=(--resolve)
|
|
fi
|
|
|
|
if ! dnf download "${dl_args[@]}" "${packages[@]}"; then
|
|
echo "[sync] WARNUNG: Download fuer Quelle '$name' fehlgeschlagen."
|
|
download_failed=1
|
|
fi
|
|
|
|
rm -f "$repofile"
|
|
done
|
|
|
|
# --- 3. Alte Paketversionen aufraeumen -----------------------------------
|
|
# repomanage braucht bereits vorhandene Metadaten. Beim allerersten Lauf
|
|
# gibt es noch keine, deshalb der Existenztest.
|
|
if [ -f "$DEST/repodata/repomd.xml" ]; then
|
|
echo "[sync] Raeume veraltete Paketversionen auf"
|
|
if ! dnf repomanage --old "$DEST" | xargs -r rm -f; then
|
|
echo "[sync] WARNUNG: repomanage fehlgeschlagen, ueberspringe Aufraeumen."
|
|
fi
|
|
else
|
|
echo "[sync] Erster Lauf, kein Aufraeumen noetig."
|
|
fi
|
|
|
|
# --- 4. RPM-Metadaten neu erzeugen ---------------------------------------
|
|
echo "[sync] Aktualisiere Repo-Metadaten"
|
|
createrepo_c --update "$DEST"
|
|
|
|
# --- 5. AppStream-Katalog erzeugen und einhaengen -------------------------
|
|
# Ohne diesen Schritt sind die Pakete zwar per dnf installierbar, tauchen
|
|
# aber weder in Discover noch in GNOME Software auf.
|
|
echo "[sync] Erzeuge AppStream-Katalog"
|
|
|
|
ASWORK=$(mktemp -d)
|
|
trap 'rm -rf "$ASWORK"' EXIT
|
|
|
|
# Welche Pakete liegen tatsaechlich im Repo? Verhindert, dass Discover
|
|
# Eintraege anzeigt, deren RPM gar nicht da ist.
|
|
if compgen -G "$DEST"/*.rpm > /dev/null; then
|
|
rpm -qp --qf '%{NAME}\n' "$DEST"/*.rpm 2>/dev/null \
|
|
| sort -u > "$ASWORK/vorhanden.txt"
|
|
else
|
|
echo "[sync] WARNUNG: Keine RPMs in $DEST gefunden."
|
|
: > "$ASWORK/vorhanden.txt"
|
|
fi
|
|
|
|
python3 "$APPSTREAM_GEN" "$PACKAGES_FILE" \
|
|
--output "$ASWORK/appstream.xml.gz" \
|
|
--available-packages "$ASWORK/vorhanden.txt"
|
|
|
|
# Optionale Validierung, falls appstreamcli im Image vorhanden ist.
|
|
if command -v appstreamcli > /dev/null 2>&1; then
|
|
if ! appstreamcli validate --no-net "$ASWORK/appstream.xml.gz"; then
|
|
echo "[sync] WARNUNG: AppStream-Katalog hat Validierungshinweise."
|
|
fi
|
|
fi
|
|
|
|
# modifyrepo_c MUSS nach createrepo_c laufen, da createrepo_c die
|
|
# repomd.xml komplett neu schreibt.
|
|
modifyrepo_c --mdtype=appstream "$ASWORK/appstream.xml.gz" "$DEST/repodata/"
|
|
|
|
if [ "$download_failed" -ne 0 ]; then
|
|
echo "[sync] $(date -Iseconds) Sync-Lauf mit Fehlern abgeschlossen"
|
|
exit 1
|
|
fi
|
|
|
|
echo "[sync] $(date -Iseconds) Sync-Lauf abgeschlossen" |