Files
gitea-actions a6c94e239b
binárka / linux (pull_request) Successful in 11s
image / image (pull_request) Successful in 18s
testy / pytest (3.11) (pull_request) Successful in 1s
testy / pytest (3.12) (pull_request) Successful in 0s
testy / pytest (3.13) (pull_request) Successful in 0s
testy / pytest (3.14) (pull_request) Successful in 1s
Zaříznout stará jména kostky
Druhý krok po #16, rovnou celý. Dvojkolejnost dávala smysl pro projekt
s neznámými uživateli; tady běží jedna instance a ta je přemigrovaná.

Pryč je:

  - deploy/pve-kostky.sh (přesměrování na nový skript)
  - stěhování ze starého jména v instalátoru včetně bloku STARY_*
  - záložní jméno přílohy kostky-* v instalátoru
  - věšení příloh pod dvěma jmény v obou workflow
  - záloha na ~/.local/share/kostky/dice.db ve storage.py
  - odstavce o přechodu v README

Jediné, co ve zdrojácích zůstalo, je docstring "Pravidla hry v kostky"
v core.py — to je česky pojmenovaná hra, ne identifikátor.

POZOR na pořadí: instalátor teď hledá jen dice-counter-*, a poslední
vydání (v1.2.1) má na releasu pouze kostky-*. Do otagování v1.2.2 tedy
`update` ani čerstvá instalace nemají co stáhnout. Merge a tag patří
k sobě.

Ověřeno v debianím kontejneru: čistá instalace, druhé spuštění za sebou,
a vydání bez nových jmen spadne se srozumitelnou hláškou místo toho, aby
sáhlo po starém. Binárka postavená lokálně zakládá databázi v
…/dice-counter/ a starou vedle sebe ignoruje. 118 testů.

Closes #19
2026-08-23 23:09:15 +02:00

