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
+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).