Binárka má vědět, která je
Venku jsou čtyři soubory na release, tři systémy a k tomu zdrojáky. Když někdo napíše, že mu něco nefunguje, není jak zjistit, co vlastně pustil. Verzi zapisuje build.sh do verze.txt, kostky.spec ji přibalí a version.py ji z rozbalené binárky přečte. Ze zdrojáků má přednost git describe, který ví i o commitech nad tagem a o rozdělané práci. V CI se na tagu vnucuje proměnnou, protože runner klonuje jeden commit bez značek. Hlásí se přes --version, v úvodní hlášce serveru a v patičce každé stránky. Zkouška v obou workflow ověří, že se do binárky opravdu dostala.
This commit is contained in:
1 parent
4ffd7d3c9d
commit
e3d56f4c22
12 files changed
+181
-8
No files matched your search
@@ -56,7 +56,13 @@ jobs:
|
||||
run: uv run --with-requirements requirements-dev.txt --no-project pytest -q
|
||||
|
||||
- name: Binárka
|
||||
run: ./build.sh
|
||||
run: |
|
||||
# Runner klonuje jeden commit bez značek, takže si git describe
|
||||
# nemá o co opřít — na tagu mu verzi řekneme rovnou.
|
||||
if [ "$GITHUB_REF_TYPE" = tag ]; then
|
||||
export VERSION="$GITHUB_REF_NAME"
|
||||
fi
|
||||
./build.sh
|
||||
|
||||
- name: Zkouška
|
||||
run: |
|
||||
@@ -74,6 +80,7 @@ jobs:
|
||||
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
|
||||
test -f /tmp/zkouska/kostky/dice.db
|
||||
./dist/kostky --version
|
||||
cat /tmp/server.log
|
||||
|
||||
- name: Na release
|
||||
|
||||
@@ -55,7 +55,13 @@ jobs:
|
||||
run: uv run --with-requirements requirements-dev.txt --no-project pytest -q
|
||||
|
||||
- name: Binárka
|
||||
run: ./build.sh
|
||||
run: |
|
||||
# Na tagu je verze jistá. Jinde je repozitář naklonovaný mělce
|
||||
# a build.sh si vystačí s tím, co z gitu vyčte.
|
||||
if [ "$GITHUB_REF_TYPE" = tag ]; then
|
||||
export VERSION="$GITHUB_REF_NAME"
|
||||
fi
|
||||
./build.sh
|
||||
|
||||
- name: Zkouška
|
||||
run: |
|
||||
@@ -75,6 +81,10 @@ jobs:
|
||||
curl -sf "http://127.0.0.1:8123/board" | grep -q "127.0.0.1:8123"
|
||||
test -f zkouska/dice.db
|
||||
|
||||
# Verze se musí do binárky opravdu dostat, ne se hlásit jako neznámá.
|
||||
"${{ matrix.built }}" --version
|
||||
! "${{ matrix.built }}" --version | grep -q "neznámá"
|
||||
|
||||
echo "Binárka jede."
|
||||
|
||||
- name: Na release
|
||||
|
||||
@@ -5,3 +5,4 @@ __pycache__/
|
||||
dist/
|
||||
build/
|
||||
.build/
|
||||
verze.txt
|
||||
@@ -59,12 +59,17 @@ 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`.
|
||||
@@ -138,6 +143,7 @@ při remíze na prvním místě se hraje dál.
|
||||
| `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 |
|
||||
@@ -195,6 +201,11 @@ srovnat.
|
||||
./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,
|
||||
|
||||
@@ -12,11 +12,18 @@
|
||||
# se do ní nedostane, takže kontejner k tomu není potřeba.
|
||||
set -euo pipefail
|
||||
|
||||
VERSION=${VERSION:-3.11}
|
||||
PYTHON_VERSION=${PYTHON_VERSION:-3.11}
|
||||
BUILD=${BUILD:-.build}
|
||||
|
||||
uv python install "$VERSION"
|
||||
uv venv --quiet --managed-python --python "$VERSION" "$BUILD"
|
||||
# Verze, kterou binárka ohlásí přes --version. V CI ji na tagu vnutíme
|
||||
# proměnnou, protože tam je repozitář naklonovaný na jeden commit a git
|
||||
# describe nemá o co se opřít.
|
||||
VERSION=${VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo neznámá)}
|
||||
printf '%s\n' "$VERSION" > verze.txt
|
||||
echo "Verze: $VERSION"
|
||||
|
||||
uv python install "$PYTHON_VERSION"
|
||||
uv venv --quiet --managed-python --python "$PYTHON_VERSION" "$BUILD"
|
||||
|
||||
# Windows dává spustitelné soubory venvu do Scripts/, zbytek světa do bin/.
|
||||
python="$BUILD/bin/python"
|
||||
|
||||
@@ -8,11 +8,13 @@ Pravidla žijí v `core.Game`, tenhle modul je jen vstup a výstup. Hraje se
|
||||
v paměti, nic se neukládá — na historii je webová aplikace (`web.py`).
|
||||
"""
|
||||
|
||||
import sys
|
||||
from os import name, system
|
||||
|
||||
from tabulate import tabulate
|
||||
|
||||
import console
|
||||
import version
|
||||
from core import FINAL_SCORE, Game
|
||||
|
||||
table_header = ["Hráč", "Skóre"]
|
||||
@@ -130,4 +132,9 @@ def main() -> None:
|
||||
|
||||
if __name__ == "__main__":
|
||||
console.utf8()
|
||||
|
||||
if {"--version", "-V"} & set(sys.argv[1:]):
|
||||
print(f"kostky {version.current()}")
|
||||
sys.exit(0)
|
||||
|
||||
main()
|
||||
+8
-1
@@ -5,12 +5,19 @@ Staví se přes `./build.sh`, ne ručně — ta se stará o to, aby výsledek je
|
||||
i na starších systémech, než je ten tvůj.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Verzi zapisuje build.sh těsně před stavbou. Když se pyinstaller pustí
|
||||
# ručně bez ní, binárka se postaví taky — jen se pak hlásí jako neznámá.
|
||||
datas = [("templates", "templates"), ("static", "static")]
|
||||
if os.path.exists("verze.txt"):
|
||||
datas.append(("verze.txt", "."))
|
||||
|
||||
analysis = Analysis(
|
||||
["web.py"],
|
||||
pathex=["."],
|
||||
datas=[("templates", "templates"), ("static", "static")],
|
||||
datas=datas,
|
||||
noarchive=False,
|
||||
)
|
||||
|
||||
|
||||
@@ -513,3 +513,11 @@ button {
|
||||
font-size: 0.85em;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Verze v patičce. Má být k nalezení, když ji někdo hledá, a jinak neplést. */
|
||||
.verze {
|
||||
margin: 2rem 0 0.75rem;
|
||||
text-align: center;
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.35;
|
||||
}
|
||||
@@ -8,5 +8,6 @@
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}{% endblock %}
|
||||
<footer class="verze">kostky {{ verze }}</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
"""Verze se musí poznat i z binárky, kde žádný git není."""
|
||||
|
||||
import pytest
|
||||
|
||||
import version
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def bez_pameti():
|
||||
"""`current()` si výsledek pamatuje; mezi testy je potřeba čistý stůl."""
|
||||
version.current.cache_clear()
|
||||
yield
|
||||
version.current.cache_clear()
|
||||
|
||||
|
||||
def test_z_binarky_se_cte_pribaleny_soubor(monkeypatch, tmp_path):
|
||||
"""V binárce leží verze.txt v dočasném adresáři vedle šablon."""
|
||||
(tmp_path / version.NAME).write_text("v9.9.9\n", encoding="utf-8")
|
||||
monkeypatch.setattr(version.sys, "frozen", True, raising=False)
|
||||
monkeypatch.setattr(version.sys, "_MEIPASS", str(tmp_path), raising=False)
|
||||
|
||||
assert version.current() == "v9.9.9"
|
||||
|
||||
|
||||
def test_binarka_se_gitu_nepta(monkeypatch, tmp_path):
|
||||
"""Na cizím počítači žádný repozitář není a ptát se nemá koho."""
|
||||
monkeypatch.setattr(version.sys, "frozen", True, raising=False)
|
||||
monkeypatch.setattr(version.sys, "_MEIPASS", str(tmp_path), raising=False)
|
||||
monkeypatch.setattr(version, "described",
|
||||
lambda: pytest.fail("binárka se ptala gitu"))
|
||||
|
||||
assert version.current() == version.UNKNOWN
|
||||
|
||||
|
||||
def test_ze_zdrojaku_ma_prednost_git(monkeypatch):
|
||||
monkeypatch.delattr(version.sys, "frozen", raising=False)
|
||||
monkeypatch.setattr(version, "described", lambda: "v1.2.3-4-gabcdef")
|
||||
monkeypatch.setattr(version, "bundled", lambda: "v1.0.0")
|
||||
|
||||
assert version.current() == "v1.2.3-4-gabcdef"
|
||||
|
||||
|
||||
def test_bez_gitu_a_bez_souboru_zbyde_neznama(monkeypatch):
|
||||
monkeypatch.delattr(version.sys, "frozen", raising=False)
|
||||
monkeypatch.setattr(version, "described", lambda: "")
|
||||
monkeypatch.setattr(version, "bundled", lambda: "")
|
||||
|
||||
assert version.current() == version.UNKNOWN
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
"""Která verze počitadla to vlastně je.
|
||||
|
||||
Venku jsou čtyři binárky pro tři systémy a k tomu zdrojáky. Když někdo
|
||||
napíše, že mu něco nefunguje, je první otázka vždycky stejná — a bez
|
||||
`--version` na ni nemá jak odpovědět.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
NAME = "verze.txt"
|
||||
UNKNOWN = "neznámá"
|
||||
|
||||
|
||||
def bundled() -> str:
|
||||
"""Verze vypálená do binárky při stavbě.
|
||||
|
||||
`build.sh` ji zapíše do souboru, `kostky.spec` ji přibalí. V binárce
|
||||
leží vedle šablon v dočasném adresáři, na který ukazuje `sys._MEIPASS`.
|
||||
"""
|
||||
root = Path(getattr(sys, "_MEIPASS", Path(__file__).parent))
|
||||
try:
|
||||
return (root / NAME).read_text(encoding="utf-8").strip()
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
|
||||
def described() -> str:
|
||||
"""Verze podle gitu. Ze zdrojáků je to přesnější než cokoli zapsaného."""
|
||||
try:
|
||||
done = subprocess.run(
|
||||
["git", "describe", "--tags", "--always", "--dirty"],
|
||||
cwd=Path(__file__).parent, capture_output=True, text=True,
|
||||
timeout=5, check=True)
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
return ""
|
||||
|
||||
return done.stdout.strip()
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def current() -> str:
|
||||
"""Verze pro výpis. Ptát se gitu má smysl jen mimo binárku."""
|
||||
if getattr(sys, "frozen", False):
|
||||
return bundled() or UNKNOWN
|
||||
|
||||
return described() or bundled() or UNKNOWN
|
||||
@@ -22,6 +22,7 @@ import console
|
||||
import net
|
||||
import stats
|
||||
import storage
|
||||
import version
|
||||
from core import FINAL_SCORE, Game, GameOver
|
||||
|
||||
def bundled(folder: str) -> str:
|
||||
@@ -125,6 +126,12 @@ def qr_svg(data: str) -> Markup:
|
||||
)
|
||||
|
||||
|
||||
@app.context_processor
|
||||
def verze():
|
||||
"""Verze do patičky. Když někdo hlásí chybu, tohle je první otázka."""
|
||||
return {"verze": version.current()}
|
||||
|
||||
|
||||
@app.template_filter("datum")
|
||||
def datum(value: str) -> str:
|
||||
"""Z ISO textu udělá 12. 8. 2026."""
|
||||
@@ -320,8 +327,9 @@ def hall():
|
||||
games=storage.recent_games(conn))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
console.utf8()
|
||||
def announce() -> None:
|
||||
"""Vypíše, kde všude je počitadlo k mání, a čím je zrovna spuštěné."""
|
||||
print(f"Kostky {version.current()}\n")
|
||||
|
||||
found = net.addresses()
|
||||
|
||||
@@ -336,4 +344,13 @@ if __name__ == "__main__":
|
||||
|
||||
print(f"\nDatabáze: {storage.DB_PATH.resolve()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
console.utf8()
|
||||
|
||||
if {"--version", "-V"} & set(sys.argv[1:]):
|
||||
print(f"kostky {version.current()}")
|
||||
sys.exit(0)
|
||||
|
||||
announce()
|
||||
app.run(host="0.0.0.0", port=PORT, threaded=True)
|
||||
Reference in new issue
Block a user