Sjednotit pojmenování: kostky → dice-counter
binárka / linux (pull_request) Failing after 10s
image / image (pull_request) Successful in 7s
testy / pytest (3.11) (pull_request) Successful in 1s
testy / pytest (3.12) (pull_request) Successful in 1s
testy / pytest (3.13) (pull_request) Successful in 1s
testy / pytest (3.14) (pull_request) Successful in 1s

Projekt se jmenuje dice-counter, ale skoro všude vystupoval jako kostky.
Tohle je první ze dvou kroků podle #16: nová jména se zavádějí vedle
starých, aby běžící instalace nic nerozbilo.

Nově:

    binárka a spec       dice-counter.spec, dist/dice-counter
    přílohy releasu      dice-counter-linux-x86_64 a spol.
    služba v LXC         dice-counter.service
    cesty v LXC          /opt/dice-counter, /var/lib/dice-counter
    uživatel             dice-counter
    hostname LXC         dice-counter
    instalátor           deploy/pve-dice-counter.sh
    datový adresář       ~/.local/share/dice-counter a obdoby
    co appka hlásí       "dice-counter <verze>" v banneru i v patičce

Stará jména zatím fungují dál:

  - Přílohy se na release věší pod oběma jmény.
  - Instalátor umí stáhnout i kostky-*, takže starší vydání jdou
    nainstalovat pořád.
  - deploy/pve-kostky.sh zůstává jako přesměrování — příkaz `update`
    v už založených kontejnerech chodí pro instalátor právě tam.
  - Kontejnery z dřívějška se při aktualizaci přestěhují samy: služba
    se přejmenuje a databáze se přesune do /var/lib/dice-counter, takže
    o historii her nikdo nepřijde.
  - Binárka sáhne po ~/.local/share/kostky/dice.db, dokud nová databáze
    neexistuje.

Při zkoušení přechodu vyplavala chyba, která tu byla už předtím: příkaz
`update` si nový instalátor uloží do $HOME_DIR/install.sh a odtud ho
pustí, takže `install -m 755 "$0" "$HOME_DIR/install.sh"` kopírovalo
soubor sám na sebe. install to odmítne, se `set -e` spadl celý update.
Ošetřeno testem `-ef`.

Ověřeno v debianím kontejneru: přechod ze staré instalace (včetně běhu
instalátoru z adresáře, který se během něj maže), čistá instalace, běh
proti releasu, kde je jen stará příloha, a druhé spuštění za sebou.

Refs #16
This commit is contained in:
gitea-actions committed 2026-08-23 22:29:58 +02:00
1 parent 96f2fc1028
commit 7449c01b14
13 files changed
+575 -427

No files matched your search

