From 38ddae0d85c3e93efaf7c2edae098753b49fd807 Mon Sep 17 00:00:00 2001 From: "David M. Klein" <4+leander19961@noreply.localhost> Date: Thu, 3 Sep 2026 14:25:32 +0200 Subject: [PATCH] =?UTF-8?q?Verzeichnis=20'mirror=20'=20l=C3=B6schen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mirror/Dockerfile | 65 --- .../almalinux-baseline.repo | 44 -- mirror/entrypoint.sh | 12 - mirror/run-as-mirror.sh | 62 --- mirror/sync.sh | 526 ------------------ 5 files changed, 709 deletions(-) delete mode 100644 mirror/Dockerfile delete mode 100644 mirror/config/alma-baseline.repo.d/almalinux-baseline.repo delete mode 100644 mirror/entrypoint.sh delete mode 100644 mirror/run-as-mirror.sh delete mode 100644 mirror/sync.sh diff --git a/mirror/Dockerfile b/mirror/Dockerfile deleted file mode 100644 index 0901fff..0000000 --- a/mirror/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -FROM almalinux:10 - -# dnf-plugins-core -> dnf download, repoquery, config-manager -# createrepo_c -> Repo-Metadaten bauen -# rpm-sign, gnupg2 -> Pakete umsignieren -# git, openssh-clients -> packages.yaml vor jedem Sync aus einem Git-Repo ziehen -# python3-pyyaml, jq -> packages.yaml (YAML) nach JSON wandeln und auswerten -# cpio -> Pakete fuer die AppStream-Erzeugung teilweise entpacken -# rsync -> privaten Paketstand pflegen + alle Fremdrepos zu -# einem gemeinsamen Repo zusammenfuehren -# util-linux-core -> stellt setpriv (root -> mirror-User droppen) sicher -RUN dnf -y install epel-release \ - && dnf -y install \ - dnf-plugins-core \ - createrepo_c \ - rpm-sign \ - gnupg2 \ - git \ - openssh-clients \ - python3-pyyaml \ - util-linux-core \ - findutils \ - cpio \ - rsync \ - procps-ng \ - jq \ - && dnf clean all - -# AppStream-Metadaten fuer Discover/GNOME Software: das noetige Werkzeug -# unterscheidet sich je nach Alma-Version. -# - AlmaLinux 9: appstream-glib (aus EPEL) liefert `appstream-builder`, -# das direkt ein Verzeichnis voller .rpm-Dateien einliest. -# - AlmaLinux 10: appstream-glib ist (Stand jetzt) weder in AlmaLinux 10 -# selbst noch in EPEL 10 paketiert. Stattdessen wird `appstreamcli` -# genutzt - dessen "compose"-Kommando ist aber NICHT im Basispaket -# `appstream` enthalten, sondern im separaten Subpaket -# `appstream-compose` (liegt im selben AlmaLinux-10-AppStream-Repo). -# Ohne dieses Subpaket bricht `appstreamcli compose` zur Laufzeit mit -# "AppStream Compose binary ... was not found" ab. -# sync.sh erkennt zur Laufzeit, welches der beiden Werkzeuge vorhanden ist. -# Ist keins von beiden installierbar, bricht der Build NICHT ab - die -# Discover-Katalogerzeugung wird dann zur Laufzeit einfach uebersprungen -# (Pakete bleiben trotzdem ganz normal per dnf/pkcon installierbar). -RUN (dnf -y install appstream-glib || dnf -y install appstream appstream-compose || dnf -y install appstream || \ - echo "WARNUNG (Build): weder appstream-glib noch appstream verfuegbar - AppStream/Discover-Metadaten werden zur Laufzeit uebersprungen.") \ - && dnf clean all - -RUN useradd -r -m -d /var/lib/mirror-gpg mirror - -COPY entrypoint.sh /usr/local/bin/entrypoint.sh -COPY run-as-mirror.sh /usr/local/bin/run-as-mirror.sh -COPY sync.sh /usr/local/bin/sync.sh -RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/run-as-mirror.sh /usr/local/bin/sync.sh - -RUN mkdir -p /srv/dnf /var/cache/mirror /var/lib/mirror-gpg \ - && chown -R mirror:mirror /srv/dnf /var/cache/mirror /var/lib/mirror-gpg - -ENV HOME=/var/lib/mirror-gpg -ENV GNUPGHOME=/var/lib/mirror-gpg/.gnupg - -# Bleibt root: entrypoint.sh chownt den bind-gemounteten ./gpg-Ordner (der dem -# Host gehoert, nicht dem Image) und wechselt danach per setpriv fest zu -# "mirror". Alles Weitere (Sync-Loop, git, dnf download, gpg) laeuft -# unprivilegiert. -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/mirror/config/alma-baseline.repo.d/almalinux-baseline.repo b/mirror/config/alma-baseline.repo.d/almalinux-baseline.repo deleted file mode 100644 index e58021c..0000000 --- a/mirror/config/alma-baseline.repo.d/almalinux-baseline.repo +++ /dev/null @@ -1,44 +0,0 @@ -# OPTIONAL. Standardmaessig fragt sync.sh fuer die "was kann Alma sowieso -# schon aufloesen?"-Pruefung einfach die im Mirror-Image bereits vorhandenen -# System-Repos ab (das Image ist selbst AlmaLinux, hat also baseos/appstream/ -# extras/crb + epel schon konfiguriert) - dafuer ist diese Datei NICHT noetig. -# -# Setze die Env-Variable ALMA_BASELINE_REPOSDIR=/etc/mirror/alma-baseline.repo.d, -# wenn du stattdessen explizit gegen andere/eigene Repo-Definitionen pruefen -# willst (z.B. anderer Releasever, eigener interner Alma-Spiegel). Es wird -# daraus nie etwas gespiegelt, nur `dnf repoquery` dagegen ausgefuehrt. - -[baseos] -name=AlmaLinux $releasever - BaseOS -mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos -gpgcheck=1 -gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-10 -enabled=1 - -[appstream] -name=AlmaLinux $releasever - AppStream -mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/appstream -gpgcheck=1 -gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-10 -enabled=1 - -[extras] -name=AlmaLinux $releasever - Extras -mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/extras -gpgcheck=1 -gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-10 -enabled=1 - -[crb] -name=AlmaLinux $releasever - CRB -mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/crb -gpgcheck=1 -gpgkey=https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-10 -enabled=1 - -[epel] -name=Extra Packages for Enterprise Linux $releasever - $basearch -metalink=https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch&infra=$infra&content=$contentdir -gpgcheck=1 -gpgkey=https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-10 -enabled=1 diff --git a/mirror/entrypoint.sh b/mirror/entrypoint.sh deleted file mode 100644 index 5444f7f..0000000 --- a/mirror/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Startet als root, damit der bind-gemountete ./gpg-Ordner (der auf dem Host -# meist root oder dem einladenden User gehoert) dem "mirror"-User im Container -# gehoert - danach wird per setpriv dauerhaft zu diesem unprivilegierten User -# gewechselt (ersetzt den Prozess direkt, kein Wrapper-Kindprozess, damit -# Signale/PID 1 sauber bleiben). -set -euo pipefail - -mkdir -p /var/lib/mirror-gpg /srv/dnf /var/cache/mirror -chown -R mirror:mirror /var/lib/mirror-gpg /srv/dnf /var/cache/mirror - -exec setpriv --reuid=mirror --regid=mirror --init-groups /usr/local/bin/run-as-mirror.sh diff --git a/mirror/run-as-mirror.sh b/mirror/run-as-mirror.sh deleted file mode 100644 index fdc9469..0000000 --- a/mirror/run-as-mirror.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -# Laeuft als unprivilegierter User "mirror" (siehe entrypoint.sh, der als -# root die Verzeichnisrechte setzt und dann per setpriv hierher wechselt). -set -euo pipefail - -: "${GPG_NAME:?GPG_NAME muss gesetzt sein}" -: "${GPG_EMAIL:?GPG_EMAIL muss gesetzt sein}" -: "${SYNC_INTERVAL_SECONDS:=21600}" - -mkdir -p "$GNUPGHOME" -chmod 700 "$GNUPGHOME" -echo "allow-loopback-pinentry" > "$GNUPGHOME/gpg-agent.conf" -echo "pinentry-mode loopback" > "$GNUPGHOME/gpg.conf" - -PASSPHRASE_FILE=/run/secrets/gpg_passphrase -if [ -z "${GPG_PASSPHRASE:-}" ] && [ ! -f "$PASSPHRASE_FILE" ]; then - PASSPHRASE_FILE="" -fi - -# GPG-Key nur beim allerersten Start erzeugen. Das gnupg-Home liegt im -# bind-gemounteten ./gpg-Ordner neben der docker-compose.yml - der Key (und -# damit der Public Key, den alle Clients einmalig vertrauen) bleibt ueber -# Container-Neustarts/-Updates hinweg gleich UND ist direkt auf dem Host -# sichtbar/sicherbar, statt in einem anonymen Docker-Volume zu stecken. -if ! gpg --batch --list-secret-keys "$GPG_NAME" >/dev/null 2>&1; then - echo "[entrypoint] Erzeuge neuen Signing-Key fuer '$GPG_NAME <$GPG_EMAIL>' ..." - KEYPARAMS=$(mktemp) - { - echo "%echo Generating mirror signing key" - echo "Key-Type: RSA" - echo "Key-Length: 4096" - echo "Name-Real: $GPG_NAME" - echo "Name-Email: $GPG_EMAIL" - echo "Expire-Date: 0" - if [ -n "$PASSPHRASE_FILE" ]; then - echo "Passphrase: $(cat "$PASSPHRASE_FILE")" - else - echo "%no-protection" - fi - echo "%commit" - } > "$KEYPARAMS" - gpg --batch --gen-key "$KEYPARAMS" - rm -f "$KEYPARAMS" -else - echo "[entrypoint] Signing-Key existiert bereits, wird wiederverwendet." -fi - -mkdir -p /srv/dnf -gpg --batch --yes --armor --export "$GPG_NAME" > /srv/dnf/RPM-GPG-KEY-mirror -echo "[entrypoint] Public Key nach /srv/dnf/RPM-GPG-KEY-mirror exportiert." - -term_handler() { - echo "[entrypoint] Beende nach aktuellem Sync-Lauf ..." - exit 0 -} -trap term_handler SIGTERM SIGINT - -while true; do - /usr/local/bin/sync.sh || echo "[entrypoint] sync.sh ist mit Fehler beendet, naechster Versuch in ${SYNC_INTERVAL_SECONDS}s" - sleep "$SYNC_INTERVAL_SECONDS" & - wait $! -done diff --git a/mirror/sync.sh b/mirror/sync.sh deleted file mode 100644 index 27ae619..0000000 --- a/mirror/sync.sh +++ /dev/null @@ -1,526 +0,0 @@ -#!/bin/bash -# Ein Sync-Durchlauf: -# 0. packages.yaml frisch aus Git ziehen (Fremdrepos + Paketnamen je Repo) -# 1. je Fremdrepo genau die dort gelisteten Pakete per `dnf download` holen, -# filtern (Alma-Baseline) und in einen privaten, pro Fremdrepo getrennten -# Paket-Cache signieren -# 2. ALLE Fremdrepos zu EINEM gemeinsamen, oeffentlichen Ausgabe-Repo -# zusammenfuehren (ein repodata, ein GPG-Key, eine Client-.repo-Datei) - -# Clients binden nur dieses eine Repo ein, unabhaengig davon, aus wie -# vielen Fremdrepos die Pakete urspruenglich stammen -# 3. Repo-Metadaten (createrepo_c) + AppStream-Metadaten (fuer Discover) bauen -# 4. repomd.xml signieren, Client-.repo-Datei schreiben -set -uo pipefail - -CACHE=/var/cache/mirror -OUT=/srv/dnf -SOURCES_GIT_CHECKOUT="$CACHE/sources-git" -PACKAGES_JSON="$CACHE/packages.json" -SIGNED_ROOT="$CACHE/signed" - -: "${GPG_NAME:?}" -: "${MIRROR_DOMAIN:?}" -: "${SOURCES_GIT_URL:?SOURCES_GIT_URL muss auf das Git-Repo mit packages.yaml zeigen}" -: "${SOURCES_GIT_REF:=main}" -: "${MIRROR_REPO_ID:=company-mirror}" -: "${ALMA_RELEASEVER:=10}" -: "${ALMA_BASEARCH:=x86_64}" -: "${ALMA_BASELINE_REPOIDS:=baseos,appstream,extras,crb,epel}" - -mkdir -p "$CACHE/stage" "$CACHE/appstream-cache" "$CACHE/tmp-repos" "$SIGNED_ROOT" "$OUT" - -log() { echo "[sync $(date '+%F %T')] $*"; } - -# Zieht packages.yaml vor JEDEM Sync-Lauf frisch aus einem Git-Repo (statt aus -# lokal gemounteten Dateien), damit Aenderungen an der Paketliste ohne -# Container-Neubau/-Neustart wirksam werden. -fetch_packages_yaml() { - if [ -n "${SOURCES_GIT_SSH_KEY:-}" ] && [ -f "$SOURCES_GIT_SSH_KEY" ]; then - export GIT_SSH_COMMAND="ssh -i ${SOURCES_GIT_SSH_KEY} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=${CACHE}/known_hosts" - fi - - if [ -d "$SOURCES_GIT_CHECKOUT/.git" ]; then - log "Aktualisiere packages.yaml aus Git ($SOURCES_GIT_URL @ $SOURCES_GIT_REF) ..." - if git -C "$SOURCES_GIT_CHECKOUT" fetch --depth 1 origin "$SOURCES_GIT_REF" >>"$CACHE/git-sources.log" 2>&1 \ - && git -C "$SOURCES_GIT_CHECKOUT" reset --hard FETCH_HEAD >>"$CACHE/git-sources.log" 2>&1 \ - && git -C "$SOURCES_GIT_CHECKOUT" clean -fdx >>"$CACHE/git-sources.log" 2>&1; then - : - else - log "WARNUNG: git fetch fehlgeschlagen, siehe $CACHE/git-sources.log - verwende letzten bekannten Stand von packages.yaml weiter." - fi - else - log "Klone packages.yaml aus Git ($SOURCES_GIT_URL @ $SOURCES_GIT_REF) ..." - rm -rf "$SOURCES_GIT_CHECKOUT" - if ! git clone --branch "$SOURCES_GIT_REF" --single-branch --depth 1 \ - "$SOURCES_GIT_URL" "$SOURCES_GIT_CHECKOUT" >>"$CACHE/git-sources.log" 2>&1; then - log "FEHLER: Erstklon von packages.yaml aus Git fehlgeschlagen, siehe $CACHE/git-sources.log" - return 1 - fi - fi - - local yaml_file="$SOURCES_GIT_CHECKOUT/packages.yaml" - if [ ! -f "$yaml_file" ]; then - log "FEHLER: $yaml_file existiert nicht im Git-Repo." - return 1 - fi - - if ! python3 -c ' -import sys, json, yaml -with open(sys.argv[1]) as f: - data = yaml.safe_load(f) or {} -data.setdefault("repos", []) -data.setdefault("force_include", []) -json.dump(data, sys.stdout) -' "$yaml_file" > "$PACKAGES_JSON.tmp" 2>"$CACHE/packages-yaml-parse.log"; then - log "FEHLER: packages.yaml ist kein gueltiges YAML, siehe $CACHE/packages-yaml-parse.log" - rm -f "$PACKAGES_JSON.tmp" - return 1 - fi - mv "$PACKAGES_JSON.tmp" "$PACKAGES_JSON" -} - -refresh_alma_names() { - log "Ermittle Paketnamen, die AlmaLinux selbst bereitstellt ..." - local -a enable_args=() - local id - IFS=',' read -ra ids <<< "$ALMA_BASELINE_REPOIDS" - for id in "${ids[@]}"; do - enable_args+=(--enablerepo="$id") - done - - local -a reposdir_args=() - if [ -n "${ALMA_BASELINE_REPOSDIR:-}" ] && ls "${ALMA_BASELINE_REPOSDIR}"/*.repo >/dev/null 2>&1; then - reposdir_args=(--setopt="reposdir=${ALMA_BASELINE_REPOSDIR}") - fi - - if ! dnf repoquery -y --quiet -a --qf '%{name}' \ - --disablerepo='*' "${enable_args[@]}" \ - --releasever="$ALMA_RELEASEVER" --forcearch="$ALMA_BASEARCH" \ - "${reposdir_args[@]}" 2>>"$CACHE/repoquery.log" | sort -u > "$CACHE/alma-names.txt.tmp"; then - log "WARNUNG: repoquery gegen AlmaLinux-Baseline fehlgeschlagen, siehe $CACHE/repoquery.log" - log "Verwende ggf. vorherigen Stand von alma-names.txt weiter." - rm -f "$CACHE/alma-names.txt.tmp" - return - fi - mv "$CACHE/alma-names.txt.tmp" "$CACHE/alma-names.txt" - log "$(wc -l < "$CACHE/alma-names.txt") Paketnamen als 'ueber Alma aufloesbar' erfasst." -} - -is_force_included() { - local name="$1" f - for f in "${FORCE_INCLUDE_NAMES[@]}"; do - [ "$f" = "$name" ] && return 0 - done - return 1 -} - -# Liest einen einzelnen Schluessel aus dem [Desktop Entry]-Abschnitt einer -# .desktop-Datei (ignoriert lokalisierte Varianten wie Name[de]= und andere -# Abschnitte wie [Desktop Action ...]). -desktop_get() { - local file="$1" key="$2" - awk -v key="$key" ' - /^\[Desktop Entry\]/ { insec=1; next } - /^\[/ { insec=0 } - insec && index($0, key "=") == 1 { sub("^" key "=", ""); print; exit } - ' "$file" -} - -xml_escape() { - local s="$1" - s="${s//&/&}" - s="${s///>}" - s="${s//\"/"}" - s="${s//\'/'}" - printf '%s' "$s" -} - -# appstreamcli compose (AppStream 1.0+) ignoriert .desktop-Dateien komplett, -# wenn keine begleitende .metainfo.xml existiert - anders als das aeltere -# appstream-builder, das genau das frueher automatisch synthetisiert hat. -# Die meisten unabhaengigen Drittanbieter-RPMs (z.B. jpackage-basierte Apps -# wie XPipe) liefern aber nur eine .desktop-Datei mit. Deshalb hier selbst -# eine minimale Ersatz-Metainfo aus den Desktop-Datei-Feldern erzeugen, damit -# die Anwendung ueberhaupt einen Katalogeintrag in Discover bekommt. Bringt -# ein Paket schon eine echte .metainfo.xml mit (die auf dasselbe Desktop-File -# verweist), hat diese immer Vorrang - hier wird dann nichts generiert. -generate_fallback_metainfo() { - local composeroot="$1" pkgmap="$2" - local appdir="$composeroot/usr/share/applications" - local metadir="$composeroot/usr/share/metainfo" - local desktop_file base name summary categories cat pkgname - - [ -d "$appdir" ] || return - mkdir -p "$metadir" - - for desktop_file in "$appdir"/*.desktop; do - [ -e "$desktop_file" ] || continue - base=$(basename "$desktop_file") - - if grep -rlq "type=\"desktop-id\">${base}<" "$metadir" 2>/dev/null; then - continue - fi - - pkgname="" - [ -f "$pkgmap" ] && pkgname=$(awk -F'\t' -v b="$base" '$1 == b {print $2; exit}' "$pkgmap") - [ -z "$pkgname" ] && pkgname="${base%.desktop}" - - name=$(desktop_get "$desktop_file" "Name") - summary=$(desktop_get "$desktop_file" "Comment") - categories=$(desktop_get "$desktop_file" "Categories") - [ -z "$name" ] && name="${base%.desktop}" - [ -z "$summary" ] && summary="$name" - - { - echo '' - echo '' - echo " ${base}" - echo " $(xml_escape "$pkgname")" - echo ' CC0-1.0' - echo ' LicenseRef-proprietary' - echo " $(xml_escape "$name")" - echo " $(xml_escape "$summary")" - echo " " - echo "

