Files
dice-counter/README.md
T
gitea-actions 33e8582079 Obsluhovat to waitressem, ne vývojovým serverem Flasku
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.
2026-08-22 18:29:00 +02:00

9.5 KiB
Raw Blame History

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:

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:

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é:

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:

uv run dice.py

Bez uv

Když uv po ruce není, jde to postaru:

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:

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:

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

./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
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), 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:

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

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á.