+13 -8
View File
@@ -67,7 +67,7 @@ jobs:
- name: Zkouška - name: Zkouška
run: | run: |
XDG_DATA_HOME=/tmp/zkouska DICE_PORT=8321 DICE_HOST=127.0.0.1 \ XDG_DATA_HOME=/tmp/zkouska DICE_PORT=8321 DICE_HOST=127.0.0.1 \
./dist/kostky > /tmp/server.log 2>&1 & ./dist/dice-counter > /tmp/server.log 2>&1 &
server=$! server=$!
trap 'kill $server 2>/dev/null || true' EXIT trap 'kill $server 2>/dev/null || true' EXIT
@@ -79,15 +79,18 @@ jobs:
curl -sf -o /dev/null http://127.0.0.1:8321/ curl -sf -o /dev/null http://127.0.0.1:8321/
curl -sf -o /dev/null http://127.0.0.1:8321/static/style.css curl -sf -o /dev/null http://127.0.0.1:8321/static/style.css
curl -sf -o /dev/null http://127.0.0.1:8321/board curl -sf -o /dev/null http://127.0.0.1:8321/board
test -f /tmp/zkouska/kostky/dice.db test -f /tmp/zkouska/dice-counter/dice.db
./dist/kostky --version ./dist/dice-counter --version
cat /tmp/server.log cat /tmp/server.log
- name: Na release - name: Na release
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
env: env:
TOKEN: ${{ secrets.GITHUB_TOKEN }} TOKEN: ${{ secrets.GITHUB_TOKEN }}
ASSET: kostky-linux-x86_64 ASSET: dice-counter-linux-x86_64
# Instalátory z dřívějška si chodí pro staré jméno. Až budou
# přemigrované, přestane se pod ním věšet.
STARY_ASSET: kostky-linux-x86_64
run: | run: |
api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" api="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}"
auth="Authorization: token ${TOKEN}" auth="Authorization: token ${TOKEN}"
@@ -104,11 +107,13 @@ jobs:
fi fi
# Součet se počítá nad jménem, pod kterým se soubor stahuje, aby # Součet se počítá nad jménem, pod kterým se soubor stahuje, aby
# šel ověřit rovnou: sha256sum -c kostky-linux-x86_64.sha256 # šel ověřit rovnou: sha256sum -c dice-counter-linux-x86_64.sha256
cp dist/kostky "$ASSET" for jmeno in "$ASSET" "$STARY_ASSET"; do
sha256sum "$ASSET" | tee "$ASSET.sha256" cp dist/dice-counter "$jmeno"
sha256sum "$jmeno" | tee "$jmeno.sha256"
done
for file in "$ASSET" "$ASSET.sha256"; do for file in "$ASSET" "$ASSET.sha256" "$STARY_ASSET" "$STARY_ASSET.sha256"; do
# Aby se dal build zopakovat, stará příloha stejného jména jde pryč. # Aby se dal build zopakovat, stará příloha stejného jména jde pryč.
curl -s -H "$auth" "$api/releases/$id/assets" \ curl -s -H "$auth" "$api/releases/$id/assets" \
| python3 -c 'import json,sys; [print(a["id"]) for a in json.load(sys.stdin) if a["name"] == sys.argv[1]]' "$file" \ | python3 -c 'import json,sys; [print(a["id"]) for a in json.load(sys.stdin) if a["name"] == sys.argv[1]]' "$file" \
+22 -12
View File
@@ -29,19 +29,26 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
# `stary` je jméno, pod kterým se příloha věšela do vydání 1.2.x.
# Instalátory z té doby si chodí pro něj, tak se pověsí obojí.
# Až budou přemigrované, sloupec zmizí.
include: include:
- os: windows-latest - os: windows-latest
asset: kostky-windows-x86_64.exe asset: dice-counter-windows-x86_64.exe
built: dist/kostky.exe stary: kostky-windows-x86_64.exe
built: dist/dice-counter.exe
- os: macos-15-intel # Intel; macos-13 GitHub zrušil - os: macos-15-intel # Intel; macos-13 GitHub zrušil
asset: kostky-macos-x86_64 asset: dice-counter-macos-x86_64
built: dist/kostky stary: kostky-macos-x86_64
built: dist/dice-counter
- os: macos-latest # Apple Silicon - os: macos-latest # Apple Silicon
asset: kostky-macos-arm64 asset: dice-counter-macos-arm64
built: dist/kostky stary: kostky-macos-arm64
built: dist/dice-counter
- os: ubuntu-24.04-arm # pro Raspberry Pi a spol. - os: ubuntu-24.04-arm # pro Raspberry Pi a spol.
asset: kostky-linux-arm64 asset: dice-counter-linux-arm64
built: dist/kostky stary: kostky-linux-arm64
built: dist/dice-counter
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
defaults: defaults:
@@ -97,6 +104,7 @@ jobs:
env: env:
TOKEN: ${{ secrets.GITEA_TOKEN }} TOKEN: ${{ secrets.GITEA_TOKEN }}
ASSET: ${{ matrix.asset }} ASSET: ${{ matrix.asset }}
STARY_ASSET: ${{ matrix.stary }}
run: | run: |
api="$GITEA/api/v1/repos/$GITEA_REPO" api="$GITEA/api/v1/repos/$GITEA_REPO"
auth="Authorization: token $TOKEN" auth="Authorization: token $TOKEN"
@@ -124,11 +132,13 @@ jobs:
fi fi
# Součet se počítá nad jménem, pod kterým se soubor stahuje, aby # Součet se počítá nad jménem, pod kterým se soubor stahuje, aby
# šel ověřit rovnou: shasum -a 256 -c kostky-macos-arm64.sha256 # šel ověřit rovnou: shasum -a 256 -c dice-counter-macos-arm64.sha256
cp "${{ matrix.built }}" "$ASSET" for jmeno in "$ASSET" "$STARY_ASSET"; do
soucet "$ASSET" | tee "$ASSET.sha256" cp "${{ matrix.built }}" "$jmeno"
soucet "$jmeno" | tee "$jmeno.sha256"
done
for file in "$ASSET" "$ASSET.sha256"; do for file in "$ASSET" "$ASSET.sha256" "$STARY_ASSET" "$STARY_ASSET.sha256"; do
# Aby se dal build zopakovat, stará příloha stejného jména jde pryč. # Aby se dal build zopakovat, stará příloha stejného jména jde pryč.
curl -s -H "$auth" "$api/releases/$id/assets" \ curl -s -H "$auth" "$api/releases/$id/assets" \
| json 'import json,sys; [print(a["id"]) for a in json.load(sys.stdin) if a["name"] == sys.argv[1]]' "$file" \ | json 'import json,sys; [print(a["id"]) for a in json.load(sys.stdin) if a["name"] == sys.argv[1]]' "$file" \
+35 -20
View File
@@ -13,19 +13,19 @@ Stáhni binárku pro svůj systém z
| Systém | Soubor | | Systém | Soubor |
|---|---| |---|---|
| Linux (x86-64) | `kostky-linux-x86_64` | | Linux (x86-64) | `dice-counter-linux-x86_64` |
| Linux (ARM64) | `kostky-linux-arm64` | | Linux (ARM64) | `dice-counter-linux-arm64` |
| macOS (Apple Silicon) | `kostky-macos-arm64` | | macOS (Apple Silicon) | `dice-counter-macos-arm64` |
| macOS (Intel) | `kostky-macos-x86_64` | | macOS (Intel) | `dice-counter-macos-x86_64` |
| Windows (x86-64) | `kostky-windows-x86_64.exe` | | Windows (x86-64) | `dice-counter-windows-x86_64.exe` |
Nic se neinstaluje — Python, Flask i šablony jsou uvnitř. Nic se neinstaluje — Python, Flask i šablony jsou uvnitř.
Na Linuxu a macOS: Na Linuxu a macOS:
```bash ```bash
chmod +x kostky-* chmod +x dice-counter-*
./kostky-linux-x86_64 ./dice-counter-linux-x86_64
``` ```
Na Windows stačí na `.exe` poklepat. Na Windows stačí na `.exe` poklepat.
@@ -38,7 +38,7 @@ Binárky nejsou podepsané — podpisové certifikáty stojí tisíce ročně a
počitadlo kostek by to byl nesmysl. Systémy si toho všimnou: počitadlo kostek by to byl nesmysl. Systémy si toho všimnou:
- **macOS** stažený soubor označí za karanténní a odmítne ho spustit. Buď - **macOS** stažený soubor označí za karanténní a odmítne ho spustit. Buď
značku sundej (`xattr -dr com.apple.quarantine kostky-macos-arm64`), nebo značku sundej (`xattr -dr com.apple.quarantine dice-counter-macos-arm64`), nebo
soubor stáhni rovnou z terminálu přes `curl -LO` — tudy se karanténa soubor stáhni rovnou z terminálu přes `curl -LO` — tudy se karanténa
nenastavuje. nenastavuje.
- **Windows** ukáže modré okno SmartScreenu. *Další informace* → - **Windows** ukáže modré okno SmartScreenu. *Další informace* →
@@ -60,7 +60,7 @@ Závislosti si skript nese v hlavičce (PEP 723) a `uv` je obstará sám.
Server vypíše všechny adresy, na kterých je dostupný: Server vypíše všechny adresy, na kterých je dostupný:
``` ```
Kostky v1.1.0 dice-counter v1.2.2
Počitadlo je dostupné na: Počitadlo je dostupné na:
→ http://10.186.234.182:8000 místní síť → http://10.186.234.182:8000 místní síť
@@ -68,7 +68,7 @@ Počitadlo je dostupné na:
http://172.17.0.1:8000 virtuální síť http://172.17.0.1:8000 virtuální síť
``` ```
Kterou verzi máš, řekne i `kostky --version`; visí taky v patičce každé Kterou verzi máš, řekne i `dice-counter --version`; visí taky v patičce každé
stránky. Když něco nefunguje, je to první věc, na kterou se zeptám. stránky. Když něco nefunguje, je to první věc, na kterou se zeptám.
Na notebooku otevři `/board`, naskenuj QR kód telefonem a hraj. Tabule Na notebooku otevři `/board`, naskenuj QR kód telefonem a hraj. Tabule
@@ -88,11 +88,15 @@ server vypíše při startu.
| Spuštěno | Databáze | | Spuštěno | Databáze |
|---|---| |---|---|
| binárkou na Linuxu | `~/.local/share/kostky/dice.db` | | binárkou na Linuxu | `~/.local/share/dice-counter/dice.db` |
| binárkou na macOS | `~/Library/Application Support/kostky/dice.db` | | binárkou na macOS | `~/Library/Application Support/dice-counter/dice.db` |
| binárkou na Windows | `%LOCALAPPDATA%\kostky\dice.db` | | binárkou na Windows | `%LOCALAPPDATA%\dice-counter\dice.db` |
| ze zdrojáků | `dice.db` v pracovním adresáři | | ze zdrojáků | `dice.db` v pracovním adresáři |
Do vydání 1.2.x se adresář jmenoval `kostky`. Kdo databázi po staru má, o ni
nepřijde — dokud nová neexistuje, používá se dál ta stará. Přesunout se dá
kdykoli ručně, přejmenováním adresáře.
Binárka se při každém spuštění rozbaluje do dočasného adresáře a pouští se Binárka se při každém spuštění rozbaluje do dočasného adresáře a pouští se
odkudkoli, takže relativní cesta by databázi rozsypala po disku — proto odkudkoli, takže relativní cesta by databázi rozsypala po disku — proto
napevno domovský adresář. Ze zdrojáků zůstává relativní, ať se dá mít víc napevno domovský adresář. Ze zdrojáků zůstává relativní, ať se dá mít víc
@@ -129,7 +133,7 @@ nastaví ho, stáhne poslední vydanou binárku, ověří kontrolní součet a z
službu: službu:
```bash ```bash
bash -c "$(curl -fsSL https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/pve-kostky.sh)" bash -c "$(curl -fsSL https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/pve-dice-counter.sh)"
``` ```
Na konci vypíše adresu, na které počitadlo poslouchá. Výchozí nastavení je Na konci vypíše adresu, na které počitadlo poslouchá. Výchozí nastavení je
@@ -143,7 +147,7 @@ MEMORY=1024 STORAGE=local-zfs PORT=8080 bash -c "$(curl -fsSL ...)"
| Proměnná | Výchozí | | Proměnná | Výchozí |
|---|---| |---|---|
| `CTID` | první volné číslo | | `CTID` | první volné číslo |
| `CT_HOSTNAME` | `kostky` | | `CT_HOSTNAME` | `dice-counter` |
| `CORES`, `MEMORY`, `DISK` | `1`, `512`, `4` | | `CORES`, `MEMORY`, `DISK` | `1`, `512`, `4` |
| `STORAGE` | první aktivní úložiště pro kontejnery | | `STORAGE` | první aktivní úložiště pro kontejnery |
| `TEMPLATE_STORAGE`, `BRIDGE` | `local`, `vmbr0` | | `TEMPLATE_STORAGE`, `BRIDGE` | `local`, `vmbr0` |
@@ -170,6 +174,13 @@ Obojí vymění binárku za poslední vydání a službu restartuje; databáze z
Příkaz `update` si přitom obnoví i sám sebe — instalátor bere z větve `main`, Příkaz `update` si přitom obnoví i sám sebe — instalátor bere z větve `main`,
takže jede na nejnovější verzi skriptu, ne na té, se kterou se instalovalo. takže jede na nejnovější verzi skriptu, ne na té, se kterou se instalovalo.
Kontejnery založené do vydání 1.2.x se při první aktualizaci přejmenují ze
starého `kostky`: služba, uživatel i cesty dostanou nová jména a databáze se
přestěhuje z `/var/lib/kostky` do `/var/lib/dice-counter`. Dělá se to samo
a stačí na to obyčejný `update`; historie her zůstává. Původní adresa skriptu
(`deploy/pve-kostky.sh`) proto ještě chvíli funguje — jen přesměrovává na
novou.
Do konzole kontejneru se dostaneš přímo z webu Proxmoxu, **žádné jméno ani Do konzole kontejneru se dostaneš přímo z webu Proxmoxu, **žádné jméno ani
heslo se nezadává** — kontejner root heslo nemá a konzole se přihlašuje sama. heslo se nezadává** — kontejner root heslo nemá a konzole se přihlašuje sama.
Nová práva to nikomu nedává: kdo se dostane do webu Proxmoxu, má root na uzlu Nová práva to nikomu nedává: kdo se dostane do webu Proxmoxu, má root na uzlu
@@ -185,7 +196,7 @@ přes rouru z curlu na disku hostitele žádný druhý soubor není. O Proxmoxu
neví, takže se dá použít i v kontejneru, který sis založil sám: neví, takže se dá použít i v kontejneru, který sis založil sám:
```bash ```bash
bash pve-kostky.sh instalator > install.sh bash pve-dice-counter.sh instalator > install.sh
``` ```
Je idempotentní; druhé spuštění jen vymění binárku za nejnovější. Je idempotentní; druhé spuštění jen vymění binárku za nejnovější.
@@ -223,7 +234,7 @@ Kdo bridge potřebuje, musí nastavit `DICE_HOST` na adresu hostitele v LAN.
| | LXC | Docker | | | LXC | Docker |
|---|---|---| |---|---|---|
| Odkud | vydaná binárka z releasu | image z registru | | Odkud | vydaná binárka z releasu | image z registru |
| Databáze | `/var/lib/kostky/dice.db` | svazek `kostky-data` | | Databáze | `/var/lib/dice-counter/dice.db` | svazek `dice-counter-data` |
| Aktualizace | `update` uvnitř kontejneru | `docker compose pull` | | Aktualizace | `update` uvnitř kontejneru | `docker compose pull` |
| Adresa a QR | funguje samo | potřeba síť hostitele | | Adresa a QR | funguje samo | potřeba síť hostitele |
@@ -262,7 +273,7 @@ při remíze na prvním místě se hraje dál.
| `dice.py` | totéž v terminálu, bez ukládání | | `dice.py` | totéž v terminálu, bez ukládání |
| `console.py` | aby čeština prošla i windowsovou konzolí | | `console.py` | aby čeština prošla i windowsovou konzolí |
| `version.py` | která verze to je — z gitu, nebo z binárky | | `version.py` | která verze to je — z gitu, nebo z binárky |
| `build.sh`, `kostky.spec` | stavba binárky | | `build.sh`, `dice-counter.spec` | stavba binárky |
| `.gitea/workflows/` | testy, linuxová binárka a zkouška image doma | | `.gitea/workflows/` | testy, linuxová binárka a zkouška image doma |
| `.github/workflows/` | binárky pro Windows a macOS | | `.github/workflows/` | binárky pro Windows a macOS |
| `deploy/` | nasazení na server: Proxmox LXC a Docker | | `deploy/` | nasazení na server: Proxmox LXC a Docker |
@@ -321,7 +332,7 @@ srovnat.
``` ```
Verzi si `build.sh` vezme z `git describe`, nebo se dá vnutit přes Verzi si `build.sh` vezme z `git describe`, nebo se dá vnutit přes
`VERSION=v1.2.3 ./build.sh`. Zapíše ji do `verze.txt`, `kostky.spec` ji `VERSION=v1.2.3 ./build.sh`. Zapíše ji do `verze.txt`, `dice-counter.spec` ji
přibalí a binárka ji pak umí ohlásit i na cizím počítači, kde žádný git není. přibalí a binárka ji pak umí ohlásit i na cizím počítači, kde žádný git není.
Verzi Pythonu, proti kterému se staví, přebíjí `PYTHON_VERSION`. Verzi Pythonu, proti kterému se staví, přebíjí `PYTHON_VERSION`.
@@ -366,9 +377,13 @@ ji kamkoli pověsí. Vedle každé visí i `.sha256`, takže se stažený soubor
ověřit: ověřit:
```bash ```bash
sha256sum -c kostky-linux-x86_64.sha256 # na macOS: shasum -a 256 -c sha256sum -c dice-counter-linux-x86_64.sha256 # na macOS: shasum -a 256 -c
``` ```
Do vydání 1.2.x se přílohy jmenovaly `kostky-*`. Ještě chvíli se budou věšet
pod oběma jmény, ať fungují instalace, které si o ně říkají po staru. Až budou
přemigrované, zůstane jen `dice-counter-*`.
Nad pull requesty se Linux staví taky, jen se nikam nevěší. Totéž platí pro Nad pull requesty se Linux staví taky, jen se nikam nevěší. Totéž platí pro
kontejnerový image — doma se nad každým PR postaví a zkusí nastartovat kontejnerový image — doma se nad každým PR postaví a zkusí nastartovat
(`.gitea/workflows/image.yml`), publikuje se ale až z tagu na GitHubu. (`.gitea/workflows/image.yml`), publikuje se ale až z tagu na GitHubu.
+1 -1
View File
@@ -38,7 +38,7 @@ fi
uv pip install --quiet --python "$python" -r requirements-build.txt uv pip install --quiet --python "$python" -r requirements-build.txt
"$pyinstaller" --clean --noconfirm \ "$pyinstaller" --clean --noconfirm \
--distpath dist --workpath "$BUILD/work" kostky.spec --distpath dist --workpath "$BUILD/work" dice-counter.spec
echo echo
ls -lh dist/ ls -lh dist/
+445
View File
@@ -0,0 +1,445 @@
#!/usr/bin/env bash
# Založí na Proxmoxu LXC kontejner s počitadlem kostek.
#
# Pouští se v terminálu uzlu Proxmoxu jako root, jedním příkazem:
#
# bash -c "$(curl -fsSL https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/pve-dice-counter.sh)"
#
# Aktualizace stávajícího kontejneru na novější vydání:
#
# CTID=123 MODE=update bash -c "$(curl -fsSL .../pve-dice-counter.sh)"
#
# Ze staženého souboru jde i obojí postaru: `bash pve-dice-counter.sh` a
# `bash pve-dice-counter.sh update 123`.
#
# Podoba je odkoukaná od community-scripts.org, ale nic z jejich frameworku
# se tu nestahuje — jejich build.func si instalační skript hledá natvrdo ve
# vlastním repozitáři, takže mimo něj nefunguje.
#
# Počítá se s domácí sítí. Appka nemá přihlašování a tenhle skript ji nijak
# nezabezpečuje; do internetu ji nepouštěj.
set -euo pipefail
APP="dice-counter"
GITEA="${GITEA:-https://gitea.spacilovi.eu}"
REPO="${REPO:-david-spacil/dice-counter}"
# Výchozí hodnoty. Všechny se dají přebít proměnnou prostředí, třeba:
# MEMORY=1024 STORAGE=local-zfs bash -c "$(curl -fsSL ...)"
CT_HOSTNAME="${CT_HOSTNAME:-dice-counter}"
CORES="${CORES:-1}"
MEMORY="${MEMORY:-512}"
DISK="${DISK:-4}"
STORAGE="${STORAGE:-}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
BRIDGE="${BRIDGE:-vmbr0}"
PORT="${PORT:-8000}"
UNPRIVILEGED="${UNPRIVILEGED:-1}"
OSVERSION="${OSVERSION:-13}"
NESTING="${NESTING:-1}"
AUTOLOGIN="${AUTOLOGIN:-1}" # konzole ve webu Proxmoxu bez hesla
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; }
header() {
echo
echo -e "\e[1;36m $APP\e[0m — počitadlo kostek do LXC"
echo -e " \e[2m$GITEA/$REPO\e[0m"
echo
}
# --- 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-dice-counter.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
GITEA="${GITEA:-https://gitea.spacilovi.eu}"
REPO="${REPO:-david-spacil/dice-counter}"
PORT="${PORT:-8000}"
VERSION="${VERSION:-}"
USER_NAME=dice-counter
HOME_DIR=/opt/dice-counter
DATA_DIR=/var/lib/dice-counter
SERVICE=dice-counter
# Do vydání 1.2.x se všechno jmenovalo kostky. Instalace z té doby se při
# aktualizaci přestěhují; až budou přemigrované, tyhle tři řádky zmizí.
STARY_USER=kostky
STARY_HOME=/opt/kostky
STARY_DATA=/var/lib/kostky
STARA_SLUZBA=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) PLATFORMA=linux-x86_64 ;;
arm64) PLATFORMA=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 pro $PLATFORMA"
BASE="$GITEA/$REPO/releases/download/$VERSION"
TMP=$(mktemp -d)
trap 'rm -rf "$TMP"' EXIT
msg "Stahuji binárku"
# Do vydání 1.2.x se přílohy jmenovaly kostky-*. Starší vydání se tak dají
# nainstalovat dál; až stará jména z releasů zmizí, zůstane jen to první.
ASSET=""
for jmeno in "dice-counter-$PLATFORMA" "kostky-$PLATFORMA"; do
curl -fsSL -o "$TMP/$jmeno" "$BASE/$jmeno" 2>/dev/null && { ASSET="$jmeno"; break; }
done
[ -n "$ASSET" ] \
|| die "Binárka pro $PLATFORMA ve vydání $VERSION není. Pro tuhle architekturu možná ještě nevyšla."
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"
# Instalace z doby, kdy se všechno jmenovalo kostky, se tady převezme. Bez
# přestěhování databáze by po aktualizaci zmizela historie her, takže se to
# nedá odbýt smazáním staré služby.
#
# Starý domovský adresář se maže až úplně na konci skriptu: při aktualizaci
# běžícího kontejneru se instalátor pouští právě z něj a `$0` z něj ještě
# potřebujeme zkopírovat na nové místo.
if [ -f "/etc/systemd/system/$STARA_SLUZBA.service" ] \
|| [ -d "$STARY_HOME" ] || [ -d "$STARY_DATA" ]; then
msg "Našel jsem instalaci pod starým jménem, stěhuji ji"
systemctl is-active --quiet "$STARA_SLUZBA" && systemctl stop "$STARA_SLUZBA"
systemctl disable --quiet "$STARA_SLUZBA" 2>/dev/null || true
rm -f "/etc/systemd/system/$STARA_SLUZBA.service"
systemctl daemon-reload
# Když nový adresář z nějakého důvodu už existuje, starý se nechá být.
# Přepsat cizí databázi je horší než nechat po sobě adresář navíc.
if [ -d "$STARY_DATA" ] && [ ! -e "$DATA_DIR" ]; then
mv "$STARY_DATA" "$DATA_DIR"
ok "Databáze přestěhována do $DATA_DIR"
elif [ -d "$STARY_DATA" ]; then
msg "$DATA_DIR už existuje, $STARY_DATA nechávám na místě"
fi
id -u "$STARY_USER" >/dev/null 2>&1 && userdel "$STARY_USER" 2>/dev/null || true
fi
# Po stěhování patří soubory uživateli, který už neexistuje. StateDirectory
# to u založeného adresáře nedorovná, tak se to udělá rovnou tady.
[ -d "$DATA_DIR" ] && chown -R "$USER_NAME:$USER_NAME" "$DATA_DIR"
# Zastavit se dá jen to, co běží — při první instalaci služba ještě není.
if systemctl is-active --quiet "$SERVICE"; then
systemctl stop "$SERVICE"
fi
install -o root -g root -m 755 "$TMP/$ASSET" "$HOME_DIR/dice-counter"
echo "$VERSION" > "$HOME_DIR/verze"
ok "Binárka je na místě"
msg "Zapisuji službu"
cat > "/etc/systemd/system/$SERVICE.service" <<EOF
[Unit]
Description=Počitadlo kostek
Documentation=$GITEA/$REPO
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=$USER_NAME
Environment=DICE_DB=$DATA_DIR/dice.db
Environment=DICE_PORT=$PORT
ExecStart=$HOME_DIR/dice-counter
Restart=on-failure
RestartSec=5
# Databáze je jediné, co appka potřebuje mít zapisovatelné. StateDirectory ji
# založí a předá správnému uživateli, takže se tu nemusí nic chownovat.
StateDirectory=dice-counter
ReadWritePaths=$DATA_DIR
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
# Binárka se při každém startu rozbaluje do /tmp; tady do vlastního, ne do
# sdíleného.
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --quiet --now "$SERVICE"
ok "Služba běží"
# --- konzole v rozhraní Proxmoxu --------------------------------------------
if [ "${AUTOLOGIN:-1}" = "1" ]; then
msg "Nastavuji konzoli"
# Kontejner nemá root heslo, takže by se do konzole ve webu Proxmoxu
# nedalo dostat. Autologin to řeší stejně, jako to dělají community-scripts.
# Nová práva to nikomu nedává — kdo má web Proxmoxu, má root na uzlu tak
# jako tak.
mkdir -p /etc/systemd/system/container-getty@1.service.d
cat > /etc/systemd/system/container-getty@1.service.d/override.conf <<'GETTY'
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 $TERM
GETTY
systemctl daemon-reload
systemctl restart container-getty@1.service 2>/dev/null || true
ok "Konzole se přihlásí sama"
fi
# --- příkaz update ----------------------------------------------------------
# Instalátor si necháme, ať je z čeho aktualizovat. Když se skript pouští
# rourou, soubor neexistuje a zůstane ten z minula.
#
# `-ef` je tu nutnost, ne opatrnost: příkaz `update` si nový instalátor uloží
# rovnou sem a odsud ho pustí, takže `$0` a cíl jsou tentýž soubor. `install`
# na to řekne "are the same file", skončí chybou a se `set -e` by celý update
# spadl dřív, než by stihl cokoli udělat.
if [ -f "$0" ] && ! [ "$0" -ef "$HOME_DIR/install.sh" ]; then
install -m 755 "$0" "$HOME_DIR/install.sh"
fi
cat > /usr/bin/update <<UPDATE
#!/usr/bin/env bash
# Aktualizace počitadla na poslední vydání. Stačí napsat: update
set -euo pipefail
# Nejdřív zkusíme obnovit i samotný instalátor, kdyby se mezitím zlepšil.
# Bez sítě nebo při změněné adrese se použije ten uložený.
NOVY=\$(mktemp)
if curl -fsSL "$GITEA/$REPO/raw/branch/main/deploy/pve-dice-counter.sh" -o "\$NOVY" 2>/dev/null \\
&& bash "\$NOVY" instalator > "\$NOVY.in" 2>/dev/null \\
&& bash -n "\$NOVY.in" 2>/dev/null; then
install -m 755 "\$NOVY.in" "$HOME_DIR/install.sh"
fi
rm -f "\$NOVY" "\$NOVY.in"
exec env PORT="$PORT" GITEA="$GITEA" REPO="$REPO" bash "$HOME_DIR/install.sh"
UPDATE
chmod 755 /usr/bin/update
ok "Aktualizovat půjde příkazem: update"
# Teď už je `$0` zkopírovaný na nové místo a starý domov nikdo nepotřebuje.
# Bash si drží otevřený popisovač na skript, takže smazat se dá i ten, který
# se zrovna vykonává.
if [ -d "$STARY_HOME" ]; then
rm -rf "$STARY_HOME"
ok "Staré $STARY_HOME uklizeno"
fi
msg "Čekám, až začne odpovídat"
for _ in $(seq 30); do
curl -fsS -o /dev/null "http://127.0.0.1:$PORT/" 2>/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 $SERVICE."
# 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" tmp
tmp=$(mktemp)
installer > "$tmp"
msg "Posílám instalátor do kontejneru $ctid"
pct push "$ctid" "$tmp" /root/dice-counter-install.sh --perms 0755
rm -f "$tmp"
pct exec "$ctid" -- env PORT="$PORT" GITEA="$GITEA" REPO="$REPO" \
VERSION="$VERSION" AUTOLOGIN="$AUTOLOGIN" bash /root/dice-counter-install.sh
}
address_of() {
pct exec "$1" -- 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/dice-counter/dice.db (uvnitř kontejneru)"
echo -e " Aktualizace: v konzoli kontejneru napiš update"
echo -e " Log: pct exec $ctid -- journalctl -u dice-counter -f"
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-dice-counter.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 [ "$MODE" = "update" ]; then
[ -n "${CTID:-}" ] || die "Řekni který: CTID=123 MODE=update ..."
pct status "$CTID" >/dev/null 2>&1 || die "Kontejner $CTID neexistuje."
header
if [ "$(pct status "$CTID")" != "status: running" ]; then
msg "Startuji kontejner"
pct start "$CTID"
sleep 5
fi
install_into "$CTID"
hotovo "$CTID"
exit 0
fi
# --- nový kontejner ----------------------------------------------------------
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=<jméno>; 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"
# 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."
msg "Stahuji $TEMPLATE (asi 120 MB, chvíli to trvá)"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null \
|| die "Stažení šablony selhalo."
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" \
--cores "$CORES" \
--memory "$MEMORY" \
--swap 256 \
--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
ok "Kontejner $CTID založen"
msg "Startuji"
pct start "$CTID"
msg "Čekám na síť"
for _ in $(seq 60); do
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?"
ok "Adresa $(address_of "$CTID")"
install_into "$CTID"
hotovo "$CTID"
+13 -375
View File
@@ -1,385 +1,23 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Založí na Proxmoxu LXC kontejner s počitadlem kostek. # Skript se přejmenoval na deploy/pve-dice-counter.sh.
# #
# Pouští se v terminálu uzlu Proxmoxu jako root, jedním příkazem: # Tenhle soubor tu zůstává schválně. Příkaz `update` v už založených
# kontejnerech si instalátor stahuje z téhle adresy — kdyby zmizela,
# aktualizace by u nich přestala fungovat dřív, než se stihnou přemigrovat.
# Až se tak stane, zmizí i tenhle soubor.
# #
# bash -c "$(curl -fsSL https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/pve-kostky.sh)" # Předává se všechno beze změny, včetně argumentů a proměnných prostředí.
# # Na stdout nesmí přibýt ani řádek navíc: starý `update` si sem chodí pro
# Aktualizace stávajícího kontejneru na novější vydání: # `instalator` a výstup rovnou kontroluje přes `bash -n`.
#
# CTID=123 MODE=update bash -c "$(curl -fsSL .../pve-kostky.sh)"
#
# Ze staženého souboru jde i obojí postaru: `bash pve-kostky.sh` a
# `bash pve-kostky.sh update 123`.
#
# Podoba je odkoukaná od community-scripts.org, ale nic z jejich frameworku
# se tu nestahuje — jejich build.func si instalační skript hledá natvrdo ve
# vlastním repozitáři, takže mimo něj nefunguje.
#
# Počítá se s domácí sítí. Appka nemá přihlašování a tenhle skript ji nijak
# nezabezpečuje; do internetu ji nepouštěj.
set -euo pipefail
APP="Kostky"
GITEA="${GITEA:-https://gitea.spacilovi.eu}"
REPO="${REPO:-david-spacil/dice-counter}"
# Výchozí hodnoty. Všechny se dají přebít proměnnou prostředí, třeba:
# MEMORY=1024 STORAGE=local-zfs bash -c "$(curl -fsSL ...)"
CT_HOSTNAME="${CT_HOSTNAME:-kostky}"
CORES="${CORES:-1}"
MEMORY="${MEMORY:-512}"
DISK="${DISK:-4}"
STORAGE="${STORAGE:-}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
BRIDGE="${BRIDGE:-vmbr0}"
PORT="${PORT:-8000}"
UNPRIVILEGED="${UNPRIVILEGED:-1}"
OSVERSION="${OSVERSION:-13}"
NESTING="${NESTING:-1}"
AUTOLOGIN="${AUTOLOGIN:-1}" # konzole ve webu Proxmoxu bez hesla
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; }
header() {
echo
echo -e "\e[1;36m $APP\e[0m — počitadlo kostek do LXC"
echo -e " \e[2m$GITEA/$REPO\e[0m"
echo
}
# --- 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 set -euo pipefail
GITEA="${GITEA:-https://gitea.spacilovi.eu}" GITEA="${GITEA:-https://gitea.spacilovi.eu}"
REPO="${REPO:-david-spacil/dice-counter}" REPO="${REPO:-david-spacil/dice-counter}"
PORT="${PORT:-8000}"
VERSION="${VERSION:-}"
USER_NAME=kostky NOVY=$(mktemp)
HOME_DIR=/opt/kostky trap 'rm -f "$NOVY"' EXIT
DATA_DIR=/var/lib/kostky
msg() { echo -e "\e[1;34m →\e[0m $*"; } curl -fsSL "$GITEA/$REPO/raw/branch/main/deploy/pve-dice-counter.sh" -o "$NOVY" \
ok() { echo -e "\e[1;32m ✓\e[0m $*"; } || { echo "Skript se přesunul do deploy/pve-dice-counter.sh, ale nejde stáhnout." >&2; exit 1; }
die() { echo -e "\e[1;31m ✗\e[0m $*" >&2; exit 1; }
case "$(dpkg --print-architecture)" in bash "$NOVY" "$@"
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 <<EOF
[Unit]
Description=Počitadlo kostek
Documentation=$GITEA/$REPO
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=$USER_NAME
Environment=DICE_DB=$DATA_DIR/dice.db
Environment=DICE_PORT=$PORT
ExecStart=$HOME_DIR/kostky
Restart=on-failure
RestartSec=5
# Databáze je jediné, co appka potřebuje mít zapisovatelné. StateDirectory ji
# založí a předá správnému uživateli, takže se tu nemusí nic chownovat.
StateDirectory=kostky
ReadWritePaths=$DATA_DIR
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
# Binárka se při každém startu rozbaluje do /tmp; tady do vlastního, ne do
# sdíleného.
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --quiet --now kostky
ok "Služba běží"
# --- konzole v rozhraní Proxmoxu --------------------------------------------
if [ "${AUTOLOGIN:-1}" = "1" ]; then
msg "Nastavuji konzoli"
# Kontejner nemá root heslo, takže by se do konzole ve webu Proxmoxu
# nedalo dostat. Autologin to řeší stejně, jako to dělají community-scripts.
# Nová práva to nikomu nedává — kdo má web Proxmoxu, má root na uzlu tak
# jako tak.
mkdir -p /etc/systemd/system/container-getty@1.service.d
cat > /etc/systemd/system/container-getty@1.service.d/override.conf <<'GETTY'
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 $TERM
GETTY
systemctl daemon-reload
systemctl restart container-getty@1.service 2>/dev/null || true
ok "Konzole se přihlásí sama"
fi
# --- příkaz update ----------------------------------------------------------
# Instalátor si necháme, ať je z čeho aktualizovat. Když se skript pouští
# rourou, soubor neexistuje a zůstane ten z minula.
if [ -f "$0" ]; then
install -m 755 "$0" "$HOME_DIR/install.sh"
fi
cat > /usr/bin/update <<UPDATE
#!/usr/bin/env bash
# Aktualizace počitadla na poslední vydání. Stačí napsat: update
set -euo pipefail
# Nejdřív zkusíme obnovit i samotný instalátor, kdyby se mezitím zlepšil.
# Bez sítě nebo při změněné adrese se použije ten uložený.
NOVY=\$(mktemp)
if curl -fsSL "$GITEA/$REPO/raw/branch/main/deploy/pve-kostky.sh" -o "\$NOVY" 2>/dev/null \\
&& bash "\$NOVY" instalator > "\$NOVY.in" 2>/dev/null \\
&& bash -n "\$NOVY.in" 2>/dev/null; then
install -m 755 "\$NOVY.in" "$HOME_DIR/install.sh"
fi
rm -f "\$NOVY" "\$NOVY.in"
exec env PORT="$PORT" GITEA="$GITEA" REPO="$REPO" bash "$HOME_DIR/install.sh"
UPDATE
chmod 755 /usr/bin/update
ok "Aktualizovat půjde příkazem: update"
msg "Čekám, až začne odpovídat"
for _ in $(seq 30); do
curl -fsS -o /dev/null "http://127.0.0.1:$PORT/" 2>/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" tmp
tmp=$(mktemp)
installer > "$tmp"
msg "Posílám instalátor do kontejneru $ctid"
pct push "$ctid" "$tmp" /root/kostky-install.sh --perms 0755
rm -f "$tmp"
pct exec "$ctid" -- env PORT="$PORT" GITEA="$GITEA" REPO="$REPO" \
VERSION="$VERSION" AUTOLOGIN="$AUTOLOGIN" bash /root/kostky-install.sh
}
address_of() {
pct exec "$1" -- 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 " Aktualizace: v konzoli kontejneru napiš update"
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
}
# --- 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 [ "$MODE" = "update" ]; then
[ -n "${CTID:-}" ] || die "Řekni který: CTID=123 MODE=update ..."
pct status "$CTID" >/dev/null 2>&1 || die "Kontejner $CTID neexistuje."
header
if [ "$(pct status "$CTID")" != "status: running" ]; then
msg "Startuji kontejner"
pct start "$CTID"
sleep 5
fi
install_into "$CTID"
hotovo "$CTID"
exit 0
fi
# --- nový kontejner ----------------------------------------------------------
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=<jméno>; 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"
# 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."
msg "Stahuji $TEMPLATE (asi 120 MB, chvíli to trvá)"
pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null \
|| die "Stažení šablony selhalo."
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" \
--cores "$CORES" \
--memory "$MEMORY" \
--swap 256 \
--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
ok "Kontejner $CTID založen"
msg "Startuji"
pct start "$CTID"
msg "Čekám na síť"
for _ in $(seq 60); do
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?"
ok "Adresa $(address_of "$CTID")"
install_into "$CTID"
hotovo "$CTID"
+1 -1
View File
@@ -28,7 +28,7 @@ exe = EXE(
analysis.scripts, analysis.scripts,
analysis.binaries, analysis.binaries,
analysis.datas, analysis.datas,
name="kostky", name="dice-counter",
console=True, console=True,
# Ořezání symbolů šetří pár megabajtů, ale jen na Linuxu. Na macOS by # Ořezání symbolů šetří pár megabajtů, ale jen na Linuxu. Na macOS by
# rozbilo podpis, který si PyInstaller sám přidává a bez kterého se # rozbilo podpis, který si PyInstaller sám přidává a bez kterého se
+1 -1
View File
@@ -134,7 +134,7 @@ if __name__ == "__main__":
console.utf8() console.utf8()
if {"--version", "-V"} & set(sys.argv[1:]): if {"--version", "-V"} & set(sys.argv[1:]):
print(f"kostky {version.current()}") print(f"dice-counter {version.current()}")
sys.exit(0) sys.exit(0)
main() main()
+16 -3
View File
@@ -27,6 +27,14 @@ def data_home() -> Path:
or Path.home() / ".local" / "share") or Path.home() / ".local" / "share")
APP_DIR = "dice-counter"
# Do vydání 1.2.x se adresář jmenoval jinak. Kdo má databázi po staru, o ni
# přejmenováním nepřijde — použije se dál. Až se stará jména přestanou
# podporovat, zmizí i tohle.
STARY_APP_DIR = "kostky"
def default_db() -> Path: def default_db() -> Path:
"""Kam se ukládá databáze. """Kam se ukládá databáze.
@@ -38,9 +46,14 @@ def default_db() -> Path:
chosen = os.environ.get("DICE_DB") chosen = os.environ.get("DICE_DB")
if chosen: if chosen:
return Path(chosen) return Path(chosen)
if getattr(sys, "frozen", False): if not getattr(sys, "frozen", False):
return data_home() / "kostky" / "dice.db" return Path("dice.db")
return Path("dice.db")
nova = data_home() / APP_DIR / "dice.db"
stara = data_home() / STARY_APP_DIR / "dice.db"
if not nova.exists() and stara.exists():
return stara
return nova
DB_PATH = default_db() DB_PATH = default_db()
+1 -1
View File
@@ -8,6 +8,6 @@
</head> </head>
<body> <body>
{% block body %}{% endblock %} {% block body %}{% endblock %}
<footer class="verze">kostky {{ verze }}</footer> <footer class="verze">dice-counter {{ verze }}</footer>
</body> </body>
</html> </html>
+23 -1
View File
@@ -152,7 +152,29 @@ def test_umisteni_databaze_z_binarky(monkeypatch, tmp_path):
monkeypatch.setattr(storage.sys, "platform", "linux") monkeypatch.setattr(storage.sys, "platform", "linux")
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path)) monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
assert storage.default_db() == tmp_path / "kostky" / "dice.db" assert storage.default_db() == tmp_path / "dice-counter" / "dice.db"
def test_umisteni_databaze_po_starem(monkeypatch, tmp_path):
"""Kdo má databázi pod starým jménem, o ni přejmenováním nepřijde."""
monkeypatch.delenv("DICE_DB", raising=False)
monkeypatch.setattr(storage.sys, "frozen", True, raising=False)
monkeypatch.setattr(storage.sys, "platform", "linux")
monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path))
stara = tmp_path / "kostky" / "dice.db"
stara.parent.mkdir()
stara.touch()
assert storage.default_db() == stara
# Jakmile nová existuje, stará se ignoruje — jinak by se dvě databáze
# přetahovaly o to, která platí.
nova = tmp_path / "dice-counter" / "dice.db"
nova.parent.mkdir()
nova.touch()
assert storage.default_db() == nova
def test_data_home_podle_systemu(monkeypatch, tmp_path): def test_data_home_podle_systemu(monkeypatch, tmp_path):
+1 -1
View File
@@ -17,7 +17,7 @@ UNKNOWN = "neznámá"
def bundled() -> str: def bundled() -> str:
"""Verze vypálená do binárky při stavbě. """Verze vypálená do binárky při stavbě.
`build.sh` ji zapíše do souboru, `kostky.spec` ji přibalí. V binárce `build.sh` ji zapíše do souboru, `dice-counter.spec` ji přibalí. V binárce
leží vedle šablon v dočasném adresáři, na který ukazuje `sys._MEIPASS`. leží vedle šablon v dočasném adresáři, na který ukazuje `sys._MEIPASS`.
""" """
root = Path(getattr(sys, "_MEIPASS", Path(__file__).parent)) root = Path(getattr(sys, "_MEIPASS", Path(__file__).parent))
+3 -3
View File
@@ -350,7 +350,7 @@ def export():
return send_file(io.BytesIO(data), mimetype="application/vnd.sqlite3", return send_file(io.BytesIO(data), mimetype="application/vnd.sqlite3",
as_attachment=True, as_attachment=True,
download_name=f"kostky-{date.today().isoformat()}.db") download_name=f"dice-counter-{date.today().isoformat()}.db")
@app.route("/stats") @app.route("/stats")
@@ -364,7 +364,7 @@ def hall():
def announce() -> None: def announce() -> None:
"""Vypíše, kde všude je počitadlo k mání, a čím je zrovna spuštěné.""" """Vypíše, kde všude je počitadlo k mání, a čím je zrovna spuštěné."""
print(f"Kostky {version.current()}\n") print(f"dice-counter {version.current()}\n")
found = net.addresses() found = net.addresses()
@@ -384,7 +384,7 @@ if __name__ == "__main__":
console.utf8() console.utf8()
if {"--version", "-V"} & set(sys.argv[1:]): if {"--version", "-V"} & set(sys.argv[1:]):
print(f"kostky {version.current()}") print(f"dice-counter {version.current()}")
sys.exit(0) sys.exit(0)
announce() announce()