feat(tools): remote <ziel> <aktion> als Vordertuer zum Server (D77-Nachtrag)

Deployen, Log ansehen, Dienst schalten und fragen was laeuft waren vier
verschiedene Beschwoerungen, drei davon von Hand als ssh + systemctl. Jetzt:

  remote backend deploy|upload|setup|install-jdk|reset-password
  remote backend start|stop|restart|status|enable|disable
  remote backend log|info|documents|backup
  remote frontend deploy|preview|info
  remote ssh

Die Skripte unter scripts/ bleiben die Implementierung und einzeln aufrufbar;
tools/remote bringt nur mit, wofuer es bisher nichts gab. Wo ein Schalter
noetig war, kam er ins Skript statt ins Werkzeug: --unit-only in
deploy-backend.sh (sonst kennte eine zweite Stelle die Unit-Platzhalter) und
--dry-run in deploy-prod.sh, das die Befoerderung ausdruecklich mit abschaltet.

Neu ist die Sicherung: H2 haelt die Datei offen, also anhalten, holen, wieder
starten (gemessen ~8 s Auszeit) - und das Archiv lesen, bevor der Befehl es
behaelt. Gegenprobe von Hand: lokal ausgepackt, Backend mit --werkbaum.data-dir
dagegen gestartet, es liefert genau die Dokumente des Servers.

