mirror of
https://github.com/Nighthawk42/bnet_auth_tool.git
synced 2026-08-30 06:22:27 +00:00
Add PyInstaller/flet-pack build tooling for standalone exes (v2.0.0-alpha)
Add packaging/ with entry scripts and build_exes.py: the CLI is frozen with PyInstaller (one-file console) and the GUI with `flet pack` (PyInstaller plus the bundled Flet desktop runtime). Both bundle settings.yaml as data so the frozen binaries load their defaults via importlib.resources, and the script zips the results into release/ for upload. Bump version to 2.0.0a0, gitignore build artifacts (*.exe, *.zip, *.spec, dist/build/release), add a `build` extra, and document pre-built binaries and the build process in the README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6580432070
commit
c0a5dc49bf
+10
@@ -27,6 +27,16 @@ dist/
|
||||
wheels/
|
||||
*.egg
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# PyInstaller / flet pack build artifacts
|
||||
# ---------------------------------------------------------------------------
|
||||
*.spec
|
||||
*.exe
|
||||
*.zip
|
||||
/release/
|
||||
# flet pack scratch dirs
|
||||
*.flet
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Virtual environments / tooling
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -54,6 +54,12 @@ authenticator secrets online, and — most importantly — keep your TOTP backup
|
||||
|
||||
## Install
|
||||
|
||||
### Pre-built Windows executables
|
||||
|
||||
Grab `bnet-auth.exe` (CLI) and `bnet-auth-gui.exe` (GUI) from the
|
||||
[Releases page](https://github.com/Nighthawk42/bnet_auth_tool/releases) — no Python needed.
|
||||
Both share the same encrypted vault.
|
||||
|
||||
### With [uv](https://docs.astral.sh/uv/) (recommended)
|
||||
|
||||
```bash
|
||||
@@ -135,6 +141,18 @@ uv run ruff check . # lint
|
||||
|
||||
See [`CLAUDE.md`](CLAUDE.md) / [`AGENTS.md`](AGENTS.md) for the architecture overview.
|
||||
|
||||
### Building the executables
|
||||
|
||||
```bash
|
||||
uv pip install -e ".[build]"
|
||||
python packaging/build_exes.py # both exes -> dist/, zip -> release/
|
||||
python packaging/build_exes.py --skip-gui # CLI only
|
||||
```
|
||||
|
||||
The CLI is frozen with PyInstaller and the GUI with `flet pack` (PyInstaller plus the
|
||||
bundled Flet desktop runtime). Build outputs (`dist/`, `build/`, `release/`, `*.exe`,
|
||||
`*.zip`, `*.spec`) are gitignored.
|
||||
|
||||
## Account recovery
|
||||
|
||||
This project has **zero** association with Blizzard and no access to their backend. If you
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
"""Build standalone Windows executables for the CLI and GUI.
|
||||
|
||||
* CLI -> PyInstaller (one-file, console).
|
||||
* GUI -> ``flet pack`` (PyInstaller under the hood, but with the Flet desktop
|
||||
runtime bundled — plain PyInstaller cannot ship a working Flet app).
|
||||
|
||||
Both bundle the package's ``settings.yaml`` as data so the frozen binaries can
|
||||
read their default configuration via ``importlib.resources``.
|
||||
|
||||
Usage (from the repo root, in an env with the ``build`` extra installed)::
|
||||
|
||||
uv pip install -e ".[build]"
|
||||
python packaging/build_exes.py # build both + zip
|
||||
python packaging/build_exes.py --skip-gui # CLI only
|
||||
python packaging/build_exes.py --no-zip # don't produce the release zip
|
||||
|
||||
Artifacts land in ``dist/`` and the zip in ``release/``. All of that is
|
||||
gitignored.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
PKG_DIR = REPO_ROOT / "src" / "bnet_auth_tool"
|
||||
SETTINGS = PKG_DIR / "settings.yaml"
|
||||
DIST = REPO_ROOT / "dist"
|
||||
# `flet pack` wipes its --distpath, so give the GUI its own and copy the result
|
||||
# into dist/ afterwards. That way CLI and GUI builds never clobber each other.
|
||||
GUI_DIST = REPO_ROOT / "build" / "gui-dist"
|
||||
RELEASE = REPO_ROOT / "release"
|
||||
ENTRY_CLI = REPO_ROOT / "packaging" / "pyi_entry_cli.py"
|
||||
ENTRY_GUI = REPO_ROOT / "packaging" / "pyi_entry_gui.py"
|
||||
|
||||
CLI_NAME = "bnet-auth"
|
||||
GUI_NAME = "bnet-auth-gui"
|
||||
DATA_SEP = ";" if os.name == "nt" else ":"
|
||||
EXE_SUFFIX = ".exe" if os.name == "nt" else ""
|
||||
|
||||
|
||||
def _version() -> str:
|
||||
sys.path.insert(0, str(REPO_ROOT / "src"))
|
||||
from bnet_auth_tool import __version__
|
||||
|
||||
return __version__
|
||||
|
||||
|
||||
def _release_label(version: str) -> str:
|
||||
# PEP 440 "2.0.0a0" -> human "2.0.0-alpha" for tags/zip names.
|
||||
return version.replace("a0", "-alpha").replace("b0", "-beta").replace("rc0", "-rc")
|
||||
|
||||
|
||||
def _run(cmd: list[str]) -> None:
|
||||
print(f"\n$ {' '.join(cmd)}\n", flush=True)
|
||||
subprocess.run(cmd, check=True, cwd=REPO_ROOT)
|
||||
|
||||
|
||||
def build_cli() -> Path:
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"PyInstaller",
|
||||
"--onefile",
|
||||
"--console",
|
||||
"--clean",
|
||||
"--noconfirm",
|
||||
"--name",
|
||||
CLI_NAME,
|
||||
"--add-data",
|
||||
f"{SETTINGS}{DATA_SEP}bnet_auth_tool",
|
||||
str(ENTRY_CLI),
|
||||
]
|
||||
)
|
||||
return DIST / f"{CLI_NAME}{EXE_SUFFIX}"
|
||||
|
||||
|
||||
def build_gui() -> Path:
|
||||
# `flet pack` wraps PyInstaller and bundles the Flet desktop runtime.
|
||||
# Its --add-data uses "source:destination"; pass a *relative* source so the
|
||||
# drive-letter colon in an absolute Windows path doesn't confuse the parser.
|
||||
settings_rel = SETTINGS.relative_to(REPO_ROOT).as_posix()
|
||||
_run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"flet.cli",
|
||||
"pack",
|
||||
"-y", # don't prompt to delete the build dir
|
||||
str(ENTRY_GUI),
|
||||
"--name",
|
||||
GUI_NAME,
|
||||
"--distpath",
|
||||
str(GUI_DIST),
|
||||
"--add-data",
|
||||
f"{settings_rel}:bnet_auth_tool",
|
||||
]
|
||||
)
|
||||
# Copy the GUI exe into the shared dist/ so it sits next to the CLI exe.
|
||||
built = GUI_DIST / f"{GUI_NAME}{EXE_SUFFIX}"
|
||||
DIST.mkdir(exist_ok=True)
|
||||
dest = DIST / f"{GUI_NAME}{EXE_SUFFIX}"
|
||||
if built.exists():
|
||||
shutil.copy2(built, dest)
|
||||
return dest
|
||||
|
||||
|
||||
def make_zip(exes: list[Path], version: str) -> Path:
|
||||
RELEASE.mkdir(exist_ok=True)
|
||||
label = _release_label(version)
|
||||
plat = "windows-x64" if os.name == "nt" else os.name
|
||||
zip_path = RELEASE / f"bnet-auth-tool-{label}-{plat}.zip"
|
||||
|
||||
readme = (
|
||||
f"Battle.net Authenticator Tool {label}\n"
|
||||
f"{'=' * 40}\n\n"
|
||||
"bnet-auth.exe - command-line interface (run in a terminal)\n"
|
||||
"bnet-auth-gui.exe - desktop GUI (double-click)\n\n"
|
||||
"Both share one encrypted vault in your user data dir. ALPHA build:\n"
|
||||
"online attach/retrieve is unverified against Blizzard's API; the\n"
|
||||
"offline vault/TOTP/migration features are the supported core.\n\n"
|
||||
"Source & docs: https://github.com/Nighthawk42/bnet_auth_tool\n"
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for exe in exes:
|
||||
zf.write(exe, exe.name)
|
||||
zf.writestr("README.txt", readme)
|
||||
return zip_path
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Build CLI/GUI executables.")
|
||||
parser.add_argument("--skip-gui", action="store_true", help="build the CLI only")
|
||||
parser.add_argument("--skip-cli", action="store_true", help="build the GUI only")
|
||||
parser.add_argument("--no-zip", action="store_true", help="skip the release zip")
|
||||
args = parser.parse_args()
|
||||
|
||||
version = _version()
|
||||
print(f"Building bnet-auth-tool {version} ({_release_label(version)})")
|
||||
|
||||
exes: list[Path] = []
|
||||
if not args.skip_cli:
|
||||
exes.append(build_cli())
|
||||
if not args.skip_gui:
|
||||
exes.append(build_gui())
|
||||
|
||||
missing = [str(p) for p in exes if not p.exists()]
|
||||
if missing:
|
||||
print(f"\nERROR: expected artifacts not found: {missing}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
print("\nBuilt:")
|
||||
for exe in exes:
|
||||
print(f" {exe} ({exe.stat().st_size / 1_000_000:.1f} MB)")
|
||||
|
||||
if not args.no_zip and exes:
|
||||
zip_path = make_zip(exes, version)
|
||||
print(f"\nPackaged: {zip_path}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,12 @@
|
||||
"""PyInstaller entry point for the CLI.
|
||||
|
||||
A standalone script (not a module) so PyInstaller has a concrete file to freeze;
|
||||
it simply imports and runs the installed package's CLI entry point.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from bnet_auth_tool.cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,10 @@
|
||||
"""PyInstaller / flet-pack entry point for the GUI.
|
||||
|
||||
A standalone script so the packager has a concrete file to freeze; it imports
|
||||
and runs the installed package's GUI entry point.
|
||||
"""
|
||||
|
||||
from bnet_auth_tool.gui import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+5
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "bnet-auth-tool"
|
||||
version = "2.0.0"
|
||||
version = "2.0.0a0"
|
||||
description = "Manage Battle.net software authenticators (CLI + optional GUI): attach/retrieve secrets and back up TOTP keys in an encrypted local vault."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
@@ -38,6 +38,10 @@ dev = [
|
||||
"pytest>=8.0",
|
||||
"ruff>=0.5",
|
||||
]
|
||||
build = [
|
||||
"flet[all]>=0.28",
|
||||
"pyinstaller>=6.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/Nighthawk42/bnet_auth_tool"
|
||||
|
||||
@@ -4,7 +4,7 @@ Attach/retrieve Battle.net authenticator secrets (online) and manage TOTP
|
||||
backups in an encrypted local vault (offline). See :mod:`bnet_auth_tool.cli`.
|
||||
"""
|
||||
|
||||
__version__ = "2.0.0"
|
||||
__version__ = "2.0.0a0"
|
||||
__author__ = "Nighthawk42"
|
||||
__license__ = "MIT"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user