feat: Wiederaufnahme aus working/, --check-config, feldtauglicher Updater (v0.6.0)
Datenverlust behoben: - Nach hartem Stopp blieb das Original in working/ liegen und wurde nie wieder angefasst (_scan_existing sah nur incoming/). Es wird jetzt beim Start an Ort und Stelle wieder aufgegriffen, mit Kollisionsschutz gegen gleichnamige neue Scans; angefangene __ocr_-Fragmente werden geloescht. - TimeoutStopSec 30 -> 300, damit laufendes OCR zu Ende laufen darf. Config-Drift sichtbar gemacht: - Neues --check-config (Exit 0 sauber / 1 Warnungen / 2 Fehler), das update.sh vor dem Neustart ueber alle Instanz-Configs laufen laesst. - Warnungen fuer [ocr].timeout >= 900 (seit 0.4.0 pro SEITE) und gesetztes pdfa_level, beim Dienststart wie im Check. - Unbekannte Config-Keys werden nicht mehr still verworfen, sondern genannt. Updater feldtauglich: - venv-Health-Check erkennt toten Symlink UND Versions-Drift gegen das System-Python; --rebuild-venv als ausdruecklicher Weg nach einem Debian- Major-Upgrade. Neubau ist ganz-oder-gar-nicht mit Rollback. - apt-Pakete werden auch beim Update synchronisiert (Quelle: install.sh). - Instanz-Erfassung inkl. activating/failed, Verifikation prueft is-failed und NRestarts statt sleep 1 + is-active. - Backup enthaelt Configs, Unit, Drop-ins und pip-freeze.txt, liegt auf 0600 und rotiert auf 5; schlaegt es fehl, bricht das Update vorher ab. - ERR-Trap faehrt die vorher laufenden Instanzen wieder hoch. - lxc-compat.conf wird beim Update nachgezogen. - requirements.txt gepinnt (ocrmypdf 16.13.0, geprueft fuer Python 3.11+3.13). Doku in Installation / Update / OS-Upgrade aufgeteilt (docs/). 135 Tests gruen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+115
@@ -1,5 +1,120 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.0] - 2026-09-22
|
||||
|
||||
### Added
|
||||
- **Wiederaufnahme aus `working/` beim Start.** `process_pdf()` verschiebt das
|
||||
Original vor dem OCR nach `working/`. Wurde der Dienst dort hart abgeschossen
|
||||
(SIGKILL nach `TimeoutStopSec`), blieb die Datei liegen und wurde **nie wieder
|
||||
angefasst** — stiller Datenverlust. `_scan_working()` greift sie jetzt beim
|
||||
Start auf (vor `incoming/`), das OCR laeuft fuer sie neu. Liegt in `incoming/`
|
||||
eine gleichnamige, andere Datei, bekommt die wiederaufgenommene einen
|
||||
Zeitstempel angehaengt, damit sich beide nicht ueberschreiben. Liegt in
|
||||
`working/` bereits eine andere Datei desselben Namens, bricht `process_pdf()`
|
||||
fuer die neue ab und laesst sie in `incoming/` liegen, statt den laufenden
|
||||
Vorgang stillschweigend zu ueberschreiben.
|
||||
- **Unvollstaendige OCR-Fragmente werden geloescht.** Die Zwischendatei, in die
|
||||
ocrmypdf schreibt, traegt jetzt das Praefix `__ocr_` (`OCR_TEMP_PREFIX`).
|
||||
Bleibt so eine Datei nach einem harten Stopp in `working/` liegen, ist sie als
|
||||
Eingabe unbrauchbar und als Ergebnis wertlos — sie wird beim Start mit einer
|
||||
Warnung entfernt, damit sie niemand fuer ein fertiges PDF haelt.
|
||||
- **`--check-config`**: prueft eine Instanz-Config, ohne irgendetwas zu
|
||||
verarbeiten (hat Vorrang vor `--once`). Zeigt die vier Pfade inkl. Hinweis auf
|
||||
noch fehlende Verzeichnisse, Sprachen, Seiten-Timeout und PDF/A-Level, faehrt
|
||||
Preflight und `[output]`-Validierung und gibt alle Warnungen aus.
|
||||
Exit **0** = sauber, **1** = nur Warnungen, **2** = Fehler (Dienst wuerde nicht
|
||||
starten).
|
||||
- **Legacy-Warnungen fuer `[ocr].timeout` und `[ocr].pdfa_level`.** Ein
|
||||
`timeout >= 900` stammt fast sicher aus einer Config vor 0.4.0, wo der Wert ein
|
||||
wirkungsloses Gesamt-Timeout mit Default 1800 war — seither sind es Sekunden
|
||||
**pro Seite** (Richtwert 300). Ein gesetztes `pdfa_level` weist auf den
|
||||
Ghostscript-Bug hin. Die Texte stehen nur in `config.py`
|
||||
(`legacy_warnings()`), weil sie sowohl beim Dienststart ins Log gehen als auch
|
||||
von `--check-config` ausgegeben werden.
|
||||
- **Unbekannte Config-Keys werden gemeldet** statt still verworfen.
|
||||
`_collect_unknown_keys()` sammelt Tippfehler (`[ocr].langauges`), Optionen aus
|
||||
aelteren Versionen, unbekannte Sektionen und unbekannte Upload-/Notify-Targets
|
||||
in `Config.unknown_keys`; die Meldung nennt den vollen Pfad. Warnung, kein
|
||||
Fehler — der Dienst startet, der Eintrag tut nur nichts.
|
||||
- **`TimeoutStopSec=300` in der Template-Unit**: ein laufendes OCR darf beim
|
||||
Stoppen zu Ende laufen. Ein `systemctl stop` kann dadurch pro Instanz bis zu
|
||||
5 Minuten dauern — das ist gewollt, ein SIGKILL wuerde den Durchlauf kosten.
|
||||
- **Feste Pins in `requirements.txt`** (`ocrmypdf==16.13.0`, `watchdog==6.0.0`,
|
||||
`requests==2.33.1`, `paramiko==4.0.0`). Ohne Pins zieht ein
|
||||
`pip install --upgrade` beim Update ungefragt einen Major-Sprung ein; ocrmypdf
|
||||
16 -> 17 wuerde alle Instanzen auf einmal reissen. Geprueft gegen Python 3.11
|
||||
(Debian 12) und 3.13 (Debian 13), Wheels fuer beide vorhanden.
|
||||
- 40 neue Tests (Wiederaufnahme aus `working/`, `--check-config`,
|
||||
Config-Warnungen). Suite jetzt **135 Tests**.
|
||||
|
||||
### Changed
|
||||
- **Die Betriebsdoku ist in drei Dokumente aufgeteilt.** Der README ist wieder
|
||||
der Einstieg (Kurzbeschreibung, Features, Schnellstart, Verzeichnis-Layout,
|
||||
Config-Ueberblick) und verlinkt:
|
||||
- `docs/INSTALLATION.md` — Erstinstallation, Basis-Install vs. Instanz-Anlage,
|
||||
die Abfragen pro Instanz, Multi-Instanz-Betrieb, LXC (Error 226/NAMESPACE),
|
||||
Ghostscript auf Debian 12, Instanz manuell loeschen und die vollstaendige
|
||||
**Konfigurationsreferenz**.
|
||||
- `docs/UPDATE.md` — Ablauf von `update.sh`, was es nicht anfasst,
|
||||
Backup-Inhalt/-Rechte/-Rotation, Rollback und dessen Grenzen,
|
||||
`--check-config` mit den Exit-Codes und Config-Drift.
|
||||
- `docs/OS-UPGRADE.md` — Debian-Major-Upgrade als eigener Ablauf.
|
||||
`AI_AGENT_BRIEFING.md` bleibt der Agent-Kontext (Aufbau und Begruendungen) und
|
||||
verweist fuer Ablaeufe auf die drei Dokumente, statt sie zu wiederholen.
|
||||
Nichts wird doppelt gepflegt.
|
||||
- **`update.sh` komplett ueberarbeitet** (`--help`, `--rebuild-venv`,
|
||||
`set -Eeuo pipefail`):
|
||||
- **venv-Health-Check und Neubau.** Geprueft werden Existenz, Lauffaehigkeit
|
||||
des Interpreters, `major.minor` gegen das System-Python und `pyvenv.cfg`.
|
||||
Passt etwas nicht — typisch nach einem Debian-Major-Upgrade, systemd meldet
|
||||
dann `203/EXEC` —, wird die venv neu gebaut, auch ohne `--rebuild-venv`. Der
|
||||
Neubau ist ganz oder gar nicht: alte venv weg sichern, neu bauen,
|
||||
Requirements installieren, **erst bei Erfolg** die alte loeschen; scheitert
|
||||
etwas, wird zurueckgerollt und hart abgebrochen. Scheitert pip an einem Pin,
|
||||
nennt das Skript das gescheiterte Paket und den naechsten Schritt
|
||||
("requirements.txt anheben").
|
||||
- **apt-Sync auch beim Update.** Die Paketliste wird aus `install.sh`
|
||||
extrahiert (einzige Quelle, Marken `BEGIN/END apt-packages`) und
|
||||
installiert; nachinstallierte Tesseract-Sprachpakete bleiben unangetastet
|
||||
(kein purge, kein autoremove). Fehlschlaege warnen nur.
|
||||
- **Haertere Verifikation.** Nach dem Start prueft `verify_unit()` nicht nur
|
||||
`is-active`, sondern auch `is-failed` und den Restart-Zaehler — ein
|
||||
Crash-Loop galt bei `Type=simple` bisher als Erfolg. Die Zusammenfassung
|
||||
stellt Soll gegen Ist und meldet eine **Regression** namentlich.
|
||||
- **Vollstaendiges Backup.** Gesichert werden Code, alle Instanz-Configs, die
|
||||
Template-Unit, alle Drop-ins und ein `pip-freeze.txt` der alten venv — ohne
|
||||
venv und ohne Datenverzeichnisse. Weil die Configs Klartext-Passwoerter
|
||||
enthalten, wird das Archiv mit `umask 077` erzeugt und auf `0600 root:root`
|
||||
gesetzt, das Verzeichnis auf `700`. Rotation: die letzten 5 Archive bleiben.
|
||||
- **ERR-Trap.** Bricht das Update ab (Fehler, Strg-C, `kill`), sagt das Skript,
|
||||
ob auf der Platte schon getauscht wurde, startet die vorher laufenden
|
||||
Instanzen wieder und nennt Backup-Datei und Rollback-Befehl.
|
||||
- **Instanz-Erfassung** deckt jetzt auch `activating` und `failed` ab (ueber
|
||||
`list-units --all`, `list-unit-files` und die vorhandenen Configs). Vorher
|
||||
kaputte Instanzen werden mitgestartet, gelten aber erst als Erfolg, wenn sie
|
||||
danach wirklich laufen; bewusst gestoppte bleiben gestoppt.
|
||||
- **Config-Pruefung vor dem Start**: `--check-config` je Instanz, Exit 2 zaehlt
|
||||
als Fehler (Update-Exit 1), Exit 1 wird als Warnung samt Nachstell-Befehl
|
||||
ausgegeben. Kennt der installierte Code das Flag noch nicht, wird die
|
||||
Pruefung uebersprungen und das Update laeuft weiter.
|
||||
- **`install.sh` repariert eine kaputte venv.** Bisher reichte das blosse
|
||||
Vorhandensein von `venv/`, um den Basis-Install zu ueberspringen — nach einem
|
||||
Distributions-Upgrade hat der Installer damit gar nichts repariert. Jetzt wird
|
||||
die venv gegen das System-Python geprueft und bei Drift nach
|
||||
`venv.old-<timestamp>` gesichert und neu gebaut.
|
||||
- Die apt-Paketliste steht als **einzige Quelle** in `install.sh` in der Funktion
|
||||
`pdf_ocr_apt_packages()` zwischen den Marken `# --- BEGIN apt-packages` /
|
||||
`# --- END apt-packages`. `update.sh` schneidet den Block heraus und wertet ihn
|
||||
aus — Marken und Funktionsname duerfen sich nicht ohne Anpassung aendern.
|
||||
|
||||
### Fixed
|
||||
- **Dateien in `working/` gingen nach einem harten Stopp still verloren.** Siehe
|
||||
Wiederaufnahme oben — der Fall trat bei jedem SIGKILL waehrend eines OCR-Laufs
|
||||
auf, also auch bei einem Update ohne `TimeoutStopSec`.
|
||||
- **Tippfehler in Config-Keys fielen nicht auf.** `load_config()` filterte
|
||||
stumm gegen die Dataclass-Annotationen; `[ocr].langauges` lief damit
|
||||
wirkungslos mit. Jetzt gibt es eine Warnung mit vollem Key-Pfad.
|
||||
|
||||
## [0.5.0] - 2026-09-22
|
||||
|
||||
### Added
|
||||
|
||||
Reference in New Issue
Block a user