$(xml_escape "$summary")

" - echo "
" - echo " ${base}" - if [ -n "$categories" ]; then - echo ' ' - local IFS=';' - for cat in $categories; do - [ -z "$cat" ] && continue - echo " $(xml_escape "$cat")" - done - echo ' ' - fi - echo '
' - } > "$metadir/${base%.desktop}.metainfo.xml" - done -} - -# AlmaLinux 9: appstream-glib (aus EPEL) liest ein Verzeichnis voller -# .rpm-Dateien direkt ein. -generate_appstream_via_builder() { - local id="$1" out="$2" - local asout="$CACHE/appstream/$id" - mkdir -p "$asout" - if appstream-builder \ - --origin="$id" --basename="$id" \ - --cache-dir="$CACHE/appstream-cache/$id" \ - --packages-dir="$out" \ - --output-dir="$asout" \ - --min-icon-size=48 --enable-hidpi \ - >>"$CACHE/appstream-$id.log" 2>&1; then - [ -f "$asout/$id.xml.gz" ] && modifyrepo_c --mdtype=appstream "$asout/$id.xml.gz" "$out/repodata" - [ -f "$asout/$id-icons.tar.gz" ] && modifyrepo_c --mdtype=appstream-icons "$asout/$id-icons.tar.gz" "$out/repodata" - else - log "WARNUNG: appstream-builder fuer $id fehlgeschlagen (nicht fatal), siehe $CACHE/appstream-$id.log" - fi -} - -# AlmaLinux 10: appstream-glib gibt es (Stand jetzt) weder dort noch in -# EPEL 10, stattdessen `appstream`/appstreamcli. Dessen "compose"-Kommando -# arbeitet nicht auf .rpm-Dateien, sondern auf einem Verzeichnisbaum wie -# einem Dateisystem-Root (/usr/share/applications, /usr/share/metainfo, ...). -# Wir entpacken deshalb erst die dafuer relevanten Pfade aus allen Paketen in -# so einen Baum und rufen dann `appstreamcli compose` darauf auf. -generate_appstream_via_compose() { - local id="$1" out="$2" - local composeroot="$CACHE/appstream-root/$id" - local asout="$CACHE/appstream/$id" - local pkgmap="$CACHE/appstream-pkgmap-$id.txt" - local rpm - - rm -rf "$composeroot" - mkdir -p "$composeroot/usr/share/applications" "$composeroot/usr/share/metainfo" - : > "$pkgmap" - - for rpm in "$out"/*.rpm; do - [ -e "$rpm" ] || continue - local pkgname - pkgname=$(rpm -qp --qf '%{NAME}' "$rpm" 2>/dev/null) - - # Icons/Pixmaps liegen so gut wie immer am Standard-FHS-Pfad - dafuer - # reicht ein blinder Glob-Extract, eine Paketzuordnung brauchen wir - # dafuer nicht. - (cd "$composeroot" && rpm2cpio "$rpm" | cpio -idm --quiet \ - './usr/share/icons/*' \ - './usr/share/pixmaps/*') >>"$CACHE/appstream-$id.log" 2>&1 - - # .desktop-/AppStream-Dateien koennen dagegen auch ausserhalb der - # Standardpfade liegen (u.a. alle jpackage-basierten Java/JavaFX-Apps - # wie z.B. XPipe legen sie direkt im eigenen Installationsverzeichnis - # ab, z.B. /opt//.desktop, und verschieben sie erst per - # %post-Skript - das sehen wir beim reinen Lesen des Pakets nicht). - # Deshalb IMMER das ganze Paket nur AUFLISTEN (kein teurer Voll- - # Extract, bei z.T. >100 MB grossen JRE-Bundles) und alle - # .desktop/.metainfo.xml/.appdata.xml-Dateien gezielt herausgreifen - - # dabei merken wir uns, aus welchem RPM-Paket jede .desktop-Datei - # stammt, fuer in der (Fallback-)Metainfo weiter unten. - local matches - mapfile -t matches < <(rpm2cpio "$rpm" 2>/dev/null | cpio -t --quiet 2>/dev/null \ - | grep -E '\.desktop$|\.metainfo\.xml$|\.appdata\.xml$') - - if [ "${#matches[@]}" -gt 0 ]; then - local extractroot f base destdir - extractroot=$(mktemp -d) - (cd "$extractroot" && rpm2cpio "$rpm" | cpio -idm --quiet "${matches[@]}") >>"$CACHE/appstream-$id.log" 2>&1 - for f in "${matches[@]}"; do - [ -f "$extractroot/$f" ] || continue - base=$(basename "$f") - case "$base" in - *.desktop) - destdir="$composeroot/usr/share/applications" - printf '%s\t%s\n' "$base" "$pkgname" >> "$pkgmap" - ;; - *) - destdir="$composeroot/usr/share/metainfo" - ;; - esac - # Nicht ueberschreiben, falls bereits ein anderes Paket schon - # eine gleichnamige Datei mitgebracht hat. - if [ ! -e "$destdir/$base" ]; then - cp "$extractroot/$f" "$destdir/$base" - # Vom Hersteller mitgelieferte .metainfo.xml/.appdata.xml - # haben so gut wie nie ein (das kennt nur die - # Distro/das Repo, nicht der Upstream-Entwickler) - ohne - # das bleibt die Komponente in Discover trotz gueltiger - # Metainfo nicht installierbar. Deshalb hier direkt - # ergaenzen, falls es fehlt. - if [ "$base" != "${base%.desktop}" ]; then - : # ist eine .desktop-Datei, braucht selbst kein - elif ! grep -q '' "$destdir/$base" 2>/dev/null; then - sed -i "s## $(xml_escape "$pkgname")\n#" "$destdir/$base" - fi - fi - done - rm -rf "$extractroot" - fi - done - - if [ -z "$(find "$composeroot" -type f 2>/dev/null)" ]; then - log "Keine Desktop-/AppStream-Dateien in den Paketen gefunden, ueberspringe Katalog-Generierung." - return - fi - - generate_fallback_metainfo "$composeroot" "$pkgmap" - - rm -rf "$asout" - mkdir -p "$asout" - # Exit-Code NICHT sofort als fatal behandeln: appstreamcli compose liefert - # bei einzelnen fehlerhaften Komponenten ("Run failed, some data was - # ignored") einen Fehler-Exitcode, schreibt aber trotzdem einen Katalog - # mit allen UEBRIGEN, gueltigen Komponenten. Nur wenn am Ende wirklich - # gar kein Katalog entstanden ist, brechen wir ab. - if ! appstreamcli compose --prefix=/usr --origin="$id" --result-root="$asout" "$composeroot" \ - >>"$CACHE/appstream-$id.log" 2>&1; then - log "WARNUNG: appstreamcli compose meldet Fehler bei einzelnen Komponenten, siehe $CACHE/appstream-$id.log - verwende ggf. trotzdem erzeugten Teil-Katalog." - fi - - local catalog - catalog=$(find "$asout" -type f \( -iname '*.xml.gz' -o -iname '*.yml.gz' \) | head -n1) - if [ -z "$catalog" ]; then - log "WARNUNG: appstreamcli compose hat keinen Katalog erzeugt, siehe $CACHE/appstream-$id.log" - return - fi - modifyrepo_c --mdtype=appstream "$catalog" "$out/repodata" - - local icons_dir - icons_dir=$(find "$asout" -type d -iname 'icons' | head -n1) - if [ -n "$icons_dir" ] && [ -n "$(find "$icons_dir" -type f 2>/dev/null)" ]; then - tar -C "$icons_dir" -czf "$asout/$id-icons.tar.gz" . - modifyrepo_c --mdtype=appstream-icons "$asout/$id-icons.tar.gz" "$out/repodata" - fi -} - -# Holt + filtert + signiert die Pakete EINES Fremdrepos in einen privaten, -# nur intern genutzten Cache ($SIGNED_ROOT/$id) - noch NICHT im oeffentlichen -# Ausgabeverzeichnis. Der eigentliche Merge zu einem gemeinsamen Repo passiert -# erst danach, einmalig fuer alle Fremdrepos zusammen (siehe unten). -# Args: id baseurl gpgkey gpgcheck(0/1) paket1 [paket2 ...] -sync_one_repo() { - local id="$1" baseurl="$2" gpgkey="$3" gpgcheck="$4" - shift 4 - local -a packages=("$@") - local stage="$CACHE/stage/$id" - local signed="$SIGNED_ROOT/$id" - local tmp_repofile="$CACHE/tmp-repos/$id.repo" - - log "== $id: hole ${#packages[@]} Paket(e) aus $baseurl ==" - rm -f "$CACHE/tmp-repos"/*.repo - { - echo "[$id]" - echo "name=$id" - echo "baseurl=$baseurl" - echo "enabled=1" - echo "gpgcheck=$gpgcheck" - [ -n "$gpgkey" ] && echo "gpgkey=$gpgkey" - } > "$tmp_repofile" - - rm -rf "$stage" - mkdir -p "$stage" - if ! dnf --setopt="reposdir=$CACHE/tmp-repos" \ - --disablerepo='*' --enablerepo="$id" \ - --forcearch="$ALMA_BASEARCH" \ - download --destdir="$stage" "${packages[@]}" \ - >>"$CACHE/download-$id.log" 2>&1; then - log "FEHLER: dnf download fuer $id fehlgeschlagen, siehe $CACHE/download-$id.log - ueberspringe (bisheriger Stand von $id bleibt im gemeinsamen Repo erhalten)." - return 1 - fi - - log "== $id: filtere Pakete, die Alma bereits bereitstellt ==" - local removed=0 kept=0 - while IFS= read -r -d '' rpm; do - local name - name=$(rpm -qp --qf '%{NAME}' "$rpm" 2>/dev/null) - if [ -z "$name" ]; then - continue - fi - if grep -qxF "$name" "$CACHE/alma-names.txt" 2>/dev/null && ! is_force_included "$name"; then - rm -f "$rpm" - removed=$((removed + 1)) - else - kept=$((kept + 1)) - fi - done < <(find "$stage" -maxdepth 1 -name '*.rpm' -print0) - log "$id: $kept Pakete werden gespiegelt, $removed uebersprungen (bereits ueber Alma verfuegbar)." - - log "== $id: aktualisiere internen Paketstand und signiere neue/geaenderte Pakete ==" - mkdir -p "$signed" - local changed_list rsync_out - changed_list=$(mktemp) - rsync_out=$(mktemp) - if ! rsync -ai --delete --include='*.rpm' --exclude='*' "$stage"/ "$signed"/ > "$rsync_out" 2>&1; then - log "FEHLER: rsync (interner Paketstand) fuer $id fehlgeschlagen: $(cat "$rsync_out") - ueberspringe $id fuer diesen Lauf." - rm -f "$changed_list" "$rsync_out" - return 1 - fi - awk '/^[><]f/{print $2}' "$rsync_out" > "$changed_list" - rm -f "$rsync_out" - - local resign_failed=0 - while IFS= read -r f; do - [ -z "$f" ] && continue - if ! rpmsign --resign \ - --define "_gpg_name $GPG_NAME" \ - --define "_gpg_path $GNUPGHOME" \ - "$signed/$f" >>"$CACHE/rpmsign-$id.log" 2>&1; then - log "FEHLER: Signieren von $signed/$f fehlgeschlagen, siehe $CACHE/rpmsign-$id.log" - resign_failed=1 - fi - done < "$changed_list" - local changed_count - changed_count=$(wc -l < "$changed_list") - rm -f "$changed_list" - log "$id: $changed_count Pakete neu signiert." - if [ "$resign_failed" -ne 0 ]; then - log "FEHLER: mind. ein Paket in $id konnte nicht signiert werden - $id wird bei der Zusammenfuehrung uebersprungen." - return 1 - fi - - log "== $id: fertig ==" -} - -if ! fetch_packages_yaml; then - log "FEHLER: keine packages.yaml verfuegbar, breche Sync-Lauf komplett ab." - exit 1 -fi - -mapfile -t FORCE_INCLUDE_NAMES < <(jq -r '.force_include[]? // empty' "$PACKAGES_JSON") - -refresh_alma_names -if [ ! -s "$CACHE/alma-names.txt" ]; then - log "FEHLER: keine Alma-Baseline-Namen verfuegbar, breche Sync-Lauf ab (Sicherheitsnetz gegen versehentliches Doppel-Spiegeln)." - exit 1 -fi - -overall_status=0 -all_ids=() -repo_count=$(jq '.repos | length' "$PACKAGES_JSON") -for ((i = 0; i < repo_count; i++)); do - repo_json=$(jq -c ".repos[$i]" "$PACKAGES_JSON") - id=$(jq -r '.id // empty' <<<"$repo_json") - baseurl=$(jq -r '.baseurl // empty' <<<"$repo_json") - gpgkey=$(jq -r '.gpgkey // empty' <<<"$repo_json") - gpgcheck=$(jq -r 'if .gpgcheck == false then "0" else "1" end' <<<"$repo_json") - mapfile -t packages < <(jq -r '.packages[]? // empty' <<<"$repo_json") - - if [ -z "$id" ] || [ -z "$baseurl" ] || [ "${#packages[@]}" -eq 0 ]; then - log "WARNUNG: Eintrag Nr. $((i + 1)) in packages.yaml unvollstaendig (id/baseurl/packages fehlt), ueberspringe." - overall_status=1 - continue - fi - - all_ids+=("$id") - sync_one_repo "$id" "$baseurl" "$gpgkey" "$gpgcheck" "${packages[@]}" || overall_status=1 -done - -# Internen Paketstand von Fremdrepos aufraeumen, die nicht mehr in -# packages.yaml stehen - sonst wuerden deren alte Pakete beim Merge unten -# unnoetig im Cache liegen bleiben (sie werden ja schon jetzt nicht mehr in -# die Merge-Quellenliste aufgenommen, siehe unten - das hier ist nur Hygiene). -if [ -d "$SIGNED_ROOT" ]; then - for d in "$SIGNED_ROOT"/*/; do - [ -d "$d" ] || continue - existing_id=$(basename "$d") - keep=0 - for id in "${all_ids[@]}"; do - [ "$id" = "$existing_id" ] && keep=1 && break - done - if [ "$keep" -eq 0 ]; then - log "Entferne internen Paketstand des nicht mehr in packages.yaml gelisteten Fremdrepos '$existing_id' ..." - rm -rf "$d" - fi - done -fi - -log "== Fuehre alle Fremdrepos zu EINEM gemeinsamen Ausgabe-Repo zusammen ==" -merge_sources=() -for id in "${all_ids[@]}"; do - [ -d "$SIGNED_ROOT/$id" ] && merge_sources+=("$SIGNED_ROOT/$id/") -done - -if [ "${#merge_sources[@]}" -eq 0 ]; then - log "FEHLER: kein Fremdrepo hat je erfolgreich Pakete geliefert - gemeinsames Repo bleibt unveraendert/leer." - exit 1 -fi - -if ! rsync -ai --delete --include='*.rpm' --exclude='*' "${merge_sources[@]}" "$OUT"/ >>"$CACHE/merge.log" 2>&1; then - log "FEHLER: Zusammenfuehren der Fremdrepos zum gemeinsamen Repo fehlgeschlagen, siehe $CACHE/merge.log" - exit 1 -fi - -log "== baue Repo-Metadaten fuer das gemeinsame Repo ==" -if ! createrepo_c --update "$OUT" >>"$CACHE/createrepo.log" 2>&1; then - log "FEHLER: createrepo_c fehlgeschlagen, siehe $CACHE/createrepo.log" - exit 1 -fi - -log "== baue AppStream-Metadaten (fuer Discover/GNOME Software) ==" -if command -v appstream-builder >/dev/null 2>&1; then - generate_appstream_via_builder "$MIRROR_REPO_ID" "$OUT" -elif command -v appstreamcli >/dev/null 2>&1; then - generate_appstream_via_compose "$MIRROR_REPO_ID" "$OUT" -else - log "WARNUNG: weder appstream-builder noch appstreamcli installiert - Pakete erscheinen ohne App-Katalogeintrag in Discover." -fi - -log "== signiere repomd.xml ==" -gpg --batch --yes --detach-sign --armor --local-user "$GPG_NAME" \ - --output "$OUT/repodata/repomd.xml.asc" "$OUT/repodata/repomd.xml" - -cat > "$OUT/$MIRROR_REPO_ID.repo" <