Flask pod pěkně naformátovanou úvodní hlášku vysypal červené varování, že tohle není produkční server. Waitress je čistě pythonní, takže ho PyInstaller zabalí bez řečí, a osm vláken pobere telefony u stolu i tabuli, která se doptává každé dvě vteřiny. Cestou se ukázalo, že hláška se nikdy nedostala do přesměrovaného výstupu: mimo konzoli drží Python výstup v bloku a pouští ho, až program skončí — jenže server neskončí. Zkouška binárky u Gitey si logovala do souboru a pak ho vypisovala, takže celou dobu tiskla prázdno. Řeší to řádkové bufferování v console.utf8(), kam to patří vedle té kódové stránky.
267 lines
9.5 KiB
Markdown
267 lines
9.5 KiB
Markdown
# 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) | `kostky-linux-x86_64` |
|
||
| macOS (Apple Silicon) | `kostky-macos-arm64` |
|
||
| macOS (Intel) | `kostky-macos-x86_64` |
|
||
| Windows (x86-64) | `kostky-windows-x86_64.exe` |
|
||
|
||
Nic se neinstaluje — Python, Flask i šablony jsou uvnitř.
|
||
|
||
Na Linuxu a macOS:
|
||
|
||
```bash
|
||
chmod +x kostky-*
|
||
./kostky-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 kostky-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ý:
|
||
|
||
```
|
||
Kostky v1.1.0
|
||
|
||
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 `kostky --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/kostky/dice.db` |
|
||
| binárkou na macOS | `~/Library/Application Support/kostky/dice.db` |
|
||
| binárkou na Windows | `%LOCALAPPDATA%\kostky\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.
|
||
|
||
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.
|
||
|
||
## 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`, `kostky.spec` | stavba binárky |
|
||
| `.gitea/workflows/` | testy a linuxová binárka doma |
|
||
| `.github/workflows/` | binárky pro Windows a macOS |
|
||
|
||
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`, `kostky.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 | `.gitea/workflows/binarka.yml` |
|
||
| GitHub Actions | Windows, macOS ×2 | `.github/workflows/binarky.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 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ěží.
|
||
|
||
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 kostky-linux-x86_64.sha256 # na macOS: shasum -a 256 -c
|
||
```
|
||
|
||
Nad pull requesty se Linux staví taky, jen se nikam nevěší.
|
||
|
||
## 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` —
|
||
102 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á.
|