Build package / php-lint (8.5) (push) Successful in 1m51s
Build package / xml-lint (push) Successful in 49s
Build package / unit-tests (push) Successful in 1m51s
Build package / php-lint (8.4) (push) Successful in 1m47s
Build package / package (push) Successful in 2m39s
appstore.yml signs the tarball of an existing v* release and registers its Gitea download URL with the App Store; it only runs on workflow_dispatch. Also adds the AGPL LICENSE and points website, bugs and repository in info.xml at the Gitea repository.
133 lines
7.6 KiB
Markdown
133 lines
7.6 KiB
Markdown
# Entwicklung
|
|
|
|
Informationen für Entwickler und Admins, die tiefer einsteigen. Die Nutzer-Doku steht in der [README](README.md). Ausführliche Hintergründe zu den Stolperfallen, also warum etwas genau so gebaut ist, stehen in [CLAUDE.md](CLAUDE.md).
|
|
|
|
## Aufbau
|
|
|
|
- **Keine HTTP/OCS-Aufrufe gegen Deck.** Lesen (Boards, Stapel, Karten, Board-Mitglieder) und das Verschieben laufen ausschließlich über Decks interne PHP-Klassen im selben Prozess. Sämtlicher Deck-Zugriff ist in [`lib/Service/DeckIntegrationService.php`](lib/Service/DeckIntegrationService.php) gebündelt.
|
|
- `OCA\Deck\*` ist keine stabile öffentliche API. Jeder Zugriff ist deshalb abgesichert: `class_exists()`-Checks, Prüfung ob Deck aktiviert ist, try/catch mit Logging.
|
|
- Nach größeren Deck-Updates lohnt sich ein Blick ins Nextcloud-Log.
|
|
- **Hintergrundjob:** [`RunWorkflowsJob`](lib/BackgroundJob/RunWorkflowsJob.php) ist ein einfacher `Job` ohne Intervall. Er läuft bei jedem Cron-Durchlauf und ruft [`WorkflowRunner`](lib/Service/WorkflowRunner.php) auf.
|
|
- **Rechte-Kontext:** Der Runner arbeitet die Workflows nach Eigentümer gruppiert ab und setzt Deck jeweils auf diesen Nutzer, per `DeckIntegrationService::beginUserContext()`. Decks Berechtigungsprüfungen lesen nämlich nicht die Session, sondern eine pro Prozess festgelegte Nutzer-ID.
|
|
- **E-Mail:** [`NotificationMailer`](lib/Service/NotificationMailer.php) verschickt über Nextclouds `IMailer`. Die Empfänger ermittelt `DeckIntegrationService::findNotificationRecipients()`, die Entscheidungsregel steckt in [`NotificationPolicy`](lib/Service/NotificationPolicy.php).
|
|
- **Nextcloud-Benachrichtigungen:** [`NotificationPusher`](lib/Service/NotificationPusher.php) legt sie über `OCP\Notification\IManager` an, eine pro Karte, an dieselben Empfänger wie die Mail. Er speichert nur Rohwerte. Den deutschen Text baut [`Notifier`](lib/Notification/Notifier.php), registriert in `Application::register()`. Ist die App `notifications` für einen Empfänger nicht aktiv, wird nichts angelegt. Push aufs Handy übernimmt die Notifications-App selbst.
|
|
- **Oberfläche:** [`src/PersonalSettings.vue`](src/PersonalSettings.vue) (Vue 3, `@nextcloud/vue` 9) spricht über [`src/api.js`](src/api.js) mit dem OCS-Controller [`WorkflowController`](lib/Controller/WorkflowController.php).
|
|
|
|
## Datenmodell
|
|
|
|
Die Tabelle `wfda_workflows` enthält eine Zeile pro Workflow:
|
|
|
|
| Spalte | Bedeutung |
|
|
| --- | --- |
|
|
| `board_id`, `source_stack_id`, `target_stack_id` | Deck-IDs |
|
|
| `user_id` | Board-Eigentümer, in dessen Namen der Job läuft. Wird bei jedem Lauf aus Deck aktualisiert. |
|
|
| `notify_mode` | `off`, `owner` oder `board` (wer) |
|
|
| `notify_channel` | `mail`, `notification` oder `both` (wie) |
|
|
| `enabled`, `last_run` | aktiv? / letzter Lauf |
|
|
|
|
Migrationen in [`lib/Migration/`](lib/Migration/):
|
|
- `Version1000…` legt die Tabelle an.
|
|
- `Version1001…` entfernt die früheren Filter-Spalten.
|
|
- `Version1002…` und `Version1003…` ersetzen `notify_email` durch `notify_mode`. Ein gesetztes Häkchen wird dabei zu `owner`.
|
|
- `Version1004…` fügt `notify_channel` hinzu. Bestehende Workflows bekommen `mail`.
|
|
|
|
Migrationen laufen nur, wenn die App-Version steigt.
|
|
|
|
## Lokales Setup
|
|
|
|
Voraussetzungen: PHP 8.4+, Composer, Node.js 24 mit npm.
|
|
|
|
```bash
|
|
composer install
|
|
npm install # nicht `npm ci`: Lockfiles werden bewusst nicht committet
|
|
npm run build # Produktions-Build
|
|
npm run watch # Entwicklung mit automatischem Rebuild
|
|
```
|
|
|
|
## Tests und Linting
|
|
|
|
```bash
|
|
composer run lint # php -l über lib/ und tests/
|
|
composer run cs:check # nextcloud/coding-standard (cs:fix korrigiert)
|
|
composer run test:unit # PHPUnit
|
|
```
|
|
|
|
Die Unit-Tests decken nur reine Logik ab, ohne Deck-Installation:
|
|
- Fälligkeitsprüfung (`WorkflowRunner::isOverdue()`)
|
|
- Empfängerregel (`NotificationPolicy::shouldNotify()`)
|
|
- Benachrichtigungsweg (`NotificationPolicy::sendsMail()`/`sendsNotification()`)
|
|
|
|
Die Deck-Anbindung lässt sich nur auf einer echten Nextcloud-Instanz mit Deck testen:
|
|
|
|
```bash
|
|
# Job-ID und letzten Lauf anzeigen
|
|
php occ background-job:list --class 'OCA\WorkflowDeckAutomation\BackgroundJob\RunWorkflowsJob'
|
|
|
|
# Sofort ausführen, ohne auf Cron zu warten
|
|
php occ background-job:execute <id> --force-execute
|
|
```
|
|
|
|
## Build, CI und Release
|
|
|
|
`make appstore` erzeugt `build/artifacts/appstore/workflow_deck_automation.tar.gz`.
|
|
|
|
Gebaut und veröffentlicht wird in [`.gitea/workflows/build-main.yml`](.gitea/workflows/build-main.yml):
|
|
|
|
| Job | Zweck |
|
|
| --- | --- |
|
|
| `php-lint` | `php -l` mit PHP 8.4 und 8.5 |
|
|
| `xml-lint` | prüft `appinfo/info.xml` gegen das Appstore-Schema |
|
|
| `unit-tests` | PHPUnit |
|
|
| `package` | Frontend-Build und `make appstore`. Läuft nur, wenn die Checks grün sind. |
|
|
|
|
Das Paket wird als Release veröffentlicht:
|
|
- Bei jedem Push auf `main` als Pre-Release **`latest-main`**, das jedes Mal neu erzeugt wird.
|
|
- Bei einem `v*`-Tag als reguläres Release.
|
|
- Bei Pull Requests wird nur gebaut, nicht veröffentlicht.
|
|
|
|
**Versionen** folgen dem Schema `<Nextcloud-Major>.<minor>.<patch>`. Die unterstützte Nextcloud-Version steht in `appinfo/info.xml`.
|
|
|
|
Ablauf für ein Release:
|
|
1. Version in `appinfo/info.xml` und `package.json` gleich anheben.
|
|
2. Auf `main` pushen und warten, bis die Pipeline grün ist.
|
|
3. `git tag -a vX.Y.Z -m "…" && git push origin vX.Y.Z`.
|
|
4. Optional in den App Store: in Gitea unter *Actions* den Workflow „Publish to App Store“ von Hand starten und den Tag angeben.
|
|
|
|
### App Store
|
|
|
|
[`.gitea/workflows/appstore.yml`](.gitea/workflows/appstore.yml) läuft nur von Hand (`workflow_dispatch`). Er baut nichts selbst. Er lädt den Tarball eines vorhandenen `vX.Y.Z`-Release ohne Anmeldung herunter und prüft, ob die Version in `info.xml` zum Tag passt. Dann signiert er das Archiv (SHA-512) und meldet die Download-URL samt Signatur an die App-Store-API. Der Store lädt den Tarball danach selbst von Gitea. Die Releases müssen deshalb öffentlich erreichbar sein, und ein Tag darf nach dem Store-Upload nicht mehr verschoben werden.
|
|
|
|
Benötigte Repository-Secrets:
|
|
|
|
| Secret | Inhalt |
|
|
| --- | --- |
|
|
| `APPSTORE_PRIVATE_KEY` | privater Schlüssel (PEM), zu dem Nextcloud das App-Zertifikat ausgestellt hat |
|
|
| `APPSTORE_TOKEN` | API-Token von <https://apps.nextcloud.com/account/token> |
|
|
|
|
Einmalig vorher: Zertifikat über [nextcloud/app-certificate-requests](https://github.com/nextcloud/app-certificate-requests) beantragen und die App auf apps.nextcloud.com registrieren. Die App wird nicht per Code-Signing (`appinfo/signature.json`) signiert. Nextcloud prüft Apps, die nicht zum Core gehören, nur dann, wenn diese Datei vorhanden ist.
|
|
|
|
Einzelheiten, etwa zum Verschieben eines Tags nach einem fehlgeschlagenen Build, stehen in [CLAUDE.md](CLAUDE.md).
|
|
|
|
## Fehlersuche: Der Job läuft nur nachts oder gar nicht
|
|
|
|
Nextcloud überspringt „nicht zeitkritische“ Jobs außerhalb des Wartungsfensters (`maintenance_window_start` in der `config.php`). Bis einschließlich v0.1.0 war dieser Job als nicht zeitkritisch markiert. Diese Markierung bleibt in der Spalte `oc_jobs.time_sensitive` stehen, auch nach App-Updates.
|
|
|
|
Symptom: Der Job läuft nur im 4-Stunden-Wartungsfenster, oder `occ background-job:list` zeigt als letzten Lauf `1970-01-01`.
|
|
|
|
Einmalig korrigieren:
|
|
|
|
```sql
|
|
UPDATE oc_jobs SET time_sensitive = 0
|
|
WHERE class = 'OCA\\WorkflowDeckAutomation\\BackgroundJob\\RunWorkflowsJob';
|
|
```
|
|
|
|
Oder ohne Datenbankzugriff den Job löschen und durch Aus- und Einschalten der App neu registrieren:
|
|
|
|
```bash
|
|
php occ background-job:delete <id>
|
|
php occ app:disable workflow_deck_automation
|
|
php occ app:enable workflow_deck_automation
|
|
```
|
|
|
|
Die frühere `config.php`-Option `workflow_deck_automation.interval` (bis v0.1.0) wird ignoriert und kann entfernt werden.
|