# Debian-Major-Upgrade Wie der Hotfolder ein Distributions-Upgrade übersteht (12 → 13, später 13 → 14). Verwandte Dokumente: [README](../README.md) · [Installation](INSTALLATION.md) · [Update](UPDATE.md) --- ## Warum das ein eigener Ablauf ist Die venv unter `/opt/pdf-ocr-hotfolder/venv/` hängt an der **Python-Version der Distribution**. Ein `apt full-upgrade` von Debian 12 auf 13 tauscht Python 3.11 gegen 3.13 aus. Danach zeigt `venv/bin/python` auf einen Interpreter, den es so nicht mehr gibt — systemd quittiert den Start jeder Instanz mit `203/EXEC`, und auch wenn der Interpreter noch existiert, passen die installierten Pakete nicht mehr zum System-Python. **Die venv muss nach dem Sprung neu gebaut werden.** Ein normales `sudo ./update.sh` genügt dafür nicht sicher genug — es gibt den ausdrücklichen Schalter `--rebuild-venv`. --- ## Der Ablauf ### 1. Vorher updaten ```bash cd /pfad/zum/repo git pull sudo ./update.sh ``` Das bringt die Installation auf den aktuellen Stand und erzeugt vor allem ein **frisches Backup** inklusive `pip-freeze.txt` — die Liste der Paketversionen, die auf dem alten System liefen. Inhalt und Ort des Backups: [UPDATE.md](UPDATE.md#backup). ### 2. Instanzen stoppen ```bash sudo systemctl stop 'pdf-ocr-hotfolder@*' ``` Während des Upgrades darf kein OCR laufen: Ghostscript, Tesseract und die Python-Pakete werden mitten im Betrieb ausgetauscht. > **Geduld.** Die Unit hat `TimeoutStopSec=300`, damit ein laufendes OCR sauber > zu Ende kommt. **Ein Stop kann pro Instanz bis zu 5 Minuten dauern** — bei > mehreren Instanzen entsprechend länger. Nicht mit `kill -9` nachhelfen; ein > harter Stopp lässt das Original in `working/` liegen (der Dienst nimmt es beim > nächsten Start zwar wieder auf, aber der Durchlauf ist verloren). Prüfen, dass wirklich alles steht: ```bash systemctl status 'pdf-ocr-hotfolder@*' ``` ### 3. Distribution upgraden Der übliche Debian-Weg — Sources auf das neue Release umstellen, dann: ```bash sudo apt update sudo apt full-upgrade sudo reboot ``` Die Instanzen sind `enabled` und starten nach dem Reboot mit; mit der alten venv scheitern sie (`203/EXEC`). Das ist erwartet und wird im nächsten Schritt behoben. ### 4. venv neu bauen ```bash cd /pfad/zum/repo git pull sudo ./update.sh --rebuild-venv ``` `--rebuild-venv` erzwingt den Neubau. Der Rest des Updates läuft wie gewohnt (siehe [UPDATE.md](UPDATE.md#was-das-skript-tut--in-dieser-reihenfolge)) — die System-Pakete werden dabei ebenfalls abgeglichen, auch `python3-venv` für das neue Python. Der Neubau ist **ganz oder gar nicht**: 1. Die alte venv wird nach `venv.old-` verschoben. 2. `python3 -m venv` baut neu. 3. Die Requirements werden installiert. 4. **Erst bei Erfolg** wird die alte venv gelöscht. ### 5. Wenn ein Pin nicht mehr passt Scheitert `pip install` an einer gepinnten Version, bricht das Skript ab und sagt genau, was los ist: - die letzten 25 Zeilen der pip-Ausgabe, - das **gescheiterte Paket** ("Gescheitertes Paket: …"), - die Diagnose: "eine in requirements.txt fest gepinnte Version gibt es für Python \ nicht (mehr)", - den nächsten Schritt: **requirements.txt anheben** und `update.sh --rebuild-venv` erneut laufen lassen. **Die alte venv ist dann zurückgerollt** — es liegt keine halb gefüllte venv herum. Sie hängt zwar weiterhin am alten Interpreter und die Instanzen laufen damit nicht (das sagt das Skript auch), aber der Zustand ist eindeutig. Also: ```bash # im Repo, auf einer Testmaschine vim requirements.txt # Version des genannten Pakets anheben pytest # Suite muss grün bleiben git commit -am 'requirements: auf anheben' git push # auf dem Zielsystem git pull sudo ./update.sh --rebuild-venv ``` ### 6. `--rebuild-venv` vergessen? Halb so wild: `update.sh` prüft die venv auch ohne den Schalter und baut sie bei Versions-Drift von selbst neu. Geprüft wird - ob das Verzeichnis und `venv/bin/python` überhaupt existieren, - ob der Interpreter der venv noch **läuft** (toter Symlink nach dem Upgrade), - ob seine `major.minor` zum System-`python3` passt, - ob `pyvenv.cfg` dieselbe Version nennt wie der Interpreter. Stimmt eines davon nicht, nennt das Skript den Grund und baut neu. `--rebuild-venv` ist also nicht der einzige, aber der **ausdrückliche** Weg — und der, den man nach einem Distributions-Upgrade nimmt, statt sich auf die Erkennung zu verlassen. --- ## Danach prüfen **Laufen alle Instanzen?** ```bash systemctl status 'pdf-ocr-hotfolder@*' ``` `update.sh` hat das schon verifiziert (Wartezeit, `is-failed`, Crash-Loop) und in der Zusammenfassung Soll gegen Ist gestellt — ein Exit 0 heißt, dass jede Instanz, die vorher lief, auch wieder läuft. **Sind die Configs sauber?** ```bash cd /opt/pdf-ocr-hotfolder for f in /etc/pdf-ocr-hotfolder/*.toml; do sudo ./venv/bin/python -m pdf_ocr_hotfolder --check-config --config "$f" done ``` Exit 0/1/2 und was bei Warnungen zu tun ist: [UPDATE.md](UPDATE.md#config-prüfung-per---check-config). **Läuft eine echte PDF durch?** ```bash cp test.pdf /var/lib/pdf-ocr-hotfolder//incoming/ journalctl -u pdf-ocr-hotfolder@ -f ``` Im `outgoing/` muss das OCR-PDF liegen, im Journal steht `OCR done`. Das ist der einzige Test, der die neue venv **und** die neuen System-Binaries (Tesseract, Ghostscript) wirklich anfasst — `systemctl status` sagt darüber nichts. **Ghostscript-Version auf dem neuen Release ansehen:** ```bash gs --version ``` Auf Debian 13 ist der Ghostscript-Bug aus Debian 12 kein Thema mehr; ein bookworm-backports-Eintrag unter `/etc/apt/sources.list.d/` gehört nach dem Upgrade entfernt. Hintergrund: [INSTALLATION.md](INSTALLATION.md#ghostscript-bug-auf-debian-12). --- ## Pins in `requirements.txt` Die Python-Abhängigkeiten sind **bewusst fest gepinnt**: ``` ocrmypdf==17.4.1 watchdog==6.0.0 requests==2.33.1 paramiko==4.0.0 ``` Ohne Pins würde ein `pip install --upgrade` bei jedem Update ungefragt eine neue Major-Version ziehen — der nächste Sprung wäre ocrmypdf **17 auf 18**, und der reißt sonst alle Instanzen auf einmal, und zwar im Moment des Updates, nicht zu einem Zeitpunkt, den man sich ausgesucht hat. > ⚠️ **ocrmypdf darf nicht unter 17 fallen.** Bis einschließlich 16.x prüft > ocrmypdf die Ghostscript-Version auch dann, wenn gar kein PDF/A erzeugt wird — > auf Debian 12 (Ghostscript 10.0.0) scheitert damit **jede** PDF, weil > `skip_text = true` unser Default ist. Genau das war der Ausfall in 0.6.0. > Hintergrund: [INSTALLATION.md](INSTALLATION.md#ghostscript-bug-auf-debian-12). Die aktuellen Pins sind gegen Python 3.11 (Debian 12) und 3.13 (Debian 13) geprüft; für beide gibt es fertige Wheels, es wird nichts kompiliert. Das gilt auch für die Abhängigkeiten, die ocrmypdf 17 zusätzlich mitbringt (`pydantic`, `pypdfium2`, `fpdf2`, `uharfbuzz`). **Beim Anheben:** 1. **Testmaschine benutzen** — nie direkt auf dem produktiven Hotfolder. 2. Prüfen, dass es für die neue Version auf **beiden** Python-Versionen fertige Wheels gibt, sonst wird auf dem Zielsystem kompiliert: ```bash pip install --dry-run --only-binary=:all: --python-version 3.11 \ --target /tmp/wheelcheck ocrmypdf== pip install --dry-run --only-binary=:all: --python-version 3.13 \ --target /tmp/wheelcheck ocrmypdf== ``` 3. Dort `update.sh --rebuild-venv` fahren, damit die Pakete wirklich frisch aufgelöst werden. 4. `pytest` muss grün bleiben (254 Tests). 5. Eine echte PDF durchschieben — die Test-Suite mockt ocrmypdf, ein Major-Sprung fällt dort also nicht auf. Der [Rauchtest](UPDATE.md#rauchtest) in `update.sh` macht genau das automatisch. 6. Erst dann committen und auf die produktiven Systeme geben. Der ocrmypdf-Sprung 16 → 17 ist ein **Major-Sprung** und gehört in einen eigenen Vorgang mit eigenem Test, nicht in ein OS-Upgrade.