Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion .github/workflows/docker-worker.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Purpose: Build and publish reviewed worker images for changed runtime sources.
# Input/Output: Reads source files and writes immutable multi-architecture images to GHCR.
# Invariants: Documentation-only and Compose-pin commits do not create a new image digest.
# Debugging: Inspect the Build and push worker image step plus its published digest.

name: Docker Worker Publish

on:
workflow_dispatch:
push:
branches:
- main
- "**"
tags:
- "v*"
paths:
- "src/**"
- "requirements.txt"
- "docker/Dockerfile"
- "docker/entrypoint.sh"
- "custom_components/paperless_kiplus/manifest.json"
- ".github/workflows/docker-worker.yml"

permissions:
contents: read
Expand All @@ -19,6 +31,14 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Read application version
id: app
shell: bash
run: |
set -euo pipefail
VERSION=$(python3 -c 'import json; print(json.load(open("custom_components/paperless_kiplus/manifest.json", encoding="utf-8"))["version"])')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

Expand Down Expand Up @@ -52,3 +72,6 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
APP_COMMIT=${{ github.sha }}
APP_VERSION=${{ steps.app.outputs.version }}
48 changes: 48 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Purpose: Run the local Python, manifest, Compose, and image checks in CI.
# Input/Output: Reads the repository at the pushed commit and publishes only test results.
# Invariants: No deployment, production secret, or remote Paperless access is required.
# Debugging: Re-run the first failed command locally from the repository root.

name: Python + Docker Tests

on:
pull_request:
push:

permissions:
contents: read

jobs:
test:
name: Python 3.12
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip

- name: Install test dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r requirements.txt pytest

