Binárky pro Windows a macOS přes GitHub Actions
binárka / linux (pull_request) Successful in 11s
testy / pytest (3.11) (pull_request) Successful in 1s
testy / pytest (3.12) (pull_request) Successful in 1s
testy / pytest (3.13) (pull_request) Successful in 1s
testy / pytest (3.14) (pull_request) Successful in 0s

PyInstaller neumí křížovou kompilaci — binárku pro každý systém musí
postavit ten systém. Vlastní runner pro Windows a hlavně pro macOS by byl
nepoměr, tak si tyhle stroje půjčujeme od GitHub Actions.

GitHub je jen půjčená dílna, ne druhý domov projektu: repozitář se tam
z Gitey zrcadlí, hotové soubory se posílají zpátky na zdejší release přes
Gitea API a stránka s releasy zůstává jedna. Linux se tam schválně
nestaví; doma to jde proti glibc 2.17 a je to zároveň pojistka, kdyby
GitHub vypadl.

build.sh je společný pro všechny tři systémy, liší se jen tím, že Windows
dává spustitelné soubory venvu do Scripts/ místo bin/. Ořezání symbolů
zůstává jen Linuxu: na macOS by rozbilo podpis, který si PyInstaller sám
přidává a bez kterého se binárka na Apple Silicon vůbec nespustí.

Zkouška po stavbě je stejná jako u linuxové: nastartovat, stáhnout si
stránku i styly a ověřit, že se založila databáze.

Nepodepsané binárky si Gatekeeper ani SmartScreen nenechají líbit, tak je
v README napsané, co s tím — certifikáty za tisíce ročně by na počitadlo
kostek byly nesmysl.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
gitea-actionsandClaude Opus 5 committed 2026-08-22 13:13:57 +02:00
1 parent 26338bfa29
commit a7621bb01b
4 files changed
+219 -26

No files matched your search

