#!/bin/sh # # Bootstrap instalace HAVIS III WebServer ze zrcadla get.havis.cz # # Použití (na stanici s přístupem k mirroru): # wget -qO- https://get.havis.cz | sh # poslední STABLE verze # wget -qO- https://get.havis.cz | sh -s -- stable # totéž, explicitně # wget -qO- https://get.havis.cz | sh -s -- rc # poslední RC (předběžná) # wget -qO- https://get.havis.cz | sh -s -- test # VÝPIS test buildů # wget -qO- https://get.havis.cz | sh -s -- 3.7.2026092201 # konkrétní verze # wget -qO- https://get.havis.cz | sh -s -- 3.7.2026092201-rc # wget -qO- https://get.havis.cz | sh -s -- 3.8.0-test-data-online # # Kanály na mirroru: dist/stable/, dist/rc/, dist/test/. U konkrétní verze # se kanál pozná z názvu: bez sufixu = stable, "-rc" = rc, jiný sufix # (název větve) = test. Testovací kanál nemá "poslední" build - vždy se # instaluje konkrétní pojmenovaný build. RC i test se před instalací # potvrzují. # # Skript je čisté POSIX sh (žádné bashismy) - na stanicích běží # různé shelly. Celé tělo je ve funkcích a main se volá až na úplně # posledním řádku, takže useknuté stažení nikdy nespustí půlku skriptu. # # Heslo se čte z /dev/tty (stdin je obsazený rourou z wget) a předává # se wgetu přes dočasný netrc soubor - nikdy není v URL, argumentech # ani ve výpisu procesů. MIRROR_HOST="get.havis.cz" BASE_URL="https://${MIRROR_HOST}/dist" AUTH_USER="havis" MAX_AUTH_ATTEMPTS=3 TMP_DIR="" STTY_SAVED="" PASSWORD="" msg() { printf '%s\n' "$*" } err() { printf 'CHYBA: %s\n' "$*" >&2 } cleanup() { # Obnov echo na terminálu (kdyby skript skončil uprostřed zadávání hesla) if [ -n "$STTY_SAVED" ] && [ -e /dev/tty ]; then stty "$STTY_SAVED" < /dev/tty 2>/dev/null || true fi # Smaž dočasný adresář včetně netrc a stažených souborů if [ -n "$TMP_DIR" ] && [ -d "$TMP_DIR" ]; then rm -rf "$TMP_DIR" fi } require_cmd() { if ! command -v "$1" > /dev/null 2>&1; then err "Na této stanici chybí nástroj '$1'. Bez něj instalace nemůže pokračovat." exit 1 fi } ask_password() { printf 'Zadej heslo pro stažení (uživatel %s): ' "$AUTH_USER" > /dev/tty STTY_SAVED=$(stty -g < /dev/tty) stty -echo < /dev/tty IFS= read -r PASSWORD < /dev/tty stty "$STTY_SAVED" < /dev/tty STTY_SAVED="" printf '\n' > /dev/tty } write_netrc() { # Dočasný netrc s právy 600 - vytvořit prázdný a zamknout DŘÍV, # než se do něj zapíše heslo : > "$TMP_DIR/.netrc" chmod 600 "$TMP_DIR/.netrc" printf 'machine %s\nlogin %s\npassword %s\n' \ "$MIRROR_HOST" "$AUTH_USER" "$PASSWORD" > "$TMP_DIR/.netrc" } # fetch [progress] # wget čte přihlašovací údaje z $HOME/.netrc, proto mu podstrčíme # dočasný HOME - heslo se nedostane do argumentů příkazu. fetch() { if [ "${3:-}" = "progress" ]; then HOME="$TMP_DIR" wget -q --show-progress -O "$2" "$1" else HOME="$TMP_DIR" wget -q -O "$2" "$1" fi } # Kanál podle názvu verze (stejná pravidla jako build skripty): # 3.7.2026092201 -> stable # 3.7.2026092201-rc -> rc (před "-rc" jen číslice a tečky) # cokoliv jiného s "-" -> test (název větve v sufixu) channel_of_version() { case "$1" in *-rc|*-rc[0-9]*) case "${1%-rc*}" in *[!0-9.]*) echo "test" ;; *) echo "rc" ;; esac ;; *-*) echo "test" ;; *) echo "stable" ;; esac } main() { ARG1="${1:-}" ARG2="${2:-}" echo "" msg "=========================================" msg " Instalace HAVIS III WebServer" msg "=========================================" echo "" require_cmd wget require_cmd sha256sum require_cmd mktemp require_cmd stty if [ ! -e /dev/tty ] || [ ! -r /dev/tty ]; then err "Není dostupný terminál pro zadání hesla." err "Spusť skript v normálním terminálu (SSH nebo konzole stanice)." exit 1 fi TMP_DIR=$(mktemp -d) || { err "Nepodařilo se vytvořit dočasný adresář."; exit 1; } trap cleanup EXIT trap 'exit 130' INT trap 'exit 143' TERM # --- Rozbor argumentů: [stable|rc|test] [verze] nebo jen verze --- MODE="install" CHANNEL="" VERSION="" case "$ARG1" in ""|stable) CHANNEL="stable"; VERSION="$ARG2" ;; rc) CHANNEL="rc"; VERSION="$ARG2" ;; test) CHANNEL="test"; VERSION="$ARG2" [ -z "$VERSION" ] && MODE="list" ;; *) VERSION="$ARG1" ;; esac if [ -n "$VERSION" ]; then VERSION_CHANNEL=$(channel_of_version "$VERSION") if [ -n "$CHANNEL" ] && [ "$CHANNEL" != "$VERSION_CHANNEL" ]; then err "Verze '$VERSION' patří do kanálu '$VERSION_CHANNEL', ne '$CHANNEL'." err "Zadej jen verzi (kanál se pozná z názvu), nebo kanál bez verze." exit 1 fi CHANNEL="$VERSION_CHANNEL" fi DIST_URL="$BASE_URL/$CHANNEL" if [ "$MODE" = "list" ]; then FIRST_FILE="builds.txt" RUN_NAME="" msg "Výpis dostupných TESTOVACÍCH buildů (kanál test)." elif [ -n "$VERSION" ]; then RUN_NAME="havisIII_WebServer-${VERSION}.run" FIRST_FILE="$RUN_NAME.sha256" msg "Požadovaná verze: $VERSION (kanál $CHANNEL)" elif [ "$CHANNEL" = "rc" ]; then RUN_NAME="latest.run" FIRST_FILE="$RUN_NAME.sha256" msg "Bude nainstalována poslední PŘEDBĚŽNÁ verze (kanál rc)." else RUN_NAME="latest.run" FIRST_FILE="$RUN_NAME.sha256" msg "Bude nainstalována poslední STABILNÍ verze (kanál stable)." fi echo "" # --- Přihlášení + stažení checksumu (malý soubor = rychlé ověření hesla) --- attempt=1 while :; do ask_password write_netrc msg "Ověřuji přihlášení a dostupnost balíčku..." rc=0 fetch "$DIST_URL/$FIRST_FILE" "$TMP_DIR/$FIRST_FILE" || rc=$? if [ "$rc" -eq 0 ]; then break fi if [ "$rc" -eq 6 ]; then # wget exit kód 6 = chyba přihlášení (HTTP 401) if [ "$attempt" -ge "$MAX_AUTH_ATTEMPTS" ]; then echo "" err "Třikrát zadané špatné heslo - instalace ukončena." err "Ověř si heslo a spusť instalaci znovu. Pokud heslo neznáš, ozvi se správci." exit 1 fi attempt=$((attempt + 1)) msg "Špatné heslo, zkus to prosím znovu (pokus $attempt z $MAX_AUTH_ATTEMPTS)." echo "" elif [ "$rc" -eq 8 ]; then # HTTP chyba jiná než 401 - typicky 404 (přihlášení už prošlo). # Kanál je daný názvem verze, jinde se nehledá. echo "" if [ "$MODE" = "list" ]; then err "V testovacím kanálu zatím není žádný build. Ozvi se správci." elif [ -n "$VERSION" ]; then err "Balíček pro verzi '$VERSION' v kanálu '$CHANNEL' na serveru není." if [ "$CHANNEL" = "test" ]; then err "Dostupné test buildy vypíše: wget -qO- https://$MIRROR_HOST | sh -s -- test" else err "Zkontroluj číslo verze, nebo spusť instalaci bez verze (nainstaluje se poslední $CHANNEL)." fi elif [ "$CHANNEL" = "rc" ]; then err "V kanálu rc zatím není žádná předběžná verze. Ozvi se správci." else err "Na serveru zatím není žádný balíček k instalaci. Ozvi se správci." fi exit 1 else echo "" err "Nepodařilo se připojit k $MIRROR_HOST (wget exit kód $rc)." err "Zkontroluj, že je stanice připojená k síti s přístupem na mirror." exit 1 fi done msg "Přihlášení v pořádku." echo "" # --- Výpis test buildů (bez instalace) --- if [ "$MODE" = "list" ]; then msg "Dostupné testovací buildy:" echo "" cat "$TMP_DIR/$FIRST_FILE" echo "" msg "Instalace konkrétního buildu:" msg " wget -qO- https://$MIRROR_HOST | sh -s -- " exit 0 fi # Skutečný název balíčku je uvnitř checksumu (latest.run je symlink) REAL_NAME=$(awk '{print $2; exit}' "$TMP_DIR/$RUN_NAME.sha256" | sed 's/^\*//') if [ -n "$REAL_NAME" ]; then msg "Instalovaný balíček: $REAL_NAME" fi # --- Potvrzení RC a testovacího kanálu --- if [ "$CHANNEL" = "test" ]; then echo "" msg "POZOR: Jedná se o TESTOVACÍ build - nepatří na produkční stanice!" printf 'Opravdu pokračovat v instalaci testovací verze? [y/N]: ' > /dev/tty IFS= read -r ANSWER < /dev/tty case "$ANSWER" in [yYaA]) ;; *) msg "Instalace zrušena." exit 0 ;; esac elif [ "$CHANNEL" = "rc" ]; then echo "" msg "Jedná se o PŘEDBĚŽNOU verzi (RC) - určená pro pilotní stanice, ne pro plošné nasazení." printf 'Pokračovat v instalaci předběžné verze? [y/N]: ' > /dev/tty IFS= read -r ANSWER < /dev/tty case "$ANSWER" in [yYaA]) ;; *) msg "Instalace zrušena." exit 0 ;; esac fi # --- Stažení balíčku --- msg "Stahuji balíček (může to chvíli trvat)..." if ! fetch "$DIST_URL/$RUN_NAME" "$TMP_DIR/$RUN_NAME" progress; then echo "" err "Stažení balíčku selhalo. Zkus instalaci spustit znovu." exit 1 fi msg "Balíček stažen." echo "" # --- Ověření kontrolního součtu --- msg "Ověřuji kontrolní součet..." EXPECTED=$(awk '{print $1; exit}' "$TMP_DIR/$RUN_NAME.sha256") ACTUAL=$(sha256sum "$TMP_DIR/$RUN_NAME" | awk '{print $1}') if [ -z "$EXPECTED" ] || [ "$EXPECTED" != "$ACTUAL" ]; then err "Kontrolní součet staženého balíčku nesouhlasí!" err "Soubor je poškozený nebo neúplný - spusť instalaci znovu." err "Pokud problém přetrvává, ozvi se správci." exit 1 fi msg "Kontrolní součet v pořádku." echo "" # --- Spuštění instalace --- msg "Spouštím instalaci (další kroky si řekne instalátor sám)..." echo "" # stdin přesměrovat na terminál - roura z wget už je vyčerpaná # a instalátor se ptá na potvrzení a sudo heslo. # Makeself vrací exit kód vnitřního deploy skriptu - MUSÍ se # kontrolovat, jinak by se po chybě vypsalo "Instalace dokončena". rc=0 sh "$TMP_DIR/$RUN_NAME" < /dev/tty || rc=$? if [ "$rc" -ne 0 ]; then echo "" err "=========================================" err " INSTALACE SELHALA (exit kód $rc)" err "=========================================" err "Nová verze NEBYLA aktivována - na stanici běží původní verze." err "Projdi chybové hlášky výše a po opravě spusť instalaci znovu." exit "$rc" fi echo "" msg "=========================================" msg " Instalace dokončena" msg "=========================================" } main "$@"