fix: Ghostscript-Angebot log nicht mehr, fehlende Voraussetzungen dokumentiert (v0.7.1)

Befunde aus dem ersten echten Erstinstallations-Test auf frischen Debian-12-
und Debian-13-Containern. Der Weg selbst hat getragen (Basis-Install,
Instanz-Anlage, zweite Instanz, Update mit Rauchtest, Rollback) — diese
Stellen haben gelogen oder gefehlt:

- Das Ghostscript-Backports-Angebot auf Debian 12 war ein garantierter
  Leerlauf, der "aktualisiert ✓" meldete: bookworm-backports enthaelt gar
  kein ghostscript (am Paketindex verifiziert). Die Routine sucht jetzt den
  echten Kandidaten, vergleicht vorher/nachher und raeumt eine nur zur Probe
  angelegte Quelle wieder weg.
- Derselbe untaugliche Rat stand in der Preflight-Meldung, der
  pdfa_level-Warnung, config.example.toml und vier Doku-Dateien — ueberall
  ersetzt durch die echten Optionen.
- Mehrzeilige Log-Hinweise waren durch "echo -e" zerrissen und nicht
  kopierbar; log_* nutzt jetzt printf mit %s.
- pip-freeze.txt landete beim Rollback als /pip-freeze.txt im
  Wurzelverzeichnis, liegt jetzt unter opt/pdf-ocr-hotfolder/.
- git und sudo fehlen auf dem Proxmox-Debian-Template; "sudo ./install.sh"
  scheitert dort. Beide Wege dokumentiert, git als Voraussetzung ergaenzt,
  HTTPS-Clone als Normalfall.
- Rollback: systemctl start kann kein Glob. journald-Reparatur: Instanzen
  danach neu starten, sonst bleibt das Journal leer.

