From 3af8fe22efbc7d5c26e8fc8c1e67af4fffcf7ab1 Mon Sep 17 00:00:00 2001 From: gitea-actions Date: Sat, 22 Aug 2026 18:22:35 +0200 Subject: [PATCH] =?UTF-8?q?Zamknout=20z=C3=A1vislosti?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bez zamčených verzí si každá stavba stáhne to, co je zrovna nejnovější: binárka z dneška a binárka z příštího jara obsahují něco jiného a není jak zjistit co. Zároveň může vydání kterékoli knihovny rozbít build, aniž by se sáhlo na řádku kódu — a nejspíš zrovna ve chvíli, kdy se tagne release. Volné seznamy jsou nově v `.in`, zamčené v `.txt`. Rozděleno na tři: běh, testy a stavba. Pytest se tak přestane instalovat do prostředí, kde se staví binárka, a PyInstaller má konečně taky zamčenou verzi — ta na podobu výsledku sahá ze všech nejvíc. Zůstává, že se verze píšou i v hlavičce PEP 723, jinak by přestalo fungovat `uv run web.py`. Aby se ta dvě místa nerozešla, hlídá je test. --- .gitea/workflows/binarka.yml | 2 +- .gitea/workflows/testy.yml | 2 +- .github/workflows/binarky.yml | 2 +- README.md | 26 ++++++++++++++- build.sh | 2 +- dice.py | 2 +- requirements-build.in | 4 +++ requirements-build.txt | 37 +++++++++++++++++++++ requirements-dev.in | 4 +++ requirements-dev.txt | 35 ++++++++++++++++++++ requirements.in | 6 ++++ requirements.txt | 29 ++++++++++++++--- tests/test_zavislosti.py | 60 +++++++++++++++++++++++++++++++++++ web.py | 2 +- 14 files changed, 202 insertions(+), 11 deletions(-) create mode 100644 requirements-build.in create mode 100644 requirements-build.txt create mode 100644 requirements-dev.in create mode 100644 requirements-dev.txt create mode 100644 requirements.in create mode 100644 tests/test_zavislosti.py diff --git a/.gitea/workflows/binarka.yml b/.gitea/workflows/binarka.yml index ae0b920..77b53eb 100644 --- a/.gitea/workflows/binarka.yml +++ b/.gitea/workflows/binarka.yml @@ -53,7 +53,7 @@ jobs: uv --version - name: Testy - run: uv run --with-requirements requirements.txt --no-project pytest -q + run: uv run --with-requirements requirements-dev.txt --no-project pytest -q - name: Binárka run: ./build.sh diff --git a/.gitea/workflows/testy.yml b/.gitea/workflows/testy.yml index 10d723c..eb7231e 100644 --- a/.gitea/workflows/testy.yml +++ b/.gitea/workflows/testy.yml @@ -49,7 +49,7 @@ jobs: - name: Testy run: | uv run --managed-python --python "${{ matrix.python }}" \ - --with-requirements requirements.txt --no-project \ + --with-requirements requirements-dev.txt --no-project \ pytest -q - name: Terminálová verze diff --git a/.github/workflows/binarky.yml b/.github/workflows/binarky.yml index f1a9aef..33f4b15 100644 --- a/.github/workflows/binarky.yml +++ b/.github/workflows/binarky.yml @@ -52,7 +52,7 @@ jobs: - uses: astral-sh/setup-uv@v10.0.1 - name: Testy - run: uv run --with-requirements requirements.txt --no-project pytest -q + run: uv run --with-requirements requirements-dev.txt --no-project pytest -q - name: Binárka run: ./build.sh diff --git a/README.md b/README.md index 4ad0c60..72d0dac 100644 --- a/README.md +++ b/README.md @@ -155,10 +155,34 @@ python -m venv .venv 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 +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 diff --git a/build.sh b/build.sh index ee0cf48..03b120a 100755 --- a/build.sh +++ b/build.sh @@ -26,7 +26,7 @@ if [ -d "$BUILD/Scripts" ]; then pyinstaller="$BUILD/Scripts/pyinstaller.exe" fi -uv pip install --quiet --python "$python" -r requirements.txt pyinstaller +uv pip install --quiet --python "$python" -r requirements-build.txt "$pyinstaller" --clean --noconfirm \ --distpath dist --workpath "$BUILD/work" kostky.spec diff --git a/dice.py b/dice.py index d05bf15..8135999 100644 --- a/dice.py +++ b/dice.py @@ -1,6 +1,6 @@ # /// script # requires-python = ">=3.11" -# dependencies = ["tabulate"] +# dependencies = ["tabulate==0.10.0"] # /// """Počitadlo skóre v terminálu. diff --git a/requirements-build.in b/requirements-build.in new file mode 100644 index 0000000..fffb682 --- /dev/null +++ b/requirements-build.in @@ -0,0 +1,4 @@ +# Co je potřeba ke stavbě binárky. Zamyká se do requirements-build.txt: +# uv pip compile requirements-build.in -o requirements-build.txt +-r requirements.in +pyinstaller diff --git a/requirements-build.txt b/requirements-build.txt new file mode 100644 index 0000000..903e786 --- /dev/null +++ b/requirements-build.txt @@ -0,0 +1,37 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements-build.in -o requirements-build.txt --python-version 3.11 +altgraph==0.17.5 + # via pyinstaller +blinker==1.9.0 + # via flask +click==8.4.2 + # via flask +flask==3.1.3 + # via -r requirements.in +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +markupsafe==3.0.3 + # via + # flask + # jinja2 + # werkzeug +packaging==26.3 + # via + # pyinstaller + # pyinstaller-hooks-contrib +pyinstaller==6.22.2 + # via -r requirements-build.in +pyinstaller-hooks-contrib==2026.6 + # via pyinstaller +qrcode==8.2 + # via -r requirements.in +setuptools==84.0.0 + # via pyinstaller +tabulate==0.10.0 + # via -r requirements.in +waitress==3.0.2 + # via -r requirements.in +werkzeug==3.1.8 + # via flask diff --git a/requirements-dev.in b/requirements-dev.in new file mode 100644 index 0000000..1e2d1dc --- /dev/null +++ b/requirements-dev.in @@ -0,0 +1,4 @@ +# Co je potřeba k vývoji a testům. Zamyká se do requirements-dev.txt: +# uv pip compile requirements-dev.in -o requirements-dev.txt +-r requirements.in +pytest diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..49187f9 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,35 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile requirements-dev.in -o requirements-dev.txt --python-version 3.11 +blinker==1.9.0 + # via flask +click==8.4.2 + # via flask +flask==3.1.3 + # via -r requirements.in +iniconfig==2.3.0 + # via pytest +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +markupsafe==3.0.3 + # via + # flask + # jinja2 + # werkzeug +packaging==26.3 + # via pytest +pluggy==1.6.0 + # via pytest +pygments==2.21.0 + # via pytest +pytest==9.1.1 + # via -r requirements-dev.in +qrcode==8.2 + # via -r requirements.in +tabulate==0.10.0 + # via -r requirements.in +waitress==3.0.2 + # via -r requirements.in +werkzeug==3.1.8 + # via flask diff --git a/requirements.in b/requirements.in new file mode 100644 index 0000000..90f9743 --- /dev/null +++ b/requirements.in @@ -0,0 +1,6 @@ +# Běhové závislosti. Verze se zamykají do requirements.txt přes: +# uv pip compile requirements.in -o requirements.txt +flask +qrcode +tabulate +waitress diff --git a/requirements.txt b/requirements.txt index f017b4d..68ed760 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,25 @@ -flask -qrcode -tabulate -pytest +# This file was autogenerated by uv via the following command: +# uv pip compile requirements.in -o requirements.txt --python-version 3.11 +blinker==1.9.0 + # via flask +click==8.4.2 + # via flask +flask==3.1.3 + # via -r requirements.in +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +markupsafe==3.0.3 + # via + # flask + # jinja2 + # werkzeug +qrcode==8.2 + # via -r requirements.in +tabulate==0.10.0 + # via -r requirements.in +waitress==3.0.2 + # via -r requirements.in +werkzeug==3.1.8 + # via flask diff --git a/tests/test_zavislosti.py b/tests/test_zavislosti.py new file mode 100644 index 0000000..505aa93 --- /dev/null +++ b/tests/test_zavislosti.py @@ -0,0 +1,60 @@ +"""Závislosti jsou zamčené a všechna místa se shodnou. + +Verze se píšou na dvou místech: v `requirements.txt` pro stavbu binárky +a v hlavičce PEP 723 pro `uv run web.py`. Ta dvě místa se dřív nebo později +rozejdou — leda že by to někdo hlídal. Proto tenhle soubor. +""" + +import re +from pathlib import Path + +import pytest + +ROOT = Path(__file__).parent.parent + +# Skript a to, co si podle hlavičky nese s sebou. +SCRIPTS = {"web.py": {"flask", "qrcode", "waitress"}, + "dice.py": {"tabulate"}} + + +def pins(name: str) -> dict[str, str]: + """Zamčené verze z compilovaného requirements souboru.""" + text = (ROOT / name).read_text(encoding="utf-8") + return {m.group(1).lower(): m.group(2) + for m in re.finditer(r"^([A-Za-z0-9_.\-]+)==(\S+)", text, re.M)} + + +def header(script: str) -> dict[str, str]: + """Závislosti z hlavičky PEP 723 na začátku skriptu.""" + text = (ROOT / script).read_text(encoding="utf-8") + line = re.search(r"^# dependencies = \[(.*)\]$", text, re.M) + assert line, f"{script} nemá hlavičku PEP 723" + + return {m.group(1).lower(): m.group(2) + for m in re.finditer(r'"([A-Za-z0-9_.\-]+)==(\S+?)"', line.group(1))} + + +@pytest.mark.parametrize("name", ["requirements.txt", "requirements-dev.txt", + "requirements-build.txt"]) +def test_zavislosti_jsou_zamcene(name): + """Bez zamčených verzí se binárka z března liší od binárky z dubna.""" + lines = [line.strip() for line in (ROOT / name).read_text().splitlines()] + volne = [line for line in lines + if line and not line.startswith("#") and "==" not in line] + + assert not volne, f"{name} má nezamčené závislosti: {volne}" + + +@pytest.mark.parametrize("script", sorted(SCRIPTS)) +def test_hlavicka_sedi_s_requirements(script): + """`uv run web.py` musí dostat totéž, co se zabalí do binárky.""" + zamcene = pins("requirements.txt") + v_hlavicce = header(script) + + assert set(v_hlavicce) == SCRIPTS[script] + + for balicek, verze in v_hlavicce.items(): + assert balicek in zamcene, f"{balicek} chybí v requirements.txt" + assert verze == zamcene[balicek], ( + f"{script}: {balicek}=={verze}, ale zamčeno je " + f"{zamcene[balicek]} — přepiš hlavičku") diff --git a/web.py b/web.py index 57faac7..1dbc687 100644 --- a/web.py +++ b/web.py @@ -1,6 +1,6 @@ # /// script # requires-python = ">=3.11" -# dependencies = ["flask", "qrcode"] +# dependencies = ["flask==3.1.3", "qrcode==8.2", "waitress==3.0.2"] # /// """Webové počitadlo pro domácí síť.