#!/usr/bin/env bash # ============================================================================= # Installation -- der kurze Weg. # # Diese Datei liegt oben im Paket und ruft den Installer unter deploy/ auf: # # tar xzf tesm-vX.Y.Z.tar.gz && cd tesm # sudo bash install.sh --app tesm --https # # Sie genuegt aber auch **allein**: liegt kein Paket um sie herum, holt sie # sich das aktuelle Release selbst. # # curl -fsSLO https://gitea.int.eertmoed.net/alientim/tesm/raw/branch/main/install.sh # sudo bash install.sh --app tesm --https # # Die Master-Ausgabe des Lizenzservers liegt im privaten Repositorium # tesm-license-master. Allein-Betrieb dafuer: # # export TESM_RELEASE_REPO=tesm-license-master # export TESM_RELEASE_TOKEN= # sudo -E bash install.sh --app tesm-license --https # # Aus einem entpackten Paket braucht es das nicht: dort entscheidet, welches # Paket es ist. Die Master-Ausgabe legt beim Installieren Signaturschluessel # und eigene Master-Lizenz selbst an. # # Alle Argumente gehen unveraendert an deploy/install.sh weiter; dessen Hilfe: # # bash install.sh --help # ============================================================================= set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # --app wird nur gelesen, nicht verbraucht: der Bootstrap braucht es fuer den # Namen des Repositoriums, der Installer bekommt die Argumente unveraendert. APP="" TAG="latest" ARGUMENTE=("$@") for ((i = 0; i < $#; i++)); do case "${ARGUMENTE[i]}" in --app) APP="${ARGUMENTE[i + 1]:-}" ;; --tag) TAG="${ARGUMENTE[i + 1]:-latest}" ;; esac done # Ohne Paket drumherum brauchen wir den Namen, um das richtige Release zu # finden -- die beiden Anwendungen liegen in getrennten Repositorien. if [[ ! -f "$SCRIPT_DIR/deploy/install.sh" ]]; then [[ "$APP" == "tesm" || "$APP" == "tesm-license" ]] || { echo "Diese Datei liegt allein hier und muss sich das Release holen --" >&2 echo "dafuer braucht sie --app ." >&2 echo "Verwendung: install.sh --app [weitere Optionen]" >&2 exit 2 } fi # --8<-- Bootstrap: Release holen -------------------------------------------- # Wortgleich in update.sh und upgrade.sh; ein Hygienetest vergleicht die beiden # Bloecke. Zwei eigenstaendige Skripte koennen sich keine Datei teilen, und # beide sollen als **einzelne Datei** genuegen: auf das Zielsystem legen, # ausfuehren, fertig -- auf einer 1.2.8 liegt nichts von diesem Projekt. # # Erwartet: $APP und $TAG, wahlweise $REPO (Vorgabe: $APP). Setzt: $TAG # (aufgeloest) und $SRC (entpacktes Release). Raeumt sein Arbeitsverzeichnis # beim Beenden auf. # # $REPO gibt es, weil Installationsname und Repositorium auseinanderfallen # koennen: die Master-Ausgabe des Lizenzservers liegt in # "tesm-license-master", die Installation heisst weiter "tesm-license". # # Zwei Fallen des Vorgaengers sind hier vermieden: # * Der Tag "latest" wird ueber die API aufgeloest, nie direkt in die # Download-URL geschrieben. Gitea liefert sonst mit HTTP 200 ein # automatisch erzeugtes Quell-Snapshot statt des echten Release-Anhangs. # * Nach dem Entpacken wird geprueft, dass ein install.sh existiert. BASE="${TESM_RELEASE_BASE:-https://gitea.int.eertmoed.net}" OWNER="${TESM_RELEASE_OWNER:-alientim}" # Ein privates Repositorium -- tesm-license-master -- gibt sein Release nur mit # Zugriffstoken heraus. Reihenfolge: Umgebungsvariable, dann eine Datei, die # der Betreiber selbst anlegt. Der Installer legt sie nie an: ob ein Token auf # dem Server liegt, ist keine Entscheidung eines Skripts. TOKEN="${TESM_RELEASE_TOKEN:-}" if [[ -z "$TOKEN" && -r /etc/tesm-release.token ]]; then TOKEN="$(tr -d '[:space:]' < /etc/tesm-release.token)" fi # Ein Wagenruecklauf im Paket macht jedes Shell-Skript auf dem Ziel unbrauchbar # ("bad interpreter"). Die Pruefung darf dabei nicht selbst von einem # Zusatzpaket abhaengen: mit "file" -- das auf einem schlanken Server fehlt -- # endete sie in "command not found", grep bekam nichts zu sehen, und das Skript # machte weiter, als waere alles in Ordnung. hat_crlf() { local cr cr="$(printf '\r')" # -U (binaer lesen) ist unter Linux ohne Wirkung, auf einer # Windows-Buildmaschine aber entscheidend: dort entfernt grep den # Wagenruecklauf beim Lesen und findet deshalb nie einen. LC_ALL=C grep -qU "$cr" "$1" } BOOTSTRAP_WORK="" bootstrap_aufraeumen() { # Den Ausstiegscode zuerst festhalten: alles danach ueberschreibt ihn. local code=$? [[ -n "$BOOTSTRAP_WORK" ]] && rm -rf "$BOOTSTRAP_WORK" # Wer einen eigenen Abschluss braucht -- etwa einen Rueckweg nach einem # Fehlschlag --, definiert "beim_beenden". Ein zweiter "trap ... EXIT" waere # der falsche Weg: er wuerde diesen hier ersetzen, und das entpackte Release # bliebe unter /tmp liegen. declare -F beim_beenden >/dev/null 2>&1 && beim_beenden "$code" return "$code" } trap bootstrap_aufraeumen EXIT release_holen() { local repo="${TESM_RELEASE_REPO:-${REPO:-$APP}}" asset # Leeres Array und "set -u" vertragen sich nur mit dieser Schreibweise. local -a kopf=() [[ -n "$TOKEN" ]] && kopf=(-H "Authorization: token $TOKEN") for werkzeug in curl tar python3 find; do command -v "$werkzeug" >/dev/null 2>&1 || { echo "Auf diesem System fehlt $werkzeug -- ohne das geht es nicht." >&2 return 1 } done BOOTSTRAP_WORK="$(mktemp -d /tmp/tesm-release-XXXXXX)" if [[ "$TAG" == "latest" ]]; then echo "==> Ermittle aktuellen Release-Tag ..." TAG="$(curl -fsS ${kopf[0]+"${kopf[@]}"} "$BASE/api/v1/repos/$OWNER/$repo/releases/latest" \ | python3 -c 'import json,sys; print(json.load(sys.stdin)["tag_name"])')" || { echo "Kein Release gefunden unter $BASE/$OWNER/$repo." >&2 [[ -z "$TOKEN" ]] && echo "Ist das Repositorium privat? Dann TESM_RELEASE_TOKEN setzen." >&2 echo "Andere Quelle: TESM_RELEASE_BASE, TESM_RELEASE_OWNER, TESM_RELEASE_REPO." >&2 return 1 } echo " $TAG" fi asset="$repo-$TAG.tar.gz" echo "==> Lade $asset ..." curl -fsSL ${kopf[0]+"${kopf[@]}"} -o "$BOOTSTRAP_WORK/$asset" \ "$BASE/$OWNER/$repo/releases/download/$TAG/$asset" || { echo "Download fehlgeschlagen: $BASE/$OWNER/$repo/releases/download/$TAG/$asset" >&2 return 1 } echo "==> Entpacke ..." tar -xzf "$BOOTSTRAP_WORK/$asset" -C "$BOOTSTRAP_WORK" # Drei Ebenen, nicht zwei: release.sh packt mit dem Praefix "/", also # liegt das Skript unter /deploy/install.sh. Mit "-maxdepth 2" fand die # Suche nie etwas -- und die Fehlermeldung schob es auf den Download. SRC="$(find "$BOOTSTRAP_WORK" -maxdepth 3 -name install.sh -path '*/deploy/*' \ -printf '%h/..\n' | head -1)" [[ -n "$SRC" ]] || { echo "Im Paket fehlt deploy/install.sh -- Download unbrauchbar." >&2 return 1 } SRC="$(cd "$SRC" && pwd)" echo "==> Pruefe auf CRLF (Windows-Buildmaschinen) ..." if hat_crlf "$SRC/deploy/install.sh"; then echo "Das Paket enthaelt CRLF-Zeilenenden. Beim Paketieren fehlte" >&2 echo "'git -c core.autocrlf=false archive'." >&2 return 1 fi } # --8<-- Ende Bootstrap ------------------------------------------------------ if [[ -f "$SCRIPT_DIR/deploy/install.sh" ]]; then # Im Paket: der Installer liegt daneben. exec bash "$SCRIPT_DIR/deploy/install.sh" ${ARGUMENTE[0]+"${ARGUMENTE[@]}"} fi # Allein: Release holen und dessen Installer benutzen. Kein exec -- sonst # laeuft die Aufraeumfalle nie und das entpackte Release bliebe unter /tmp. release_holen || { echo "Das Release liess sich nicht holen." >&2; exit 1; } bash "$SRC/deploy/install.sh" ${ARGUMENTE[0]+"${ARGUMENTE[@]}"}