.envrc legt tools/ auf den PATH (direnv), 217 Plan-Knoten, 0 Warnungen.
This commit is contained in:
mhoennig
2026-08-26 19:59:24 +02:00
parent 9443790382
commit ac6df1bb6b
11 changed files with 566 additions and 31 deletions
+1
View File
@@ -25,6 +25,7 @@ reverse.
- The backend can be deployed to a server: its own JDK, a systemd service, and Apache passing `/api/` on
- "Put on the server" in the document menu turns a local plan into a shared one and puts the link in the address bar
- A service endpoint `/api/v1/info` says which build is running — for deploys and monitoring
- One `remote` command drives the server: deploy, log, service state, and a database backup that is read back before it is kept
## 2026-08-25
+82
View File
@@ -6516,3 +6516,85 @@ Probe mit 404 und ist von außen nicht erreichbar (`server.address=127.0.0.1`,
gegengeprüft über die LAN-Adresse). Der Deploy selbst — SSH, `systemctl`, die
Proxy-Regel im Betrieb — läuft erst, wenn jemand ihn startet. Das ist dieselbe
Grenze wie in D25 und D72: Was die Umgebung stellt, stellt der Emulator nicht.
**Nachtrag — `tools/remote`: eine Vordertür, Ziel und Aktion (2026-08-26).**
Die Skripte deckten den Deploy ab und sonst nichts. Alles Übrige — Log
ansehen, Dienst schalten, fragen was läuft — war ein von Hand getipptes
`ssh … systemctl --user …`, jedes Mal samt der `XDG_RUNTIME_DIR`-Falle. Nach
dem Muster eines anderen Projekts des Nutzers gibt es dafür jetzt einen
Befehl: `remote <ziel> <aktion>`, mit `backend`, `frontend` und `ssh` als
Zielen. Eine `.envrc` legt `tools/` auf den PATH (direnv), sodass `remote`
ohne Pfad genügt.
**Die Skripte bleiben die Implementierung, `remote` ist die Vordertür.** Sie
sind in beiden READMEs und in diesem Eintrag beschrieben, einzeln aufrufbar
und in Vorbereitung eines CI-Laufs nützlich; sie in das Werkzeug zu ziehen
hätte einen großen Diff für keinen Gewinn gebracht. `remote` bringt nur mit,
wofür es bisher gar nichts gab: die systemd-Verben, `log`, `info`,
`documents`, `backup`. Der Preis ist benannt — zwei Namen für dieselbe Sache,
deshalb nennen die READMEs jetzt `remote …` zuerst.
**Wo es einen Schalter brauchte, kam der ins Skript, nicht ins Werkzeug.**
`remote backend setup` schreibt nur die Unit neu; dafür hat
`deploy-backend.sh` ein `--unit-only` bekommen, statt dass `tools/remote`
die Platzhalter der Vorlage ein zweites Mal ersetzt. Genau diese Verdopplung
ist in D16 schon einmal teuer geworden (dieselben `sed`-Regeln in Workflow
und Skript). Ebenso `remote frontend preview``deploy-prod.sh --dry-run`.
**`--dry-run` schaltet die Beförderung ausdrücklich mit ab.** Sie läuft als
Schritt 0, also **vor** der rsync-Vorschau, und macht einen Commit (D30) —
ein Probelauf, der etwas schreibt, ist keiner. Gebaut und zusammengestellt
wird trotzdem, sonst wüsste der Vergleich nicht, wogegen er läuft.
Nachgemessen: HEAD und der Plan bleiben unangetastet.
**Sichern heißt anhalten.** H2 hält die Datei offen, solange der Dienst
läuft; eine Kopie im Betrieb kann zerrissen sein, und eine Sicherung, der man
nicht trauen kann, ist keine. `remote backend backup` hält den Dienst an,
holt `data/` als Tar-Strom und startet ihn wieder — gemessen rund 8 s Auszeit.
Zwei Feinheiten, die beide lautlos zuschlagen würden: Der Strom geht über
stdout in die Datei, **alle** Meldungen müssen deshalb nach stderr (sonst
landen sie im Archiv), und der Trap fängt `PIPE` mit ab — bricht die lokale
Seite weg, bliebe der Dienst sonst ausgerechnet dann unten.
**Und der Befehl liest das Archiv, bevor er es behält.** Erst nach
`tar tzf` und dem Nachweis, dass eine H2-Datei darin liegt, wird aus
`.teil` die endgültige Datei. Die eigentliche Gegenprobe lief einmal von
Hand und gehört hierher, weil sie die Zusage prüft und nicht die Mechanik:
Das Archiv lokal ausgepackt, das Backend mit `--werkbaum.data-dir` dagegen
gestartet — es kommt hoch und liefert genau die Dokumente, die auch auf dem
Server stehen. Ein Archiv, das nie jemand geöffnet hat, ist eine Hoffnung.
**`backend info` fragt über die öffentliche Adresse**, nicht am Dienst
vorbei: So ist die Proxy-Regel mitgeprüft. Die Domain wird aus dem
rsync-Ziel des Frontends abgelesen (`…/doms/<domain>/…`); ohne sie fragt der
Befehl direkt an `127.0.0.1` **und sagt, dass er es tut**. `frontend info`
liest den Versions-Link aus dem ausgelieferten Footer (D16) und vergleicht
den Commit mit dem eigenen HEAD — die einzige Stelle, an der die Datei
selbst sagt, was sie ist.
**`documents` bekommt das Passwort nie über die Kommandozeile.** Ohne
Angabe fragt curl selbst danach; mit `--from-env` kommt es als
`curl -K -` über stdin. Die Begründung steht schon im
`reset-password`-Nachtrag oben und gilt hier wörtlich: Argumente stehen in
der Prozessliste, und die Shell verändert das Passwort vorher.
**Gemessen statt geraten, zwei Kleinigkeiten am Rand:** Ein `frontend log`
gibt es **nicht**`~/doms/werkbaum.javagil.de/var/` ist leer, der Managed
Webspace reicht die Apache-Logs nicht ins Home (nur monatliche
`~/var/domaintraffic-*.log`); an seine Stelle tritt `frontend info`. Und
`ssh -t` gehört nur dorthin, wo es ein Terminal gibt: Sonst steht
„Pseudo-terminal will not be allocated" als erste Zeile mitten im Log
(beim Bauen gemessen).
**Der Befehl wartet, bis der Dienst wieder antwortet.** Die erste Fassung von
`backup` meldete „Dienst wieder gestartet" und das nächste `info` bekam ein
503 — der Start dauert rund 8 s. Ein Werkzeug, das eine Sache meldet, die
gleich darauf nicht stimmt, ist schlechter als eines, das schweigt.
**Nachgemessen** gegen die produktive Instanz: `backend info` (200 samt
Version), `frontend info` (1.1.162, Commit = HEAD), `backend status`,
`backend log`, `backend documents --from-env` (liefert die Dokumente),
`backend setup -y` (Unit neu, Jar unangetastet — mtime unverändert),
`backend restart`, `backend backup` samt lokalem Wiederanlauf,
`frontend preview` (nichts geschrieben, HEAD unverändert) und die
Fehlerpfade (Exit-Code 2 bei unbekanntem Ziel und fehlender Aktion).
+13 -1
View File
@@ -107,10 +107,12 @@
- [^] #bld.pwa.sw: A service worker that serves the app offline (S) %% network-first, cache as fallback
- [^] #bld.pwa.update: The reload notice stays truthful under the worker (S) %% see D45/D73
- [^] #bld.pwa.files: Double-click a .werkbaum file, save without any dialog (S) :#ed.files.inplace %% file_handlers + launchQueue
- [^] #bld.backend: Ship the backend as a service of its own (M)
- [^] #bld.backend: Ship the backend as a service of its own (L)
- [^] #bld.backend.jdk: Its own JDK 21 in the server's home (S) %% only 17 is installed there
- [^] #bld.backend.unit: A systemd user unit that survives the session (S)
- [^] #bld.backend.proxy: Apache passes /api/ on, in .htaccess (XS) %% measured: the P flag is allowed
- [x] #bld.backend.remote: One command for everything on the server (S) %% remote <target> <action>
- [x] #bld.backend.backup: Save the database, and prove it can be read back (XS)
- [-] #bld.ghpages: Deploy through a gh-pages branch (S) %% GitHub Actions instead
- [-] #bld.gfonts: Fonts from Google (XS) %% self-hosted, no third-party request
- [ ] #be: Backend (XL)
@@ -749,6 +751,16 @@
.htaccess and the vhost directory is out of reach. Measured on the target: a
request is held open for 30 seconds, which is what the change feed needs.
#bld.backend.remote
Deploying, reading the log, asking what is running and switching the service
were four different incantations, three of them typed by hand as ssh plus
systemctl. One front door names them all as target and action.
#bld.backend.backup
The database is a file in a home directory, and H2 holds it open, so a copy
taken while the service runs can be torn. Stop, fetch, start again — and read
the archive back before trusting it.
#bld.ghpages
Rejected: the official Actions route needs no extra branch and no deploy
key.