From eaa9c12ebc30b34c7dd93f4f8b2597d500d775ee Mon Sep 17 00:00:00 2001 From: gitea-actions Date: Sat, 22 Aug 2026 19:12:57 +0200 Subject: [PATCH 1/4] =?UTF-8?q?Nasazen=C3=AD=20na=20server:=20Proxmox=20LX?= =?UTF-8?q?C=20a=20Docker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aby se to dalo nechat běžet pořád, ne jen spouštět večer na notebooku. pve-kostky.sh se pouští na uzlu Proxmoxu, založí kontejner a předá řízení lxc-install.sh, který uvnitř stáhne vydanou binárku, ověří kontrolní součet a zapíše službu. Ty dva jsou oddělené schválně: instalátor o Proxmoxu nic neví, takže se dá pustit i v kontejneru, který sis založil sám. Podoba je odkoukaná od community-scripts.org, ale nic z jejich frameworku se nestahuje — jejich build.func si instalační skript hledá natvrdo ve vlastním repozitáři, takže mimo něj nefunguje. Docker se staví ze zdrojáků, ne z binárky: image pak jde sestavit pro obě architektury a nezávisí na tom, jestli release proběhl. Zamčené závislosti z minula dělají sestavení opakovatelné. Compose používá síť hostitele, a to je podstatné. V bridge režimu vidí kontejner uvnitř adresu 172.17.0.2, tabule ji poctivě nabídne a udělá na ni QR kód — jenže z telefonu je nedosažitelná. Ověřeno, ne odhadnuto; kdo bridge potřebuje, musí nastavit DICE_HOST. Aplikace sama nepotřebovala změnit nic. DICE_DB, DICE_PORT a DICE_HOST pokrývají obojí a v LXC má kontejner vlastní adresu v LAN, takže hledání adres i QR kód fungují beze změny. --- .dockerignore | 20 ++++++ README.md | 60 +++++++++++++++++ deploy/Dockerfile | 47 +++++++++++++ deploy/compose.yaml | 45 +++++++++++++ deploy/lxc-install.sh | 124 ++++++++++++++++++++++++++++++++++ deploy/pve-kostky.sh | 153 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 449 insertions(+) create mode 100644 .dockerignore create mode 100644 deploy/Dockerfile create mode 100644 deploy/compose.yaml create mode 100755 deploy/lxc-install.sh create mode 100755 deploy/pve-kostky.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4177d60 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +# Do image patří jen to, co appka za běhu potřebuje. +.git/ +.github/ +.gitea/ +.venv/ +.build/ +.pytest_cache/ +__pycache__/ +dist/ +tests/ +deploy/ +*.db +*.spec +build.sh +dice.py +verze.txt +README.md +requirements*.in +requirements-dev.txt +requirements-build.txt diff --git a/README.md b/README.md index a9b4069..c96a1c6 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,65 @@ hlášku vysypal varování, že takhle se to nemá, a měl by pravdu. Počítá se s domácí sítí — appka nemá přihlašování a kdokoli na stejné WiFi může zapisovat. +## Nasazení na server + +Kdo nechce každý večer spouštět binárku na notebooku, může to nechat běžet +pořád. Obojí níž počítá **jen s domácí sítí** — appka nemá přihlašování +a ani jedno nasazení ji nijak nezabezpečuje. Do internetu to nepatří. + +### Proxmox (LXC) + +Skript se pouští v terminálu uzlu Proxmoxu jako root. Založí kontejner, +nastaví ho, stáhne poslední vydanou binárku, ověří kontrolní součet a zapne +službu: + +```bash +bash deploy/pve-kostky.sh +``` + +Na konci vypíše adresu, na které počitadlo poslouchá. Výchozí nastavení je +1 jádro, 512 MB, 4 GB disku a Debian 13; přebíjí se proměnnými: + +```bash +MEMORY=1024 STORAGE=local-zfs PORT=8080 bash deploy/pve-kostky.sh +``` + +Aktualizace na novější vydání je totéž s číslem kontejneru: + +```bash +bash deploy/pve-kostky.sh update 123 +``` + +Podoba je odkoukaná od [community-scripts.org](https://community-scripts.org), +ale nic z jejich frameworku se nestahuje — jejich `build.func` si instalační +skript hledá natvrdo ve vlastním repozitáři, takže mimo něj nefunguje. + +`deploy/lxc-install.sh` o Proxmoxu nic neví, takže se dá pustit i v LXC nebo +virtuálu, který sis založil sám. Je idempotentní; druhé spuštění jen vymění +binárku za nejnovější. + +### Docker + +```bash +cd deploy && docker compose up -d +``` + +Staví se ze zdrojáků, takže image jde sestavit pro amd64 i arm64 a nezávisí +na tom, jestli release pro danou architekturu proběhl. Databáze leží +v pojmenovaném svazku, appka běží pod nerootovým uživatelem. + +**Pozor na síť.** Compose schválně používá `network_mode: host`. V bridge +režimu kontejner uvnitř vidí adresu jako `172.17.0.2`, tabule ji poctivě +nabídne a vygeneruje na ni QR kód — jenže z telefonu je nedosažitelná. +Kdo bridge potřebuje, musí nastavit `DICE_HOST` na adresu hostitele v LAN. + +| | LXC | Docker | +|---|---|---| +| Odkud | vydaná binárka z releasu | zdrojáky | +| Databáze | `/var/lib/kostky/dice.db` | svazek `kostky-data` | +| Aktualizace | `pve-kostky.sh update ` | `docker compose build --pull` | +| Adresa a QR | funguje samo | potřeba síť hostitele | + ## Když se telefon nepřipojí Adresy se hledají při každém načtení tabule, takže přechod z domácí WiFi na @@ -154,6 +213,7 @@ při remíze na prvním místě se hraje dál. | `build.sh`, `kostky.spec` | stavba binárky | | `.gitea/workflows/` | testy a linuxová binárka doma | | `.github/workflows/` | binárky pro Windows a macOS | +| `deploy/` | nasazení na server: Proxmox LXC a Docker | Terminálová verze zůstává funkční jako záloha: diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..4342e00 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,47 @@ +# Počitadlo kostek jako kontejner. +# +# Staví se ze zdrojáků, ne z vydané binárky: image jde pak sestavit pro +# amd64 i arm64 a nezávisí na tom, jestli pro danou architekturu release +# proběhl. Závislosti jsou zamčené v requirements.txt, takže je sestavení +# opakovatelné. +# +# docker build -f deploy/Dockerfile -t kostky . +# +# Sestavuje se z kořene repozitáře, ne z deploy/. +FROM python:3.11-slim + +# Verze se jinak nemá kde vzít — v image není git ani zabalená binárka. +ARG VERSION=neznámá + +RUN useradd --system --uid 1000 --home-dir /app --shell /usr/sbin/nologin kostky + +WORKDIR /app + +# Závislosti zvlášť a dřív než kód, ať se vrstva s pipem přestavuje jen tehdy, +# když se opravdu změnily. +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY console.py core.py net.py stats.py storage.py version.py web.py ./ +COPY templates/ templates/ +COPY static/ static/ +RUN echo "$VERSION" > verze.txt + +# Adresář pro databázi musí vzniknout už tady a patřit správnému uživateli — +# svazek, který Docker založí sám, by jinak patřil rootovi a appka běžící pod +# kostky by do něj nezapsala. +RUN install -d -o kostky -g kostky /data +VOLUME ["/data"] + +ENV DICE_DB=/data/dice.db \ + DICE_PORT=8000 + +EXPOSE 8000 +USER kostky + +# Bez curlu, ten ve slim image není. Stačí, že stránka odpoví — sahá i do +# databáze, takže se tím kontroluje i ta. +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD python -c "import os,urllib.request; urllib.request.urlopen('http://127.0.0.1:' + os.environ.get('DICE_PORT','8000') + '/')" + +CMD ["python", "web.py"] diff --git a/deploy/compose.yaml b/deploy/compose.yaml new file mode 100644 index 0000000..afffe17 --- /dev/null +++ b/deploy/compose.yaml @@ -0,0 +1,45 @@ +# Počitadlo kostek v Dockeru. +# +# cd deploy && docker compose up -d +# +# Počítá se s domácí sítí. Appka nemá přihlašování, tohle ji nijak +# nezabezpečuje a do internetu nepatří. + +services: + kostky: + build: + context: .. + dockerfile: deploy/Dockerfile + args: + VERSION: ${VERSION:-vlastní sestavení} + image: kostky:latest + container_name: kostky + restart: unless-stopped + + # Python jako PID 1 nesklízí osiřelé procesy a hůř reaguje na signály. + init: true + + # Síť hostitele schválně. V bridge režimu kontejner uvnitř vidí adresu + # jako 172.17.0.2, tabule ji poctivě nabídne a vygeneruje na ni QR kód — + # jenže z telefonu je nedosažitelná. Se sítí hostitele appka najde tu + # adresu, na kterou se telefon opravdu dovolá. + # + # Kdo bridge potřebuje, ať zakomentuje řádek níž, odkomentuje ports + # a nastaví DICE_HOST na adresu hostitele v LAN: + # + # ports: + # - "8000:8000" + # environment: + # DICE_HOST: 192.168.1.10 + network_mode: host + + environment: + DICE_PORT: 8000 + DICE_DB: /data/dice.db + + volumes: + - kostky-data:/data + +volumes: + # Zálohu si stáhneš rovnou z appky přes /export, takže se sem nemusí lézt. + kostky-data: diff --git a/deploy/lxc-install.sh b/deploy/lxc-install.sh new file mode 100755 index 0000000..fbdc1c5 --- /dev/null +++ b/deploy/lxc-install.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# Instalace počitadla uvnitř kontejneru. Pouští se přes pve-kostky.sh, ne ručně. +# +# Oddělené od skriptu pro Proxmox schválně: tenhle běží uvnitř Debianu a nic +# o Proxmoxu neví, takže se dá pustit i v LXC, který sis založil sám, nebo ve +# virtuálu. Je idempotentní — po druhém spuštění máš nejnovější binárku. +set -euo pipefail + +GITEA="${GITEA:-https://gitea.spacilovi.eu}" +REPO="${REPO:-david-spacil/dice-counter}" +PORT="${PORT:-8000}" +VERSION="${VERSION:-}" # prázdné = poslední release + +USER_NAME=kostky +HOME_DIR=/opt/kostky +DATA_DIR=/var/lib/kostky + +msg() { echo -e "\e[1;34m →\e[0m $*"; } +ok() { echo -e "\e[1;32m ✓\e[0m $*"; } +die() { echo -e "\e[1;31m ✗\e[0m $*" >&2; exit 1; } + +# --- co vlastně stahovat ----------------------------------------------------- + +case "$(dpkg --print-architecture)" in + amd64) ASSET=kostky-linux-x86_64 ;; + arm64) ASSET=kostky-linux-arm64 ;; + *) die "Pro architekturu $(dpkg --print-architecture) binárka není." ;; +esac + +msg "Doinstalovávám, co je potřeba ke stažení" +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq curl ca-certificates >/dev/null +ok "Základ je hotový" + +if [ -z "$VERSION" ]; then + msg "Zjišťuji poslední vydanou verzi" + # Jen tag; adresa přílohy se z něj složí, takže tu nepotřebujeme jq. + VERSION=$(curl -fsSL "$GITEA/api/v1/repos/$REPO/releases/latest" \ + | grep -o '"tag_name":[[:space:]]*"[^"]*"' | head -1 | cut -d'"' -f4) + [ -n "$VERSION" ] || die "Nepodařilo se zjistit poslední verzi z $GITEA." +fi +ok "Instaluje se $VERSION ($ASSET)" + +# --- stažení a ověření ------------------------------------------------------- + +BASE="$GITEA/$REPO/releases/download/$VERSION" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +msg "Stahuji binárku" +curl -fsSL -o "$TMP/$ASSET" "$BASE/$ASSET" \ + || die "$ASSET pro $VERSION na releasu není. Vydání pro tuhle architekturu možná ještě neproběhlo." +curl -fsSL -o "$TMP/$ASSET.sha256" "$BASE/$ASSET.sha256" \ + || die "Chybí kontrolní součet — radši nic neinstaluju." + +msg "Ověřuji kontrolní součet" +( cd "$TMP" && sha256sum -c "$ASSET.sha256" >/dev/null ) \ + || die "Kontrolní součet nesedí. Stažený soubor zahazuju." +ok "Součet sedí" + +# --- uživatel, soubory, služba ---------------------------------------------- + +id -u "$USER_NAME" >/dev/null 2>&1 \ + || useradd --system --home-dir "$HOME_DIR" --shell /usr/sbin/nologin "$USER_NAME" + +install -d -o root -g root -m 755 "$HOME_DIR" + +# Služba se zastaví, jen když už běží — při první instalaci ještě neexistuje. +systemctl is-active --quiet kostky && systemctl stop kostky +install -o root -g root -m 755 "$TMP/$ASSET" "$HOME_DIR/kostky" +echo "$VERSION" > "$HOME_DIR/verze" +ok "Binárka je na místě" + +msg "Zapisuji službu" +cat > /etc/systemd/system/kostky.service <&2; exit 1; } + +header() { + echo + echo -e "\e[1;36m $APP\e[0m — počitadlo kostek do LXC" + echo -e " \e[2m$REPO_URL\e[0m" + echo +} + +# --- kontroly, než se něco založí ------------------------------------------- + +command -v pct >/dev/null || die "Tohle patří na uzel Proxmoxu — pct tu není." +[ "$(id -u)" -eq 0 ] || die "Spusť to jako root." +[ -f "$INSTALLER" ] || die "Vedle skriptu chybí lxc-install.sh." + +# --- instalace do kontejneru ------------------------------------------------- + +install_into() { + local ctid="$1" + + msg "Posílám instalátor do kontejneru $ctid" + pct push "$ctid" "$INSTALLER" /root/lxc-install.sh --perms 0755 + + pct exec "$ctid" -- env PORT="$PORT" bash /root/lxc-install.sh +} + +address_of() { + local ctid="$1" + pct exec "$ctid" -- hostname -I 2>/dev/null | awk '{print $1}' +} + +hotovo() { + local ctid="$1" ip jmeno + ip=$(address_of "$ctid") + jmeno=$(pct config "$ctid" | awk -F': ' '/^hostname:/ {print $2}') + + echo + ok "Hotovo. Kontejner $ctid ($jmeno) běží." + echo + echo -e " Zápisník na notebook: \e[1;32mhttp://${ip}:${PORT}/board\e[0m" + echo -e " Zadávání z telefonu: \e[1;32mhttp://${ip}:${PORT}/\e[0m" + echo + echo -e " \e[2mDatabáze: /var/lib/kostky/dice.db (uvnitř kontejneru)" + echo -e " Log: pct exec $ctid -- journalctl -u kostky -f" + echo -e " Konzole: pct enter $ctid\e[0m" + echo +} + +# --- aktualizace ------------------------------------------------------------- + +if [ "${1:-}" = "update" ]; then + ctid="${2:-}" + [ -n "$ctid" ] || die "Řekni který: bash pve-kostky.sh update " + pct status "$ctid" >/dev/null 2>&1 || die "Kontejner $ctid neexistuje." + + header + [ "$(pct status "$ctid")" = "status: running" ] || { + msg "Startuji kontejner" + pct start "$ctid" + sleep 5 + } + + install_into "$ctid" + hotovo "$ctid" + exit 0 +fi + +# --- nový kontejner ---------------------------------------------------------- + +header + +CTID="${CTID:-$(pvesh get /cluster/nextid)}" +msg "Kontejner dostane číslo $CTID" + +msg "Hledám šablonu Debianu $OSVERSION" +pveam update >/dev/null 2>&1 || true +TEMPLATE=$(pveam available --section system \ + | awk '{print $2}' | grep "^debian-${OSVERSION}-standard" | sort -V | tail -1) +[ -n "$TEMPLATE" ] || die "Šablona pro Debian $OSVERSION není k dispozici." + +if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg "Stahuji $TEMPLATE" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null +fi +ok "Šablona $TEMPLATE" + +msg "Zakládám kontejner" +pct create "$CTID" "$TEMPLATE_STORAGE:vztmpl/$TEMPLATE" \ + --hostname "$CT_HOSTNAME" \ + --cores "$CORES" \ + --memory "$MEMORY" \ + --swap 256 \ + --rootfs "$STORAGE:$DISK" \ + --net0 "name=eth0,bridge=$BRIDGE,ip=dhcp" \ + --unprivileged "$UNPRIVILEGED" \ + --ostype debian \ + --onboot 1 \ + --description "$APP — $REPO_URL" >/dev/null +ok "Kontejner $CTID založen" + +msg "Startuji" +pct start "$CTID" + +msg "Čekám na síť" +for _ in $(seq 60); do + [ -n "$(address_of "$CTID")" ] \ + && pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1 \ + && break + sleep 2 +done +[ -n "$(address_of "$CTID")" ] || die "Kontejner nedostal adresu. Sedí bridge $BRIDGE?" +ok "Adresa $(address_of "$CTID")" + +install_into "$CTID" +hotovo "$CTID" From 746916a755c9adfff19f3a23e424386a76ba5a7d Mon Sep 17 00:00:00 2001 From: gitea-actions Date: Sat, 22 Aug 2026 19:22:53 +0200 Subject: [PATCH 2/4] =?UTF-8?q?Jeden=20p=C5=99=C3=ADkaz=20m=C3=ADsto=20dvo?= =?UTF-8?q?u=20sta=C5=BEen=C3=BDch=20soubor=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instalace teď vypadá tak, jak se u community-scripts čeká: bash -c "$(curl -fsSL .../deploy/pve-kostky.sh)" Dva soubory to neuměly. Přes rouru z curlu není na disku hostitele žádný soubor, který by šel poslat do kontejneru, takže se instalátor přesunul dovnitř skriptu jako text v heredocu. Duplicita tím nevzniká — vypsat se dá zpátky přes `bash pve-kostky.sh instalator`, když ho chce někdo pustit v kontejneru, který si založil sám. Argumenty se rourou předávají mizerně, tak jde všechno i proměnnou: CTID=123 MODE=update. Ze staženého souboru funguje i pozičně. Ke stejné příležitosti: úložiště se přestalo hádat. local-lvm na ZFS instalacích neexistuje a pct create by spadlo až po půlce práce; teď se vybere první aktivní, a když se nějaké vnutí, ověří se, že vůbec existuje. --- README.md | 36 +++++-- deploy/lxc-install.sh | 124 ----------------------- deploy/pve-kostky.sh | 231 +++++++++++++++++++++++++++++++++++------- 3 files changed, 224 insertions(+), 167 deletions(-) delete mode 100755 deploy/lxc-install.sh diff --git a/README.md b/README.md index c96a1c6..e9afc01 100644 --- a/README.md +++ b/README.md @@ -124,34 +124,52 @@ a ani jedno nasazení ji nijak nezabezpečuje. Do internetu to nepatří. ### Proxmox (LXC) -Skript se pouští v terminálu uzlu Proxmoxu jako root. Založí kontejner, +Jeden příkaz v terminálu uzlu Proxmoxu, jako root. Založí kontejner, nastaví ho, stáhne poslední vydanou binárku, ověří kontrolní součet a zapne službu: ```bash -bash deploy/pve-kostky.sh +bash -c "$(curl -fsSL https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/pve-kostky.sh)" ``` Na konci vypíše adresu, na které počitadlo poslouchá. Výchozí nastavení je -1 jádro, 512 MB, 4 GB disku a Debian 13; přebíjí se proměnnými: +1 jádro, 512 MB, 4 GB disku a Debian 13; úložiště si najde samo. Přebíjí se +proměnnými před příkazem: ```bash -MEMORY=1024 STORAGE=local-zfs PORT=8080 bash deploy/pve-kostky.sh +MEMORY=1024 STORAGE=local-zfs PORT=8080 bash -c "$(curl -fsSL ...)" ``` -Aktualizace na novější vydání je totéž s číslem kontejneru: +| Proměnná | Výchozí | +|---|---| +| `CTID` | první volné číslo | +| `CT_HOSTNAME` | `kostky` | +| `CORES`, `MEMORY`, `DISK` | `1`, `512`, `4` | +| `STORAGE` | první aktivní úložiště pro kontejnery | +| `TEMPLATE_STORAGE`, `BRIDGE` | `local`, `vmbr0` | +| `PORT` | `8000` | +| `UNPRIVILEGED`, `OSVERSION` | `1`, `13` | +| `VERSION` | poslední vydání | + +Aktualizace na novější vydání je ten samý příkaz s číslem kontejneru: ```bash -bash deploy/pve-kostky.sh update 123 +CTID=123 MODE=update bash -c "$(curl -fsSL ...)" ``` Podoba je odkoukaná od [community-scripts.org](https://community-scripts.org), ale nic z jejich frameworku se nestahuje — jejich `build.func` si instalační skript hledá natvrdo ve vlastním repozitáři, takže mimo něj nefunguje. -`deploy/lxc-install.sh` o Proxmoxu nic neví, takže se dá pustit i v LXC nebo -virtuálu, který sis založil sám. Je idempotentní; druhé spuštění jen vymění -binárku za nejnovější. +Instalátor, který běží uvnitř kontejneru, je ve skriptu vložený jako text — +přes rouru z curlu na disku hostitele žádný druhý soubor není. O Proxmoxu nic +neví, takže se dá použít i v kontejneru, který sis založil sám: + +```bash +bash pve-kostky.sh instalator > install.sh +``` + +Je idempotentní; druhé spuštění jen vymění binárku za nejnovější. ### Docker diff --git a/deploy/lxc-install.sh b/deploy/lxc-install.sh deleted file mode 100755 index fbdc1c5..0000000 --- a/deploy/lxc-install.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bash -# Instalace počitadla uvnitř kontejneru. Pouští se přes pve-kostky.sh, ne ručně. -# -# Oddělené od skriptu pro Proxmox schválně: tenhle běží uvnitř Debianu a nic -# o Proxmoxu neví, takže se dá pustit i v LXC, který sis založil sám, nebo ve -# virtuálu. Je idempotentní — po druhém spuštění máš nejnovější binárku. -set -euo pipefail - -GITEA="${GITEA:-https://gitea.spacilovi.eu}" -REPO="${REPO:-david-spacil/dice-counter}" -PORT="${PORT:-8000}" -VERSION="${VERSION:-}" # prázdné = poslední release - -USER_NAME=kostky -HOME_DIR=/opt/kostky -DATA_DIR=/var/lib/kostky - -msg() { echo -e "\e[1;34m →\e[0m $*"; } -ok() { echo -e "\e[1;32m ✓\e[0m $*"; } -die() { echo -e "\e[1;31m ✗\e[0m $*" >&2; exit 1; } - -# --- co vlastně stahovat ----------------------------------------------------- - -case "$(dpkg --print-architecture)" in - amd64) ASSET=kostky-linux-x86_64 ;; - arm64) ASSET=kostky-linux-arm64 ;; - *) die "Pro architekturu $(dpkg --print-architecture) binárka není." ;; -esac - -msg "Doinstalovávám, co je potřeba ke stažení" -export DEBIAN_FRONTEND=noninteractive -apt-get update -qq -apt-get install -y -qq curl ca-certificates >/dev/null -ok "Základ je hotový" - -if [ -z "$VERSION" ]; then - msg "Zjišťuji poslední vydanou verzi" - # Jen tag; adresa přílohy se z něj složí, takže tu nepotřebujeme jq. - VERSION=$(curl -fsSL "$GITEA/api/v1/repos/$REPO/releases/latest" \ - | grep -o '"tag_name":[[:space:]]*"[^"]*"' | head -1 | cut -d'"' -f4) - [ -n "$VERSION" ] || die "Nepodařilo se zjistit poslední verzi z $GITEA." -fi -ok "Instaluje se $VERSION ($ASSET)" - -# --- stažení a ověření ------------------------------------------------------- - -BASE="$GITEA/$REPO/releases/download/$VERSION" -TMP=$(mktemp -d) -trap 'rm -rf "$TMP"' EXIT - -msg "Stahuji binárku" -curl -fsSL -o "$TMP/$ASSET" "$BASE/$ASSET" \ - || die "$ASSET pro $VERSION na releasu není. Vydání pro tuhle architekturu možná ještě neproběhlo." -curl -fsSL -o "$TMP/$ASSET.sha256" "$BASE/$ASSET.sha256" \ - || die "Chybí kontrolní součet — radši nic neinstaluju." - -msg "Ověřuji kontrolní součet" -( cd "$TMP" && sha256sum -c "$ASSET.sha256" >/dev/null ) \ - || die "Kontrolní součet nesedí. Stažený soubor zahazuju." -ok "Součet sedí" - -# --- uživatel, soubory, služba ---------------------------------------------- - -id -u "$USER_NAME" >/dev/null 2>&1 \ - || useradd --system --home-dir "$HOME_DIR" --shell /usr/sbin/nologin "$USER_NAME" - -install -d -o root -g root -m 755 "$HOME_DIR" - -# Služba se zastaví, jen když už běží — při první instalaci ještě neexistuje. -systemctl is-active --quiet kostky && systemctl stop kostky -install -o root -g root -m 755 "$TMP/$ASSET" "$HOME_DIR/kostky" -echo "$VERSION" > "$HOME_DIR/verze" -ok "Binárka je na místě" - -msg "Zapisuji službu" -cat > /etc/systemd/system/kostky.service <&2; exit 1; } header() { echo echo -e "\e[1;36m $APP\e[0m — počitadlo kostek do LXC" - echo -e " \e[2m$REPO_URL\e[0m" + echo -e " \e[2m$GITEA/$REPO\e[0m" echo } -# --- kontroly, než se něco založí ------------------------------------------- +# --- instalátor, který poběží uvnitř kontejneru ------------------------------ +# +# Je tu jako text schválně. Skript se pouští i rourou z curlu, kdy na disku +# hostitele žádný soubor není a nebylo by co poslat dovnitř. Uvozovky kolem +# 'INSTALL' jsou podstatné: nic z toho se tady nesmí rozvinout, všechny +# proměnné patří až tomu, co poběží v kontejneru. +# +# Vypsat se dá i samostatně: `bash pve-kostky.sh instalator > install.sh`, +# což se hodí, když chceš appku dostat do kontejneru, který sis založil sám. +installer() { +cat <<'INSTALL' +#!/usr/bin/env bash +# Instalace počitadla uvnitř kontejneru. O Proxmoxu nic neví, takže se dá +# pustit v jakémkoli Debianu. Je idempotentní — druhé spuštění jen vymění +# binárku za nejnovější. +set -euo pipefail -command -v pct >/dev/null || die "Tohle patří na uzel Proxmoxu — pct tu není." -[ "$(id -u)" -eq 0 ] || die "Spusť to jako root." -[ -f "$INSTALLER" ] || die "Vedle skriptu chybí lxc-install.sh." +GITEA="${GITEA:-https://gitea.spacilovi.eu}" +REPO="${REPO:-david-spacil/dice-counter}" +PORT="${PORT:-8000}" +VERSION="${VERSION:-}" -# --- instalace do kontejneru ------------------------------------------------- +USER_NAME=kostky +HOME_DIR=/opt/kostky +DATA_DIR=/var/lib/kostky + +msg() { echo -e "\e[1;34m →\e[0m $*"; } +ok() { echo -e "\e[1;32m ✓\e[0m $*"; } +die() { echo -e "\e[1;31m ✗\e[0m $*" >&2; exit 1; } + +case "$(dpkg --print-architecture)" in + amd64) ASSET=kostky-linux-x86_64 ;; + arm64) ASSET=kostky-linux-arm64 ;; + *) die "Pro architekturu $(dpkg --print-architecture) binárka není." ;; +esac + +msg "Doinstalovávám, co je potřeba ke stažení" +export DEBIAN_FRONTEND=noninteractive +apt-get update -qq +apt-get install -y -qq curl ca-certificates >/dev/null +ok "Základ je hotový" + +if [ -z "$VERSION" ]; then + msg "Zjišťuji poslední vydanou verzi" + # Jen tag; adresa přílohy se z něj složí, takže tu nepotřebujeme jq. + VERSION=$(curl -fsSL "$GITEA/api/v1/repos/$REPO/releases/latest" \ + | grep -o '"tag_name":[[:space:]]*"[^"]*"' | head -1 | cut -d'"' -f4) + [ -n "$VERSION" ] || die "Nepodařilo se zjistit poslední verzi z $GITEA." +fi +ok "Instaluje se $VERSION ($ASSET)" + +BASE="$GITEA/$REPO/releases/download/$VERSION" +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT + +msg "Stahuji binárku" +curl -fsSL -o "$TMP/$ASSET" "$BASE/$ASSET" \ + || die "$ASSET pro $VERSION na releasu není. Vydání pro tuhle architekturu možná ještě neproběhlo." +curl -fsSL -o "$TMP/$ASSET.sha256" "$BASE/$ASSET.sha256" \ + || die "Chybí kontrolní součet — radši nic neinstaluju." + +msg "Ověřuji kontrolní součet" +( cd "$TMP" && sha256sum -c "$ASSET.sha256" >/dev/null ) \ + || die "Kontrolní součet nesedí. Stažený soubor zahazuju." +ok "Součet sedí" + +id -u "$USER_NAME" >/dev/null 2>&1 \ + || useradd --system --home-dir "$HOME_DIR" --shell /usr/sbin/nologin "$USER_NAME" + +install -d -o root -g root -m 755 "$HOME_DIR" + +# Zastavit se dá jen to, co běží — při první instalaci služba ještě není. +if systemctl is-active --quiet kostky; then + systemctl stop kostky +fi + +install -o root -g root -m 755 "$TMP/$ASSET" "$HOME_DIR/kostky" +echo "$VERSION" > "$HOME_DIR/verze" +ok "Binárka je na místě" + +msg "Zapisuji službu" +cat > /etc/systemd/system/kostky.service </dev/null && break + sleep 1 +done +curl -fsS -o /dev/null "http://127.0.0.1:$PORT/" \ + || die "Služba nastartovala, ale neodpovídá. Mrkni na journalctl -u kostky." + +# Verzi bereme ze souboru, ne z binárky. Starší vydání --version neumí +# a místo výpisu by nastartovala server, který by tenhle skript zavěsil. +ok "Počitadlo $(cat "$HOME_DIR/verze") odpovídá na portu $PORT" +INSTALL +} + +# --- společné části ---------------------------------------------------------- install_into() { - local ctid="$1" + local ctid="$1" tmp + tmp=$(mktemp) + installer > "$tmp" msg "Posílám instalátor do kontejneru $ctid" - pct push "$ctid" "$INSTALLER" /root/lxc-install.sh --perms 0755 + pct push "$ctid" "$tmp" /root/kostky-install.sh --perms 0755 + rm -f "$tmp" - pct exec "$ctid" -- env PORT="$PORT" bash /root/lxc-install.sh + pct exec "$ctid" -- env PORT="$PORT" GITEA="$GITEA" REPO="$REPO" \ + VERSION="$VERSION" bash /root/kostky-install.sh } address_of() { - local ctid="$1" - pct exec "$ctid" -- hostname -I 2>/dev/null | awk '{print $1}' + pct exec "$1" -- hostname -I 2>/dev/null | awk '{print $1}' } hotovo() { @@ -80,26 +214,41 @@ hotovo() { echo echo -e " \e[2mDatabáze: /var/lib/kostky/dice.db (uvnitř kontejneru)" echo -e " Log: pct exec $ctid -- journalctl -u kostky -f" - echo -e " Konzole: pct enter $ctid\e[0m" + echo -e " Konzole: pct enter $ctid" + echo -e " Zrušit: pct stop $ctid \&\& pct destroy $ctid\e[0m" echo } +# --- co se má vlastně dělat -------------------------------------------------- +# +# Rourou z curlu se argumenty předávají mizerně, tak jde všechno i proměnnou: +# MODE=update CTID=123. Ze souboru funguje i `bash pve-kostky.sh update 123`. +MODE="${MODE:-${1:-create}}" +[ "$MODE" = "update" ] && CTID="${CTID:-${2:-}}" + +if [ "$MODE" = "instalator" ]; then + installer + exit 0 +fi + +command -v pct >/dev/null || die "Tohle patří na uzel Proxmoxu — pct tu není." +[ "$(id -u)" -eq 0 ] || die "Spusť to jako root." + # --- aktualizace ------------------------------------------------------------- -if [ "${1:-}" = "update" ]; then - ctid="${2:-}" - [ -n "$ctid" ] || die "Řekni který: bash pve-kostky.sh update " - pct status "$ctid" >/dev/null 2>&1 || die "Kontejner $ctid neexistuje." +if [ "$MODE" = "update" ]; then + [ -n "${CTID:-}" ] || die "Řekni který: CTID=123 MODE=update ..." + pct status "$CTID" >/dev/null 2>&1 || die "Kontejner $CTID neexistuje." header - [ "$(pct status "$ctid")" = "status: running" ] || { + if [ "$(pct status "$CTID")" != "status: running" ]; then msg "Startuji kontejner" - pct start "$ctid" + pct start "$CTID" sleep 5 - } + fi - install_into "$ctid" - hotovo "$ctid" + install_into "$CTID" + hotovo "$CTID" exit 0 fi @@ -107,6 +256,19 @@ fi header +# Úložiště se nehádá. local-lvm na ZFS instalacích neexistuje a pct create by +# spadlo až po půlce práce, tak se radši zeptáme systému, co tu opravdu je. +if [ -z "$STORAGE" ]; then + STORAGE=$(pvesm status -content rootdir 2>/dev/null \ + | awk 'NR>1 && $3=="active" {print $1; exit}') + [ -n "$STORAGE" ] || die "Nenašel jsem úložiště pro kontejnery. Zadej STORAGE=; co máš, ukáže pvesm status." + msg "Úložiště: $STORAGE (nalezeno automaticky)" +else + pvesm status -content rootdir 2>/dev/null | awk 'NR>1 {print $1}' | grep -qx "$STORAGE" \ + || die "Úložiště $STORAGE pro kontejnery nesedí. Co máš, ukáže: pvesm status -content rootdir" + msg "Úložiště: $STORAGE" +fi + CTID="${CTID:-$(pvesh get /cluster/nextid)}" msg "Kontejner dostane číslo $CTID" @@ -117,7 +279,7 @@ TEMPLATE=$(pveam available --section system \ [ -n "$TEMPLATE" ] || die "Šablona pro Debian $OSVERSION není k dispozici." if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then - msg "Stahuji $TEMPLATE" + msg "Stahuji $TEMPLATE (asi 120 MB, chvíli to trvá)" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null fi ok "Šablona $TEMPLATE" @@ -133,7 +295,7 @@ pct create "$CTID" "$TEMPLATE_STORAGE:vztmpl/$TEMPLATE" \ --unprivileged "$UNPRIVILEGED" \ --ostype debian \ --onboot 1 \ - --description "$APP — $REPO_URL" >/dev/null + --description "$APP — $GITEA/$REPO" >/dev/null ok "Kontejner $CTID založen" msg "Startuji" @@ -141,9 +303,10 @@ pct start "$CTID" msg "Čekám na síť" for _ in $(seq 60); do - [ -n "$(address_of "$CTID")" ] \ - && pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1 \ - && break + if [ -n "$(address_of "$CTID")" ] \ + && pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + break + fi sleep 2 done [ -n "$(address_of "$CTID")" ] || die "Kontejner nedostal adresu. Sedí bridge $BRIDGE?" From 861470da55d4e0385a2dd6992c7ab1f1c4a23ec8 Mon Sep 17 00:00:00 2001 From: gitea-actions Date: Sat, 22 Aug 2026 21:21:09 +0200 Subject: [PATCH 3/4] =?UTF-8?q?=C5=A0ablonu=20vyb=C3=ADrat=20podle=20archi?= =?UTF-8?q?tektury=20uzlu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pveam nabízí amd64 i arm64 vedle sebe a výběr přes `sort -V | tail -1` padl na tu abecedně poslední — tedy arm64 i na x86 uzlu. Šablona, která na uzlu už ležela, se pak nenašla a skript začal stahovat 120 MB nepoužitelného archivu. Architektura se teď bere z dpkg --print-architecture a nejdřív se kouká, co na uzlu už je; stahuje se, až když tam nic nesedí. Neúspěšné stažení navíc skript zastaví, místo aby se šlo zakládat kontejner ze šablony, která není. Ke stejné příležitosti Ctrl+C: bez trapu zabil jen rozdělaný podproces a skript pokračoval dál — v hlášení od uživatele je vidět, jak po přerušeném stahování vesele oznámil hotovou šablonu a pustil se do pct create. --- deploy/pve-kostky.sh | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/deploy/pve-kostky.sh b/deploy/pve-kostky.sh index def1f58..8572cf1 100755 --- a/deploy/pve-kostky.sh +++ b/deploy/pve-kostky.sh @@ -40,6 +40,10 @@ VERSION="${VERSION:-}" # prázdné = poslední vydání # --- výpisy ------------------------------------------------------------------ +# Bez tohohle Ctrl+C zabije jen rozdělaný podproces a skript jede vesele dál +# — třeba rovnou zakládat kontejner, na který se čekat nemá. +trap 'echo; echo -e "\e[1;31m ✗\e[0m Přerušeno." >&2; exit 130' INT TERM + msg() { echo -e "\e[1;34m →\e[0m $*"; } ok() { echo -e "\e[1;32m ✓\e[0m $*"; } die() { echo -e "\e[1;31m ✗\e[0m $*" >&2; exit 1; } @@ -215,7 +219,7 @@ hotovo() { echo -e " \e[2mDatabáze: /var/lib/kostky/dice.db (uvnitř kontejneru)" echo -e " Log: pct exec $ctid -- journalctl -u kostky -f" echo -e " Konzole: pct enter $ctid" - echo -e " Zrušit: pct stop $ctid \&\& pct destroy $ctid\e[0m" + echo -e " Zrušit: pct stop $ctid && pct destroy $ctid\e[0m" echo } @@ -272,17 +276,28 @@ fi CTID="${CTID:-$(pvesh get /cluster/nextid)}" msg "Kontejner dostane číslo $CTID" -msg "Hledám šablonu Debianu $OSVERSION" -pveam update >/dev/null 2>&1 || true -TEMPLATE=$(pveam available --section system \ - | awk '{print $2}' | grep "^debian-${OSVERSION}-standard" | sort -V | tail -1) -[ -n "$TEMPLATE" ] || die "Šablona pro Debian $OSVERSION není k dispozici." +# Architektura uzlu, ne první šablona v seznamu. pveam nabízí amd64 i arm64 +# vedle sebe a bez tohohle filtru by výběr padl na tu abecedně poslední. +ARCH=$(dpkg --print-architecture) +SABLONA="debian-${OSVERSION}-standard[^[:space:]]*_${ARCH}\.tar\.[a-z]*" + +msg "Hledám šablonu Debianu $OSVERSION pro $ARCH" + +# Nejdřív co už na uzlu leží — stahovat 120 MB znovu je zbytečné. +TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -o "$SABLONA" | sort -V | tail -1) + +if [ -n "$TEMPLATE" ]; then + ok "Šablona $TEMPLATE (už je na uzlu)" +else + pveam update >/dev/null 2>&1 || true + TEMPLATE=$(pveam available --section system 2>/dev/null | grep -o "$SABLONA" | sort -V | tail -1) + [ -n "$TEMPLATE" ] || die "Šablona pro Debian $OSVERSION a $ARCH není k dispozici." -if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then msg "Stahuji $TEMPLATE (asi 120 MB, chvíli to trvá)" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null \ + || die "Stažení šablony selhalo." + ok "Šablona $TEMPLATE" fi -ok "Šablona $TEMPLATE" msg "Zakládám kontejner" pct create "$CTID" "$TEMPLATE_STORAGE:vztmpl/$TEMPLATE" \ From 83654fe6b59e3a3682416e967b17be1a8635e471 Mon Sep 17 00:00:00 2001 From: gitea-actions Date: Sat, 22 Aug 2026 21:23:51 +0200 Subject: [PATCH 4/4] Zapnout kontejneru nesting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debian 13 veze systemd 257 a ten v neprivilegovaném LXC bez nestingu nedostane, co potřebuje — Proxmox na to při startu sám upozorňuje hláškou "Systemd 257 detected. You may need to enable nesting." Appka i tak nastartovala, ale spoléhat se na to nemá cenu. Stejnou výchozí hodnotu mají i community-scripts a sami varují, že moderní distribuce se systemd nesting potřebují. --- README.md | 1 + deploy/pve-kostky.sh | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index e9afc01..77044e5 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ MEMORY=1024 STORAGE=local-zfs PORT=8080 bash -c "$(curl -fsSL ...)" | `TEMPLATE_STORAGE`, `BRIDGE` | `local`, `vmbr0` | | `PORT` | `8000` | | `UNPRIVILEGED`, `OSVERSION` | `1`, `13` | +| `NESTING` | `1` — systemd v Debianu 13 ho potřebuje | | `VERSION` | poslední vydání | Aktualizace na novější vydání je ten samý příkaz s číslem kontejneru: diff --git a/deploy/pve-kostky.sh b/deploy/pve-kostky.sh index 8572cf1..d7ecffb 100755 --- a/deploy/pve-kostky.sh +++ b/deploy/pve-kostky.sh @@ -36,6 +36,7 @@ BRIDGE="${BRIDGE:-vmbr0}" PORT="${PORT:-8000}" UNPRIVILEGED="${UNPRIVILEGED:-1}" OSVERSION="${OSVERSION:-13}" +NESTING="${NESTING:-1}" VERSION="${VERSION:-}" # prázdné = poslední vydání # --- výpisy ------------------------------------------------------------------ @@ -299,6 +300,9 @@ else ok "Šablona $TEMPLATE" fi +# Nesting: Debian 13 veze systemd 257 a ten v neprivilegovaném kontejneru bez +# něj nedostane, co potřebuje — Proxmox na to při startu sám upozorňuje. +# Stejnou výchozí hodnotu mají i community-scripts. msg "Zakládám kontejner" pct create "$CTID" "$TEMPLATE_STORAGE:vztmpl/$TEMPLATE" \ --hostname "$CT_HOSTNAME" \ @@ -308,6 +312,7 @@ pct create "$CTID" "$TEMPLATE_STORAGE:vztmpl/$TEMPLATE" \ --rootfs "$STORAGE:$DISK" \ --net0 "name=eth0,bridge=$BRIDGE,ip=dhcp" \ --unprivileged "$UNPRIVILEGED" \ + --features "nesting=$NESTING" \ --ostype debian \ --onboot 1 \ --description "$APP — $GITEA/$REPO" >/dev/null