Dateien nach "mirror" hochladen
This commit is contained in:
+526
@@ -0,0 +1,526 @@
|
||||
#!/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//\"/"}"
|
||||
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 '<?xml version="1.0" encoding="UTF-8"?>'
|
||||
echo '<component type="desktop-application">'
|
||||
echo " <id>${base}</id>"
|
||||
echo " <pkgname>$(xml_escape "$pkgname")</pkgname>"
|
||||
echo ' <metadata_license>CC0-1.0</metadata_license>'
|
||||
echo ' <project_license>LicenseRef-proprietary</project_license>'
|
||||
echo " <name>$(xml_escape "$name")</name>"
|
||||
echo " <summary>$(xml_escape "$summary")</summary>"
|
||||
echo " <description>"
|
||||
echo " <p>$(xml_escape "$summary")</p>"
|
||||
echo " </description>"
|
||||
echo " <launchable type=\"desktop-id\">${base}</launchable>"
|
||||
if [ -n "$categories" ]; then
|
||||
echo ' <categories>'
|
||||
local IFS=';'
|
||||
for cat in $categories; do
|
||||
[ -z "$cat" ] && continue
|
||||
echo " <category>$(xml_escape "$cat")</category>"
|
||||
done
|
||||
echo ' </categories>'
|
||||
fi
|
||||
echo '</component>'
|
||||
} > "$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/<app>/<app>.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 <pkgname> 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 <pkgname> (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 <pkgname>
|
||||
elif ! grep -q '<pkgname>' "$destdir/$base" 2>/dev/null; then
|
||||
sed -i "s#</component># <pkgname>$(xml_escape "$pkgname")</pkgname>\n</component>#" "$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" <<EOF
|
||||
[$MIRROR_REPO_ID]
|
||||
name=$MIRROR_REPO_ID (Mirror)
|
||||
baseurl=https://$MIRROR_DOMAIN/dnf/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
repo_gpgcheck=1
|
||||
gpgkey=https://$MIRROR_DOMAIN/dnf/RPM-GPG-KEY-mirror
|
||||
EOF
|
||||
|
||||
package_count=$(find "$OUT" -maxdepth 1 -name '*.rpm' | wc -l)
|
||||
log "== fertig: $package_count Paket(e) im gemeinsamen Repo =="
|
||||
|
||||
exit "$overall_status"
|
||||
Reference in New Issue
Block a user