- name: Compile Python sources
run: >-
python -m py_compile
src/*.py
custom_components/paperless_kiplus/*.py
tests/*.py

- name: Run unit and integration tests
run: python -m pytest -q

- name: Validate production Compose
run: docker compose -f docker/docker-compose.unraid-broker.yml config --quiet

- name: Build production worker image
run: docker build --file docker/Dockerfile --tag paperless-kiplus-worker:test .
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Changelog

## Unreleased

### Added

- Persistente, idempotente HTTP-202-Hintergrundjobs für Sorter, Restart,
Entity-Scan und Entity-Merge.
- Authentifizierter Jobstatus mit sicheren Request-IDs, exaktem Fortschritt
und Reload-Recovery in beiden Weboberflächen.
- Restart-, Parallelitäts-, Redaction-, API- und Docker-CI-Tests.

### Changed

- Home Assistant pollt Remote-Jobs bis zum terminalen Status.
- Browser-Token werden nur noch sitzungsbezogen gespeichert.
- Lange Live-Review-Abfragen verwenden den Job-Endpunkt.

### Security

- API-, Log- und Konfigurationsantworten sind nicht cachebar.
- Jobfehler und Worker-Logs maskieren Zugangsdaten und Providerdetails.
- Unterbrochene Schreibjobs werden nicht automatisch wiederholt.
- Python-Basisimage und Runtime-Abhängigkeiten sind reproduzierbar gepinnt.
- Produktion baut commitgebunden im Broker und benötigt keinen privaten
Registry-Pull.
30 changes: 24 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,30 @@ Vielen Dank für dein Interesse an Beiträgen zu **Paperless KIplus**.

## Lokale Checks

Vor einem PR bitte mindestens:

1. Syntax prüfen:
- `python3 -m py_compile custom_components/paperless_kiplus/*.py src/paperless_ai_sorter.py`
2. Integration laden und einen Testlauf in Home Assistant durchführen
3. Prüfen, dass README/Docs bei neuen Features aktualisiert sind
Vor einem PR bitte aus dem Repository-Root ausführen:

```bash
python3 -m pip install -r requirements.txt pytest
python3 -m pytest -q
docker compose -f docker/docker-compose.unraid-broker.yml config --quiet
docker build -f docker/Dockerfile -t paperless-kiplus-worker:test .
```

Bei Änderungen an Hintergrundjobs zusätzlich mindestens einen Happy Path,
einen ungültigen Input, Restart-Recovery und fünf wiederholte parallele
Admission-Läufe testen. Echte Paperless- oder API-Token dürfen nie in Fixtures,
Logs oder Fehlermeldungen erscheinen.

Für gezieltes Debugging:

```bash
PAPERLESS_KIPLUS_LOG_LEVEL=DEBUG python3 src/worker_api.py --data-dir ./worker-data
python3 -m unittest tests.test_background_jobs -v
```

Anschließend die Integration in Home Assistant laden und einen kleinen Dry-Run
gegen eine Testinstanz durchführen. README und Betriebsdoku müssen das neue
Verhalten erklären.

## Pull-Request Ablauf

Expand Down
92 changes: 28 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -901,13 +901,15 @@ Was der Worker mitbringt:
- vollständige Ausführung ohne Home Assistant
- eingebaute Weboberfläche unter `/`
- Entity-Review-Seite unter `/review` für Dopplungen und KI-Lernhinweise
- JSON-API für Run / Stop / Resume / Restart / Backfill
- persistente HTTP-202-Jobs für Run / Resume / Restart / Backfill und Review
- Idempotency-Keys, sichere Request-IDs und Reload-Recovery mit Backoff
- Log-Download, Status und Konfigurationsverwaltung
- persistente Dateien für Config, Metriken und Resume-State unter `/data`

Dokumentation:

- [Docker- und Unraid-Betrieb](./docs/docker-unraid.md)
- [Cloudflare-sichere Langläufer](./docs/cloudflare-long-running-jobs.md)
- [Migration von Home Assistant zum Remote-Worker](./docs/migration-ha-to-worker.md)
- [Lokale LLMs für kleinere Aufgaben](./docs/local-llm-routing.md)

Expand All @@ -925,76 +927,38 @@ Danach:
- Dopplungsreview: `http://<server>:8787/review`
- Status-API: `http://<server>:8787/api/status`

### Robuste Unraid-Installation
### Sichere Unraid-Installation in der Feberdin-Umgebung

Für Unraid gibt es jetzt zwei klare Wege:
Die produktive Compose-Quelle liegt unter
`docker/docker-compose.unraid-broker.yml`. Deployments erfolgen ausschließlich
über den Unraid Deployment Broker und einen vollständigen Git-Commit-SHA:

1. Von macOS/Linux per SSH auf einen entfernten Unraid-Server deployen
2. Direkt im Unraid-Terminal ohne Repo-Checkout installieren
3. Direkt auf dem Unraid-Server mit vorhandenem Repo installieren
1. `stack_source_status`
2. `stack_validate`
3. `deploy_plan`
4. `approval_request`, falls der Plan dies verlangt
5. `deploy_apply`
6. `deployment_status`, `docker_list` und `logs_tail`

#### Empfohlen: Remote-Deploy von macOS/Linux nach Unraid
Der Stack erhält `PAPERLESS_KIPLUS_TOKEN` zur Laufzeit über
`secret://PAPERLESS_KIPLUS_TOKEN`. Echte Tokenwerte gehören weder in Git noch
in Chat, Logs oder Compose. Das bestehende Appdata-Verzeichnis
`/mnt/user/appdata/paperless-kiplus` wird unverändert als `/data` eingebunden.

```bash
bash docker/deploy-to-unraid.sh \
--unraid-host 192.168.178.30 \
--paperless-url http://192.168.178.20:8000 \
--paperless-token PAPERLESS_TOKEN \
--ai-api-key OPENAI_KEY \
--ai-model gpt-4.1-mini
```

Das Remote-Skript:

- verbindet sich per SSH mit Unraid
- kopiert den eigentlichen Host-Installer auf den Server
- überträgt optional eine lokale `config.yaml`
- führt die Installation direkt auf Unraid aus

#### Direkte Ausführung auf dem Unraid-Server

Wenn du direkt im Unraid-Terminal bist und das Repo dort nicht lokal liegen
hast, kannst du jetzt den Bootstrap-Weg nutzen. Er legt zuerst den passenden
Ordner an, lädt den Installer herunter und startet ihn direkt:

```bash
mkdir -p /boot/config/custom/paperless-kiplus && \
curl -fsSL https://raw.githubusercontent.com/Feberdin/Paperless-KIplus/v1.4.6/docker/bootstrap-unraid-worker.sh \
-o /boot/config/custom/paperless-kiplus/bootstrap-unraid-worker.sh && \
chmod +x /boot/config/custom/paperless-kiplus/bootstrap-unraid-worker.sh && \
bash /boot/config/custom/paperless-kiplus/bootstrap-unraid-worker.sh \
--ref v1.4.6 \
--paperless-url http://192.168.178.20:8000 \
--paperless-token PAPERLESS_TOKEN \
--ai-api-key OPENAI_KEY \
--ai-model gpt-4.1-mini
```

Dabei wird der Installer standardmäßig hier abgelegt:

```text
/boot/config/custom/paperless-kiplus/install-unraid-worker.sh
```

Wenn du bereits ein Repo-Checkout auf Unraid hast, kannst du weiterhin direkt
das Host-Skript verwenden:
Der Broker baut das Worker-Image aus dem commitgebundenen Checkout. Dockerfile,
Basisimage, Python-Abhängigkeiten, App-Version und geprüfter App-Commit sind
festgelegt; damit ist kein privater Registry-Pull für die Produktion nötig.

```bash
bash /pfad/zum/repo/docker/install-unraid-worker.sh \
--paperless-url http://192.168.178.20:8000 \
--paperless-token PAPERLESS_TOKEN \
--ai-api-key OPENAI_KEY \
--ai-model gpt-4.1-mini
```
Ein Rollback verwendet denselben Broker-Ablauf mit dem vorherigen GitOps-Commit.
Direkte SSH-, Docker-CLI- oder Unraid-Shell-Deployments sind für diese
Produktionsumgebung nicht vorgesehen.

Das Host-Skript:
### Logging und Fehlersuche

- legt die Appdata-Verzeichnisse an
- sichert bestehende Dateien
- erzeugt eine startfähige `config.yaml`
- schreibt einen Compose-Stack mit GHCR-Image
- startet oder aktualisiert den Worker
- prüft die API per Health-Check
Der Standard-Level ist `INFO`. Für einen zeitlich begrenzten Diagnose-Lauf kann
im Broker-Stack `PAPERLESS_KIPLUS_LOG_LEVEL=DEBUG` gesetzt werden. Logs werden
über `logs_tail` abgerufen; Zugangsdaten werden vor Datei-, Speicher- und
UI-Ausgabe maskiert. Jobfehler lassen sich über ihre `request_id` zuordnen.

### Remote-Steuerung aus Home Assistant

Expand Down
2 changes: 1 addition & 1 deletion custom_components/paperless_kiplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"iot_class": "local_polling",
"issue_tracker": "https://github.com/Feberdin/Paperless-KIplus/issues",
"requirements": [],
"version": "1.4.20"
"version": "1.4.21"
}
Loading
Loading