+124
View File
@@ -0,0 +1,124 @@
# Binárky pro Windows a macOS.
#
# PyInstaller neumí křížovou kompilaci: binárku pro každý systém musí postavit
# ten systém. Kupovat kvůli tomu Mac nebo krmit Windows VM by bylo přehnané,
# tak si tyhle stroje půjčujeme od GitHub Actions.
#
# GitHub je tu jen půjčená dílna, ne druhý domov projektu: repozitář se sem
# z Gitey zrcadlí a hotové soubory se posílají zpátky na tamní release přes
# Gitea API. Jedna stránka s releasy, ta původní.
#
# Linux se tu schválně nestaví — o ten se stará vlastní runner
# (.gitea/workflows/binarka.yml) a dělá to líp, proti glibc 2.17.
name: binárky
on:
push:
branches: [main]
tags: ["v*"]
workflow_dispatch:
env:
GITEA: https://gitea.spacilovi.eu
GITEA_REPO: david-spacil/dice-counter
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
asset: kostky-windows-x86_64.exe
built: dist/kostky.exe
- os: macos-13 # poslední Intel běhoun
asset: kostky-macos-x86_64
built: dist/kostky
- os: macos-latest # Apple Silicon
asset: kostky-macos-arm64
built: dist/kostky
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10
- name: Testy
run: uv run --with-requirements requirements.txt --no-project pytest -q
- name: Binárka
run: ./build.sh
- name: Zkouška
run: |
# Binárka se musí umět rozbalit, najít šablony a začít poslouchat.
# Adresa se vnucuje, ať zkouška nezávisí na síti běhounu.
export DICE_HOST=127.0.0.1 DICE_PORT=8123 DICE_DB=zkouska/dice.db
"${{ matrix.built }}" &
for _ in $(seq 30); do
curl -sf -o /dev/null "http://127.0.0.1:8123/" && break
sleep 1
done
curl -sf -o /dev/null "http://127.0.0.1:8123/" || {
echo "Server se nerozeběhl." >&2; exit 1; }
curl -sf -o /dev/null "http://127.0.0.1:8123/static/style.css"
curl -sf "http://127.0.0.1:8123/board" | grep -q "127.0.0.1:8123"
test -f zkouska/dice.db
echo "Binárka jede."
- name: Na release
if: startsWith(github.ref, 'refs/tags/')
env:
TOKEN: ${{ secrets.GITEA_TOKEN }}
ASSET: ${{ matrix.asset }}
run: |
api="$GITEA/api/v1/repos/$GITEA_REPO"
auth="Authorization: token $TOKEN"
json() { uv run --no-project python -c "$@"; }
# Release u tagu být může, ale nemusí — dá se založit i ručně a
# sahají na něj čtyři buildy naráz, tak se po neúspěchu ptáme znovu.
for _ in 1 2 3; do
id=$(curl -s -H "$auth" "$api/releases/tags/${GITHUB_REF_NAME}" \
| json 'import json,sys; print(json.load(sys.stdin).get("id",""))')
[ -n "$id" ] && break
curl -s -o /dev/null -X POST -H "$auth" -H "Content-Type: application/json" \
-d "{\"tag_name\":\"${GITHUB_REF_NAME}\",\"name\":\"${GITHUB_REF_NAME}\"}" \
"$api/releases"
done
[ -n "$id" ] || { echo "Release ${GITHUB_REF_NAME} se nepodařilo najít." >&2; exit 1; }
# macOS zná jen shasum, Git Bash na Windows jen sha256sum. Formát
# výsledku je stejný, takže se dá ověřit kterýmkoli z nich.
if command -v sha256sum >/dev/null; then
soucet() { sha256sum "$@"; }
else
soucet() { shasum -a 256 "$@"; }
fi
# Součet se počítá nad jménem, pod kterým se soubor stahuje, aby
# šel ověřit rovnou: shasum -a 256 -c kostky-macos-arm64.sha256
cp "${{ matrix.built }}" "$ASSET"
soucet "$ASSET" | tee "$ASSET.sha256"
for file in "$ASSET" "$ASSET.sha256"; do
# Aby se dal build zopakovat, stará příloha stejného jména jde pryč.
curl -s -H "$auth" "$api/releases/$id/assets" \
| json 'import json,sys; [print(a["id"]) for a in json.load(sys.stdin) if a["name"] == sys.argv[1]]' "$file" \
| while read -r old; do
curl -sf -X DELETE -H "$auth" "$api/releases/$id/assets/$old"
done
curl -sf -o /dev/null -X POST -H "$auth" -F "attachment=@$file" \
"$api/releases/$id/assets?name=$file"
echo "$file pověšen na ${GITHUB_REF_NAME}"
done
+67 -16
View File
@@ -8,16 +8,45 @@ ten samý Adam jako z minulého měsíce. Žádné účty, žádná hesla.
## Spuštění ## Spuštění
Stáhni binárku z [releases](https://gitea.spacilovi.eu/david-spacil/dice-counter/releases) Stáhni binárku pro svůj systém z
a spusť ji: [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 ```bash
chmod +x kostky chmod +x kostky-*
./kostky ./kostky-linux-x86_64
``` ```
Nic se neinstaluje — Python, Flask i šablony jsou uvnitř. Zatím jen pro Linux Na Windows stačí na `.exe` poklepat.
na x86-64; postavené proti glibc 2.17, takže jede prakticky všude.
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é: Ze zdrojáků to je stejně krátké:
@@ -53,7 +82,9 @@ server vypíše při startu.
| Spuštěno | Databáze | | Spuštěno | Databáze |
|---|---| |---|---|
| binárkou | `~/.local/share/kostky/dice.db` | | 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 | | 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 Binárka se při každém spuštění rozbaluje do dočasného adresáře a pouští se
@@ -101,6 +132,8 @@ při remíze na prvním místě se hraje dál.
| `web.py` | Flask aplikace | | `web.py` | Flask aplikace |
| `dice.py` | totéž v terminálu, bez ukládání | | `dice.py` | totéž v terminálu, bez ukládání |
| `build.sh`, `kostky.spec` | stavba 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: Terminálová verze zůstává funkční jako záloha:
@@ -135,26 +168,44 @@ 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 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, 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 ne dopřednou. Nic z hostitele se do binárky nedostane, takže kontejner k tomu
potřeba není. Výsledek je v `dist/kostky`. 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ů. Terminálová verze součástí binárky není; ta se pouští ze zdrojáků.
Releasy si tohle dělají samy. Otagovaný commit spustí ### Jak vznikají releasy
`.gitea/workflows/binarka.yml`, ta binárku postaví, projede testy, zkusí
ji nastartovat a pověsí ji na release jako `kostky-linux-x86_64`: 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 ```bash
git tag v1.0.0 && git push origin v1.0.0 git tag v1.0.0 && git push origin v1.0.0
``` ```
Vedle binárky se věší i `kostky-linux-x86_64.sha256`, takže se stažený GitHub je tu jen půjčená dílna. Repozitář se tam z Gitey zrcadlí
soubor dá ověřit: ([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 ```bash
sha256sum -c kostky-linux-x86_64.sha256 sha256sum -c kostky-linux-x86_64.sha256 # na macOS: shasum -a 256 -c
``` ```
Stejná stavba běží i nad každým pull requestem, jen se nikam nevěší. Nad pull requesty se Linux staví taky, jen se nikam nevěší.
## Testy ## Testy
@@ -165,5 +216,5 @@ uv run --with-requirements requirements.txt --no-project pytest
Nebo z připraveného prostředí prostě `pytest`. Nebo z připraveného prostředí prostě `pytest`.
Nad každým pull requestem a nad `main` běží `.gitea/workflows/testy.yml` — Nad každým pull requestem a nad `main` běží `.gitea/workflows/testy.yml` —
84 testů na Pythonu 3.11, 3.12, 3.13 i 3.14, a k tomu skriptovaná partie 99 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á. v terminálové verzi, na kterou pytest nesahá.
+22 -9
View File
@@ -1,11 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Postaví jednosouborovou binárku pro Linux do dist/kostky. # Postaví jednosouborovou binárku do dist/ — pro systém, na kterém běží.
# #
# Staví se proti samostatnému CPythonu od uv, ne proti systémovému. Ten je # PyInstaller neumí křížovou kompilaci: binárku pro každý systém musí postavit
# slinkovaný s glibc 2.17, takže binárka jede i na letitých systémech. # ten systém. Linux se staví doma na vlastním runneru, Windows a macOS na
# Postavená proti Pythonu z Fedory 44 by chtěla glibc 2.43 a nešla by # půjčených strojích GitHub Actions (.github/workflows/binarky.yml).
# spustit skoro nikde — glibc drží zpětnou kompatibilitu, ne dopřednou. #
# Nic z hostitele se do ní nedostane, takže kontejner k tomu není potřeba. # Staví se proti samostatnému CPythonu od uv, ne proti systémovému. Na Linuxu
# je slinkovaný s glibc 2.17, takže binárka jede i na letitých systémech.
# Postavená proti Pythonu z Fedory 44 by chtěla glibc 2.43 a nešla by spustit
# skoro nikde — glibc drží zpětnou kompatibilitu, ne dopřednou. Nic z hostitele
# se do ní nedostane, takže kontejner k tomu není potřeba.
set -euo pipefail set -euo pipefail
VERSION=${VERSION:-3.11} VERSION=${VERSION:-3.11}
@@ -13,10 +17,19 @@ BUILD=${BUILD:-.build}
uv python install "$VERSION" uv python install "$VERSION"
uv venv --quiet --managed-python --python "$VERSION" "$BUILD" uv venv --quiet --managed-python --python "$VERSION" "$BUILD"
uv pip install --quiet --python "$BUILD/bin/python" -r requirements.txt pyinstaller
"$BUILD/bin/pyinstaller" --clean --noconfirm \ # Windows dává spustitelné soubory venvu do Scripts/, zbytek světa do bin/.
python="$BUILD/bin/python"
pyinstaller="$BUILD/bin/pyinstaller"
if [ -d "$BUILD/Scripts" ]; then
python="$BUILD/Scripts/python.exe"
pyinstaller="$BUILD/Scripts/pyinstaller.exe"
fi
uv pip install --quiet --python "$python" -r requirements.txt pyinstaller
"$pyinstaller" --clean --noconfirm \
--distpath dist --workpath "$BUILD/work" kostky.spec --distpath dist --workpath "$BUILD/work" kostky.spec
echo echo
ls -lh dist/kostky ls -lh dist/
+6 -1
View File
@@ -5,6 +5,8 @@ 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. i na starších systémech, než je ten tvůj.
""" """
import sys
analysis = Analysis( analysis = Analysis(
["web.py"], ["web.py"],
pathex=["."], pathex=["."],
@@ -21,6 +23,9 @@ exe = EXE(
analysis.datas, analysis.datas,
name="kostky", name="kostky",
console=True, console=True,
strip=True, # Ořezání symbolů šetří pár megabajtů, ale jen na Linuxu. Na macOS by
# rozbilo podpis, který si PyInstaller sám přidává a bez kterého se
# binárka na Apple Silicon vůbec nespustí; na Windows nedělá nic.
strip=sys.platform == "linux",
upx=False, upx=False,
) )