392 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Dice Counter
Doprovod k fyzickým kostkám: **telefon zapisuje skóre, notebook ukazuje
zápisník** a hry se pamatují mezi večery.
Hráči se napříč hrami poznávají podle jména — Adam ze středečního večera je
ten samý Adam jako z minulého měsíce. Žádné účty, žádná hesla.
## Spuštění
Stáhni binárku pro svůj systém z
[releases](https://gitea.spacilovi.eu/david-spacil/dice-counter/releases):
| Systém | Soubor |
|---|---|
| Linux (x86-64) | `dice-counter-linux-x86_64` |
| Linux (ARM64) | `dice-counter-linux-arm64` |
| macOS (Apple Silicon) | `dice-counter-macos-arm64` |
| macOS (Intel) | `dice-counter-macos-x86_64` |
| Windows (x86-64) | `dice-counter-windows-x86_64.exe` |
Nic se neinstaluje — Python, Flask i šablony jsou uvnitř.
Na Linuxu a macOS:
```bash
chmod +x dice-counter-*
./dice-counter-linux-x86_64
```
Na Windows stačí na `.exe` poklepat.
Linuxová binárka je postavená proti glibc 2.17, takže jede prakticky všude.
### Než to poprvé pustíš
Binárky nejsou podepsané — podpisové certifikáty stojí tisíce ročně a na
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ď
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
nenastavuje.
- **Windows** ukáže modré okno SmartScreenu. *Další informace* →
*Přesto spustit*. Občas si postěžuje i antivirus; u zabalených Python
programů je to běžný falešný poplach. Při prvním spuštění se ještě zeptá
brána firewall — bez povolení do místní sítě se telefon nepřipojí.
Kdo tomu nechce věřit, ověří si stažený soubor podle přiloženého součtu
(níž) nebo si ho postaví sám — `./build.sh`, zdrojáky jsou tady celé.
Ze zdrojáků to je stejně krátké:
```bash
uv run web.py
```
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ý:
```
dice-counter v1.2.2
Počitadlo je dostupné na:
→ http://10.186.234.182:8000 místní síť
http://100.91.0.24:8000 přes Tailscale
http://172.17.0.1:8000 virtuální síť
```
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.
Na notebooku otevři `/board`, naskenuj QR kód telefonem a hraj. Tabule
nabízí i zbylé adresy — když jedna nefunguje, klikni na jinou a QR kód se
přepne na ni. Zastavuje se `Ctrl+C`.
| Proměnná | K čemu je | Výchozí |
|---|---|---|
| `DICE_DB` | soubor s databází | podle způsobu spuštění, viz níž |
| `DICE_PORT` | port | `8000` |
| `DICE_HOST` | pevná adresa; vypne hledání | hledá se za běhu |
## Kde jsou data
Jeden soubor SQLite. Založí se sám při prvním načtení stránky, cestu k němu
server vypíše při startu.
| Spuštěno | Databáze |
|---|---|
| binárkou na Linuxu | `~/.local/share/dice-counter/dice.db` |
| binárkou na macOS | `~/Library/Application Support/dice-counter/dice.db` |
| binárkou na Windows | `%LOCALAPPDATA%\dice-counter\dice.db` |
| ze zdrojáků | `dice.db` v pracovním adresáři |
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
napevno domovský adresář. Ze zdrojáků zůstává relativní, ať se dá mít víc
sad vedle sebe.
Zálohovat i stěhovat jde prostým zkopírováním souboru; `DICE_DB` si ho
najde kdekoli. Kdo ho hledat nechce — a z binárky na Windows je zalezlý —
si ho stáhne ze síně slávy odkazem **Stáhnout databázi** (`/export`). Kopie
se dělá přes `VACUUM INTO`, takže je použitelná i uprostřed rozehrané hry.
Schéma má verzi uloženou v `PRAGMA user_version` a `storage.migrate()` si
databázi při každém připojení dorovná. Starší soubory z verzí, kdy se verze
schématu ještě nepsala, se poznají podle nuly a orazítkují se. Databázi
založenou novější verzí počitadla appka odmítne otevřít, místo aby ji tiše
rozbila.
Obsluhuje to `waitress` — čistě pythonní WSGI server, který se zabalí do
binárky stejně snadno jako zbytek. Vestavěný server Flasku by pod úvodní
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)
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 -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
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 -c "$(curl -fsSL ...)"
```
| Proměnná | Výchozí |
|---|---|
| `CTID` | první volné číslo |
| `CT_HOSTNAME` | `dice-counter` |
| `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` |
| `NESTING` | `1` — systemd v Debianu 13 ho potřebuje |
| `AUTOLOGIN` | `1` — konzole ve webu Proxmoxu bez hesla |
| `VERSION` | poslední vydání |
Aktualizace na novější vydání se dá spustit dvěma způsoby. Buď zevnitř
kontejneru, kde na to stačí jedno slovo:
```bash
update
```
Nebo z uzlu Proxmoxu tím samým příkazem s číslem kontejneru:
```bash
CTID=123 MODE=update bash -c "$(curl -fsSL ...)"
```
Obojí vymění binárku za poslední vydání a službu restartuje; databáze zůstává.
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.
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.
Nová práva to nikomu nedává: kdo se dostane do webu Proxmoxu, má root na uzlu
tak jako tak. Komu se to nezdá, vypne to přes `AUTOLOGIN=0` a do kontejneru
pak leze přes `pct enter <ctid>`.
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.
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-dice-counter.sh instalator > install.sh
```
Je idempotentní; druhé spuštění jen vymění binárku za nejnovější.
### Docker
Stačí jeden soubor, repozitář k tomu potřeba není:
```bash
curl -fsSLO https://gitea.spacilovi.eu/david-spacil/dice-counter/raw/branch/main/deploy/docker-compose.yaml
docker compose up -d
```
Image se stáhne hotový z registru Gitey, pro amd64 i arm64 — Docker si vybere
svoji variantu sám. Databáze leží v pojmenovaném svazku, appka běží pod
nerootovým uživatelem.
Aktualizace na novější vydání:
```bash
docker compose pull && docker compose up -d
```
Kdo si ho radši postaví ze zdrojáků, přebije ten stažený stejnou značkou:
```bash
docker build -f deploy/Dockerfile -t gitea.spacilovi.eu/david-spacil/dice-counter:latest .
```
**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 | image z registru |
| Databáze | `/var/lib/dice-counter/dice.db` | svazek `dice-counter-data` |
| Aktualizace | `update` uvnitř kontejneru | `docker compose 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
hotspot a zpátky si tabule pohlídá sama a načte se znovu.
Zbývají tři důvody, proč se telefon nedostane na notebook:
- **Telefon je na mobilních datech**, notebook na WiFi. Různé sítě, nepotkají
se. Připoj telefon na stejnou WiFi, nebo notebook na hotspot telefonu —
to funguje taky.
- **Zapnutý hotspot na telefonu shodil jeho WiFi.** Většina Androidů to dělá.
Pak je řešením připojit na ten hotspot i notebook.
- **VPN nebo Tailscale exit node na telefonu** posílá veškerý provoz mimo
místní síť. V Tailscale to řeší přepínač „Allow local network access";
případně použij tailnet adresu notebooku, ta funguje odkudkoli.
## Pravidla
Skóre se zadává ručně po tazích, hází se doopravdy. Tři nulové tahy za sebou
vynulují hráči skóre; indikátor `x` / `xx` ukazuje, jak blízko k tomu je.
Vítěz se vyhlašuje až po dokončeném kole, aby měli všichni stejný počet tahů —
při remíze na prvním místě se hraje dál.
## Co kde je
| Soubor | Role |
|---|---|
| `core.py` | pravidla hry, žádný vstup ani výstup |
| `net.py` | hledání adres, na kterých je počitadlo dostupné |
| `storage.py` | SQLite: hráči, hry, tahy |
| `stats.py` | síň slávy a kariérní statistiky |
| `web.py` | Flask aplikace |
| `dice.py` | totéž v terminálu, bez ukládání |
| `console.py` | aby čeština prošla i windowsovou konzolí |
| `version.py` | která verze to je — z gitu, nebo z binárky |
| `build.sh`, `dice-counter.spec` | stavba binárky |
| `.gitea/workflows/` | testy, linuxová binárka a zkouška image 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:
```bash
uv run dice.py
```
## Bez uv
Když `uv` po ruce není, jde to postaru:
```bash
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python web.py
```
Na Fedoře stačí i systémové balíčky, pak se nemusí řešit vůbec nic:
```bash
sudo dnf install python3-flask python3-qrcode python3-tabulate python3-waitress
python3 web.py
```
## Závislosti
Verze jsou zamčené, aby binárka postavená dnes a za půl roku obsahovala
totéž. Volné seznamy jsou v `.in`, zamčené v `.txt`:
| Soubor | K čemu |
|---|---|
| `requirements.in` → `.txt` | běh aplikace |
| `requirements-dev.in` → `.txt` | + pytest, pro testy |
| `requirements-build.in` → `.txt` | + PyInstaller, pro stavbu binárky |
Aktualizace je vědomý krok, ne vedlejší efekt toho, že něco vyšlo na PyPI:
```bash
for f in requirements requirements-dev requirements-build; do
uv pip compile "$f.in" -o "$f.txt"
done
```
Verze se píšou ještě jednou v hlavičce PEP 723 na začátku `web.py` a
`dice.py`, aby fungovalo `uv run web.py`. Že se ty dva zápisy shodují, hlídá
`tests/test_zavislosti.py` — po každém přegenerování je potřeba hlavičky
srovnat.
## Vlastní binárka
```bash
./build.sh
```
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`, `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í.
Verzi Pythonu, proti kterému se staví, přebíjí `PYTHON_VERSION`.
Staví se proti samostatnému CPythonu od `uv`, ne proti systémovému. Ten je
slinkovaný s glibc 2.17; postavené proti Pythonu z Fedory 44 by to chtělo
glibc 2.43 a nešlo by spustit skoro nikde — glibc drží zpětnou kompatibilitu,
ne dopřednou. Nic z hostitele se do binárky nedostane, takže kontejner k tomu
potřeba není. Výsledek je v `dist/`.
Stejný `build.sh` staví i na macOS a na Windows (v Git Bash) — jen tam bez
té starosti o glibc. Křížem to nejde: PyInstaller balí do výsledku interpret
a knihovny toho systému, na kterém běží, takže **binárku pro každý systém
musí postavit ten systém.**
Terminálová verze součástí binárky není; ta se pouští ze zdrojáků.
### Jak vznikají releasy
Otagovaný commit spustí stavbu na obou stranách:
| Kde | Co staví | Workflow |
|---|---|---|
| vlastní runner u Gitey | Linux x86-64 | `.gitea/workflows/binarka.yml` |
| GitHub Actions | Windows, macOS ×2, Linux ARM64 | `.github/workflows/binarky.yml` |
| GitHub Actions | image amd64 + arm64 | `.github/workflows/image.yml` |
```bash
git tag v1.0.0 && git push origin v1.0.0
```
GitHub je tu jen půjčená dílna. Repozitář se tam z Gitey zrcadlí
([mirror](https://github.com/david-spacil/dice-counter)), postavené soubory
se posílají zpátky na zdejší release přes Gitea API a projekt má pořád jednu
stránku s releasy — tuhle. Linux na x86-64 se na GitHubu schválně nestaví;
doma to jde proti staré glibc, a když GitHub vypadne, release má aspoň tu
platformu, na které to reálně poběží. ARM64 je výjimka — doma na něm není
na čem stavět, a hodí se pro Raspberry Pi nebo jinou malou pořád zapnutou
krabičku.
Každá stavba nejdřív projede testy a zkusí hotovou binárku nastartovat, než
ji kamkoli pověsí. Vedle každé visí i `.sha256`, takže se stažený soubor dá
ověřit:
```bash
sha256sum -c dice-counter-linux-x86_64.sha256 # na macOS: shasum -a 256 -c
```
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
(`.gitea/workflows/image.yml`), publikuje se ale až z tagu na GitHubu.
Image se schválně nevydává doma: tamní runner běží v LXC, kde nejde
zaregistrovat emulaci pro arm64 — je to vlastnost jádra hostitele a ten je
produkční stroj. Běhouni GitHubu jsou plnohodnotné virtuály, kde to funguje
bez zásahu do čehokoli.
## Testy
```bash
uv run --with-requirements requirements.txt --no-project pytest
```
Nebo z připraveného prostředí prostě `pytest`.
Nad každým pull requestem a nad `main` běží `.gitea/workflows/testy.yml` —
118 testů na Pythonu 3.11, 3.12, 3.13 i 3.14, a k tomu skriptovaná partie
v terminálové verzi, na kterou pytest nesahá.