diff --git a/apps/tesm/src/tesm/services/monitor.py b/apps/tesm/src/tesm/services/monitor.py index 773c84c..a2ca7cc 100644 --- a/apps/tesm/src/tesm/services/monitor.py +++ b/apps/tesm/src/tesm/services/monitor.py @@ -106,7 +106,13 @@ def ping(host: str, *, timeout: int = PING_TIMEOUT_SECONDS) -> PingResult: command = [binary, "-c", "1", "-W", str(timeout), host] try: completed = subprocess.run( # noqa: S603 - feste Argumentliste, keine Shell - command, capture_output=True, text=True, timeout=timeout + 3, check=False + command, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + timeout=timeout + 3, + check=False, ) except subprocess.TimeoutExpired: return PingResult(False, detail="Zeitlimit ueberschritten") diff --git a/apps/tesm/src/tesm/services/windowsops.py b/apps/tesm/src/tesm/services/windowsops.py index ef874dd..1185abe 100644 --- a/apps/tesm/src/tesm/services/windowsops.py +++ b/apps/tesm/src/tesm/services/windowsops.py @@ -167,7 +167,13 @@ def _net_with_credentials( command = [binary, *args, "--authentication-file", path] try: completed = subprocess.run( # noqa: S603 - feste Argumentliste, keine Shell - command, capture_output=True, text=True, timeout=timeout, check=False + command, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + timeout=timeout, + check=False, ) except subprocess.TimeoutExpired: return RemoteResult(False, "", f"Zeitlimit von {timeout}s ueberschritten.") diff --git a/deploy/install.sh b/deploy/install.sh index 8130693..05dcc82 100644 --- a/deploy/install.sh +++ b/deploy/install.sh @@ -112,6 +112,21 @@ MONITOR_UNIT="$NAME-monitor.service" NGINX_SITE="/etc/nginx/sites-available/$NAME" ENV_PREFIX="TESM_" [[ "$APP" == "tesm-license" ]] && ENV_PREFIX="TESM_LICENSE_" +# Bei einer Aktualisierung gilt zuerst, was schon eingerichtet ist. Sonst +# verschiebt ein Update ohne Portangaben die Anwendung: eine benannte Instanz +# faellt auf 8080/8443 zurueck, auch wenn sie seit der Installation auf Port 80 +# lief -- und ein Reverse Proxy davor zeigt dann ins Leere. Dieselbe Regel wie +# beim Secure-Flag weiter unten: eine Aktualisierung nimmt nichts weg, was +# nicht ausdruecklich neu gesetzt wird. +ENV_FILE="/etc/tesm/$NAME.env" +if [[ -z "$PORT" && -f "$ENV_FILE" ]]; then + PORT="$(sed -n "s/^${ENV_PREFIX}BIND=127\.0\.0\.1:\([0-9]\{1,5\}\)$/\1/p" "$ENV_FILE" | head -1)" +fi +if [[ -f "$NGINX_SITE" ]]; then + [[ -z "$HTTP_PORT" ]] && HTTP_PORT="$(sed -n 's/^[[:space:]]*listen[[:space:]]\([0-9]\{1,5\}\);.*/\1/p' "$NGINX_SITE" | head -1)" + [[ -z "$HTTPS_PORT" ]] && HTTPS_PORT="$(sed -n 's/^[[:space:]]*listen[[:space:]]\([0-9]\{1,5\}\)[[:space:]]\+ssl.*/\1/p' "$NGINX_SITE" | head -1)" +fi + if [[ -z "$PORT" ]]; then PORT=5000 [[ "$APP" == "tesm-license" ]] && PORT=5001 @@ -134,7 +149,7 @@ fi # Browser sendet das Cookie dann weiter nur ueber HTTPS, die Anwendung setzt es # aber ohne das Flag neu, und beide reden aneinander vorbei. COOKIE_SECURE=0 -if [[ -f "/etc/tesm/$NAME.env" ]] && grep -q "^${ENV_PREFIX}COOKIE_SECURE=1" "/etc/tesm/$NAME.env"; then +if [[ -f "$ENV_FILE" ]] && grep -q "^${ENV_PREFIX}COOKIE_SECURE=1" "$ENV_FILE"; then COOKIE_SECURE=1 fi [[ $HTTPS -eq 1 ]] && COOKIE_SECURE=1 diff --git a/deploy/update.sh b/deploy/update.sh index feb2e04..2f4d496 100644 --- a/deploy/update.sh +++ b/deploy/update.sh @@ -14,15 +14,31 @@ set -euo pipefail APP="${1:-}" TAG="${2:-latest}" -BASE="${TESM_RELEASE_BASE:-https://gitea.example.invalid}" -OWNER="${TESM_RELEASE_OWNER:-wis}" -REPO="${TESM_RELEASE_REPO:-tesm}" + +# Alles nach dem Tag geht unveraendert an install.sh weiter. Ohne das liesse +# sich eine benannte Instanz nicht aktualisieren: install.sh leitet den Namen +# aus "--instance" ab, und ohne den Schalter entstuende neben "tesm-opus" eine +# zweite Installation namens "tesm" -- mit eigener Datenbank, eigener +# nginx-Site und demselben Port. +shift $(( $# > 2 ? 2 : $# )) +EXTRA=("$@") +# Bezugsquelle. Die Vorgaben zeigen dorthin, wo die Releases tatsaechlich +# liegen -- ein Platzhalter wie "gitea.example.invalid" macht das Skript auf +# jedem Host unbrauchbar, und genau das war hier der Fall. +# +# Das Repository heisst wie die Anwendung; getrennte Repositorien, getrennte +# Releases. Der Anhang heisst "-.tar.gz", siehe unten. +BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}" +OWNER="${TESM_RELEASE_OWNER:-alientim}" [[ "$APP" == "tesm" || "$APP" == "tesm-license" ]] || { - echo "Verwendung: update.sh [tag]" >&2 + echo "Verwendung: update.sh [tag] [install.sh-Argumente ...]" >&2 + echo "Beispiel : update.sh tesm latest --instance opus" >&2 exit 2 } +REPO="${TESM_RELEASE_REPO:-$APP}" + WORK="$(mktemp -d /tmp/tesm-update-XXXXXX)" trap 'rm -rf "$WORK"' EXIT # anders als im Vorgaenger wird aufgeraeumt @@ -50,7 +66,7 @@ if file "$SRC/deploy/install.sh" | grep -q CRLF; then exit 1 fi -read -r -p "Version $TAG fuer $APP installieren? [j/N] " answer +read -r -p "Version $TAG fuer $APP${EXTRA[0]+ (${EXTRA[*]})} installieren? [j/N] " answer [[ "$answer" =~ ^[jJyY]$ ]] || exit 0 -exec sudo bash "$SRC/deploy/install.sh" --app "$APP" --yes +exec sudo bash "$SRC/deploy/install.sh" --app "$APP" --yes ${EXTRA[0]+"${EXTRA[@]}"} diff --git a/docs/BETRIEB.md b/docs/BETRIEB.md index a5b3a99..92acd01 100644 --- a/docs/BETRIEB.md +++ b/docs/BETRIEB.md @@ -103,6 +103,44 @@ Aktualisierung ist, und tut dann zusätzlich: Eine bereits eingerichtete HTTPS-Konfiguration bleibt erhalten, auch ohne `--https`. +### Ohne Quellbaum: `update.sh` + +Liegt kein entpacktes Release vor, holt `update.sh` es selbst -- es lädt das +Release-Asset, entpackt es und ruft dessen `install.sh` auf: + +```bash +sudo bash /opt/tesm/deploy/update.sh tesm +sudo bash /opt/tesm-license/deploy/update.sh tesm-license +``` + +Alles nach dem Tag geht unverändert an `install.sh` weiter -- das ist der Weg, +eine **benannte Instanz** zu aktualisieren: + +```bash +sudo bash /opt/tesm-opus/deploy/update.sh tesm latest --instance opus +``` + +Ohne `--instance` entstünde daneben eine zweite Installation namens `tesm`, mit +eigener Datenbank und eigener nginx-Site. Die Ports einer bestehenden +Installation bleiben erhalten, auch wenn sie von den Vorgaben abweichen: eine +Aktualisierung verschiebt nichts, was nicht ausdrücklich neu gesetzt wird. + +Ohne Tag wird `latest` **über die API aufgelöst**, nicht in die URL geschrieben: +Gitea liefert unter einem Tag-Namen sonst ein automatisch erzeugtes +Quell-Archiv mit HTTP 200 statt des echten Anhangs -- und das enthält kein +Release, sondern den Repositoriumsstand. + +Die Bezugsquelle ist voreingestellt und lässt sich über die Umgebung umlenken, +etwa auf eine Spiegelung: + +| Variable | Vorgabe | +|---|---| +| `TESM_RELEASE_BASE` | `https://gitea.int.eertmoed.net` | +| `TESM_RELEASE_OWNER` | `alientim` | +| `TESM_RELEASE_REPO` | der Anwendungsname (`tesm` bzw. `tesm-license`) | + +Der erwartete Anhang heisst `-.tar.gz`. + --- ## 4. HTTPS diff --git a/packages/tesm-core/src/tesm_core/sysops.py b/packages/tesm-core/src/tesm_core/sysops.py index fc41469..7ad5b6b 100644 --- a/packages/tesm-core/src/tesm_core/sysops.py +++ b/packages/tesm-core/src/tesm_core/sysops.py @@ -166,6 +166,13 @@ class SysOps: input=stdin, capture_output=True, text=True, + # Kodierung festnageln. "text=True" allein nimmt die Locale des + # Prozesses -- unter systemd ist die haeufig C/ASCII, und dann + # bricht das Lesen ab, sobald ein Werkzeug einen Umlaut + # ausgibt. Der Fehler entsteht im Leser-Thread von subprocess, + # also weit weg von der Zeile, die ihn ausgeloest hat. + encoding="utf-8", + errors="replace", timeout=timeout or self.timeout, check=False, ) @@ -197,6 +204,8 @@ class SysOps: [binary, *command[1:]], capture_output=True, text=True, + encoding="utf-8", + errors="replace", timeout=timeout, check=False, ) diff --git a/tesm-v2.0.2.tar.gz b/tesm-v2.0.2.tar.gz new file mode 100644 index 0000000..1908551 Binary files /dev/null and b/tesm-v2.0.2.tar.gz differ