254 Tests gruen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 01:36:42 +02:00
parent cd803a3dfe
commit 465ff8873f
15 changed files with 611 additions and 101 deletions
+210 -4
View File
@@ -27,10 +27,17 @@ PDF_OCR_COMMON_LOADED=1
# ============================================================
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
log_step() { echo -e "\n${BLUE}==>${NC} $*"; }
# Die Meldung selbst geht als %s durch, NICHT als %b (frueher: echo -e). Sonst
# wird ein '\n' im Text — etwa in einem Befehl, den wir zum Kopieren anzeigen —
# zu einem echten Zeilenumbruch: die Folgezeilen haetten dann kein [WARN] davor
# und wer den Block kopiert, schleppt das Praefix mit. Nur die Farbcodes
# brauchen %b. Eine Meldung pro Zeile — mehrzeilige Hinweise bitte als mehrere
# Aufrufe schreiben.
log_info() { printf '%b[INFO]%b %s\n' "$GREEN" "$NC" "$*"; }
log_warn() { printf '%b[WARN]%b %s\n' "$YELLOW" "$NC" "$*"; }
log_error() { printf '%b[ERROR]%b %s\n' "$RED" "$NC" "$*"; }
log_step() { printf '\n%b==>%b %s\n' "$BLUE" "$NC" "$*"; }
# Bricht ab, wenn nicht root. $1 = der Aufruf, der gemeint ist.
require_root() {
@@ -50,6 +57,12 @@ require_root() {
: "${SYSTEMD_DIR:=/etc/systemd/system}"
: "${DEFAULT_USER:=pdfocr}"
# Systempfade, die nur die Ghostscript-Pruefung braucht. Ueberschreibbar,
# damit sich die Pruefung gegen eine Fake-Umgebung testen laesst.
: "${OS_RELEASE_FILE:=/etc/os-release}"
: "${APT_SOURCES_LIST:=/etc/apt/sources.list}"
: "${APT_SOURCES_DIR:=/etc/apt/sources.list.d}"
SERVICE_TEMPLATE="pdf-ocr-hotfolder@.service"
LXC_DROPIN_DIR="$SYSTEMD_DIR/${SERVICE_TEMPLATE}.d"
# shellcheck disable=SC2034 # wird von install.sh und update.sh genutzt
@@ -88,6 +101,199 @@ PKGLIST
}
# --- END apt-packages ---
# ============================================================
# Ghostscript
# ============================================================
# Ghostscript 10.0.0 bis 10.02.0 erzeugt fehlerhafte PDF/A-Ausgaben: zusammen
# mit [ocr].pdfa_level UND skip_text = true scheitert ocrmypdf an jeder Datei.
# Mit leerem pdfa_level (der Default) ist so ein Ghostscript voellig
# unproblematisch — die Pruefung ist deshalb ein Hinweis, kein Abbruchgrund.
# Betroffen? $1 = Ausgabe von 'gs --version'.
gs_version_affected() {
case "$1" in
10.0.0|10.00.0|10.01.*|10.02.0) return 0 ;;
*) return 1 ;;
esac
}
# Version, die gs meldet; leer, wenn gs nicht aufrufbar ist.
gs_version() {
command -v gs >/dev/null 2>&1 || return 0
gs --version 2>/dev/null || true
}
# Codename der Distribution (VERSION_CODENAME aus os-release); leer, wenn
# unbekannt.
os_codename() {
[ -r "$OS_RELEASE_FILE" ] || return 0
sed -n 's/^VERSION_CODENAME=//p' "$OS_RELEASE_FILE" | tr -d '"' | head -n1
}
# Ist $1 (z.B. bookworm-backports) schon irgendwo als apt-Quelle eingetragen?
apt_release_configured() {
grep -RqsF -- "$1" "$APT_SOURCES_LIST" "$APT_SOURCES_DIR" 2>/dev/null
}
# Installierte Paketversion laut dpkg; leer, wenn das Paket nicht da ist.
apt_installed_version() {
command -v dpkg-query >/dev/null 2>&1 || return 0
dpkg-query -W -f '${Version}' "$1" 2>/dev/null || true
}
# Hoechste Version von Paket $1, die aus Release $2 (z.B. bookworm-backports)
# zu haben ist — gelesen aus der Versionstabelle von 'apt-cache policy'.
# Leer, wenn das Release das Paket nicht fuehrt. Bewusst nicht hart verdrahtet:
# liefert Debian spaeter doch ein Backports-Ghostscript, greift der Pfad.
apt_version_in_release() {
local pkg="$1" release="$2"
command -v apt-cache >/dev/null 2>&1 || return 0
LC_ALL=C apt-cache policy "$pkg" 2>/dev/null | awk -v rel="$release" '
$1 == "***" { ver = $2; next }
NF == 2 && $1 ~ /^[0-9]/ { ver = $1; next }
ver != "" && index($0, rel) { print ver; exit }
'
}
# Entfernt eine Quelle, die NUR fuer die Pruefung angelegt wurde ($2 = 1).
# War sie vorher schon da, bleibt sie unangetastet — sie kann von etwas
# anderem stammen.
gs_drop_probe_source() {
local list_file="$1" added="$2"
[ "$added" = "1" ] || return 0
rm -f "$list_file" 2>/dev/null || true
log_info "Quelle wieder entfernt: $list_file (sie hat nichts gebracht)"
apt-get update -qq || log_warn "'apt-get update' nach dem Aufraeumen schlug fehl."
}
# Versucht, Ghostscript aus Release $1 zu aktualisieren. $2 = bisherige
# gs-Version. Rueckgabe 0 NUR, wenn hinterher nachweislich eine andere,
# nicht mehr betroffene Version laeuft.
gs_try_release_upgrade() {
local release="$1" old_ver="$2"
local list_file="$APT_SOURCES_DIR/${release}.list"
local added=0 cand installed new_ver new_installed
# Ohne apt-cache/dpkg laesst sich nicht feststellen, was dort ueberhaupt
# liegt — dann wird auch nichts eingetragen.
if ! command -v apt-cache >/dev/null 2>&1 || ! command -v dpkg >/dev/null 2>&1; then
log_warn "apt-cache oder dpkg fehlt — der Paketstand von $release ist nicht pruefbar."
return 1
fi
if apt_release_configured "$release"; then
log_info "$release ist bereits eingetragen — die Quelle bleibt, wie sie ist."
else
log_info "Trage $release voruebergehend ein, um den Paketstand zu pruefen..."
mkdir -p "$APT_SOURCES_DIR" 2>/dev/null || true
if ! printf 'deb http://deb.debian.org/debian %s main\n' "$release" > "$list_file" 2>/dev/null; then
log_warn "$list_file liess sich nicht schreiben — Pruefung nicht moeglich."
return 1
fi
added=1
if ! apt-get update -qq; then
log_warn "'apt-get update' schlug fehl — $release nicht auswertbar."
gs_drop_probe_source "$list_file" "$added"
return 1
fi
fi
installed="$(apt_installed_version ghostscript)"
cand="$(apt_version_in_release ghostscript "$release")"
if [ -z "$cand" ]; then
log_warn "$release fuehrt gar kein Paket 'ghostscript' — von dort kommt nichts."
gs_drop_probe_source "$list_file" "$added"
return 1
fi
if [ -n "$installed" ] && ! dpkg --compare-versions "$cand" gt "$installed"; then
log_warn "$release hat ghostscript $cand — nicht neuer als das installierte $installed."
gs_drop_probe_source "$list_file" "$added"
return 1
fi
log_info "$release bietet ghostscript $cand (installiert: ${installed:-unbekannt}) — installiere..."
if ! apt-get install -y -t "$release" ghostscript; then
log_warn "Installation aus $release schlug fehl — Ghostscript bleibt, wie es war."
gs_drop_probe_source "$list_file" "$added"
return 1
fi
# Erfolg wird NUR gemeldet, wenn sich die Version wirklich geaendert hat.
new_ver="$(gs_version)"
new_installed="$(apt_installed_version ghostscript)"
if [ "$new_ver" = "$old_ver" ] && [ "$new_installed" = "$installed" ]; then
log_warn "Ghostscript unveraendert: $old_ver (Paket ${installed:-unbekannt})."
log_warn "Das Upgrade aus $release hat nichts bewirkt."
gs_drop_probe_source "$list_file" "$added"
return 1
fi
log_info "Ghostscript: $old_ver -> $new_ver (Paket ${installed:-unbekannt} -> ${new_installed:-unbekannt})"
if gs_version_affected "$new_ver"; then
log_warn "Auch $new_ver liegt noch im betroffenen Bereich (10.0.0-10.02.0)."
return 1
fi
log_info "Ghostscript ist jetzt frei vom PDF/A-Fehler ✓"
return 0
}
# Sagt dem Admin, was wirklich zur Auswahl steht. Kein Verweis auf Wege, die
# es nicht gibt.
gs_report_options() {
log_warn "Es bleibt bei der betroffenen Ghostscript-Version. Echte Optionen:"
log_warn " 1. Nichts tun: [ocr].pdfa_level leer lassen (Default). Dann ist der"
log_warn " Fehler folgenlos — die Ausgabe ist nur kein PDF/A."
log_warn " 2. PDF/A wirklich noetig? Dann [ocr].skip_text = false setzen."
log_warn " 3. Neuere Distribution: Debian 13 (trixie) liefert Ghostscript 10.05.1."
log_warn "Die Installation laeuft normal weiter."
}
# Vollstaendige Pruefung fuer den Installer. Gibt immer 0 zurueck: ein
# betroffenes Ghostscript ist ein Hinweis, kein Abbruch.
check_ghostscript() {
local ver codename release answer
ver="$(gs_version)"
if [ -z "$ver" ]; then
log_warn "Ghostscript ist nicht aufrufbar — Versionspruefung uebersprungen."
return 0
fi
log_info "Ghostscript: $ver"
gs_version_affected "$ver" || return 0
echo
log_warn "═══════════════════════════════════════════════════════════════"
log_warn "Ghostscript $ver ist vom PDF/A-Fehler betroffen (10.0.0-10.02.0)."
log_warn "Betroffen sind nur Instanzen mit [ocr].pdfa_level UND skip_text = true;"
log_warn "mit leerem pdfa_level (Default) ist die Version unauffaellig."
log_warn "═══════════════════════════════════════════════════════════════"
echo
codename="$(os_codename)"
if [ "$codename" = "bookworm" ]; then
release="bookworm-backports"
# Bewusst als Frage nach dem PRUEFEN formuliert: ob dort ueberhaupt ein
# neueres Ghostscript liegt, steht erst nach 'apt-cache policy' fest.
read -r -p "In $release nach einem neueren Ghostscript suchen? [J/n]: " answer || answer=""
answer="${answer:-J}"
if [[ "$answer" =~ ^[JjYy]$ ]]; then
if gs_try_release_upgrade "$release" "$ver"; then
echo
return 0
fi
else
log_info "Uebersprungen."
fi
else
log_warn "Kein Debian 12 (bookworm) erkannt${codename:+ (Codename: $codename)} — es gibt hier keinen Backports-Weg."
fi
gs_report_options
echo
return 0
}
# ============================================================
# Python / venv
# ============================================================