Cross-OS packaging: build orchestration, PyInstaller/py2app, CI matrix - #43
Open
Phantom-VK wants to merge 12 commits into
Open
Cross-OS packaging: build orchestration, PyInstaller/py2app, CI matrix#43Phantom-VK wants to merge 12 commits into
Phantom-VK wants to merge 12 commits into
Conversation
Bundles the built frontend, drops the customtkinter/PIL._tkinter_finder Tk leftovers and the CTk-only icon assets, and excludes every unused pywebview backend so an unrelated Qt install on the build machine can't silently double the bundle. Two real bugs surfaced by actually launching the frozen Linux build, not just compiling it: - PyGObject looks up gi.overrides.<Module> by name for every gi.repository import (the same "discovered at runtime, invisible to static analysis" problem tiktoken_ext already needed a hiddenimport for). Without it, `import gi.repository.Gtk` silently succeeds without actually initializing anything, and Gdk.Display.get_default() returns None instead of a real display -- the app launches clean and crashes the instant it touches the screen list. - PyInstaller's own gi runtime hook points GI_TYPELIB_PATH at the frozen bundle's gi_typelibs/ dir unconditionally, even when its build-time hook found nothing to put there, which hides the system's real typelibs instead of falling back to them. - A related split-brain: PyInstaller bundles libglib/libgobject/libgio (pulled in via PyGObject's compiled extension) without bundling libgtk/libgdk themselves, so the system's GTK ends up linked against a different glib copy than it was built against. Excluded the bundled copies so the whole stack comes from the system consistently. Verified by actually building and running dist/NoRefund/NoRefund on this machine: window opens, all six views render with real bundled config and frontend data, and PDF export (bundled reportlab font data) produces a valid file.
py2app is the pywebview-recommended path on macOS. Bundles the built frontend and config YAML (py2app's static analysis can't see either the same way PyInstaller's can't), requests no entitlements the app doesn't need, and targets macOS 12+. Written but not build-verified -- no macOS hardware available in this environment. Flagged in the phase tracking for a real-hardware build and ad-hoc-sign pass.
Covers build prerequisites, the build command, output location, and end-user runtime requirements for Linux/Windows/macOS, plus the two real GTK packaging bugs the Linux rewrite surfaced and how they were fixed. Verified the Linux missing-runtime failure path for real: installed the app into a virtualenv with no PyGObject at all and confirmed it prints the exact install-command message (matching missing_runtime_message()) and exits with code 2, not a traceback.
New build.yml: a lighter-weight, continuous companion to release.yml's tag-triggered builds. Runs on every PR and push to main across all three OSes -- frontend typecheck/test/build, pytest, ruff, then the actual frozen build via packaging/build.py, with a launch smoke test on Linux and Windows (macOS lacks an easy headless display to smoke-test against in CI, so it's build-only there for now). Also fixes release.yml, which the packaging rewrite silently broke: it built via a raw `pyinstaller packaging/norefund.spec` call that never built the frontend first (dist would have shipped a blank window) and never installed the `linux` extra pygobject needs. Both jobs now go through packaging/build.py instead.
…clean dist/ Local iterative rebuilds otherwise fail outright the moment dist/NoRefund/ already exists from a previous run.
… the CI matrix - pygobject has no prebuilt wheel for the CI runner's exact platform, so pip builds it from source, which needs pycairo's own build deps (libgirepository*-dev, libcairo2-dev, pkg-config, python3-dev) -- missing on a fresh ubuntu-latest runner even though none of them are needed at actual runtime. - pyproject.toml's empty author email fails py2app's stricter build-time metadata validation (hatchling itself never checked it). Dropped the empty field rather than inventing a value. - Bounded the Python test step to 8 minutes so a hang fails fast with a clear timeout instead of silently eating the whole job's budget. Windows CI still has a real, separate problem this run surfaced: the Python test step hung for 18+ minutes inside test_fit_check_view.py (legacy Tk gui/ tests), after two earlier failures in test_compare_view.py and test_desktop_dto.py with no visible traceback (the job was cancelled before pytest's summary printed). This is the first time any workflow has run the suite on Windows at all, so it predates this branch; not something fixable without real Windows access to reproduce and debug interactively.
Modern setuptools rejects the legacy install_requires/setup_requires
kwargs on a project that already declares its dependencies via
pyproject.toml's [project] table, which this one does -- CI's macos-latest
runner has a setuptools recent enough to make that a hard error
("install_requires is no longer supported") rather than a warning.
py2app itself is already guaranteed present via the macos pip extra
before this script runs, so setup_requires was never load-bearing.
…bility
py2app 0.28.9 made having any dependencies in pyproject.toml's [project]
table a hard build error ("install_requires is no longer supported"),
regardless of what the py2app-specific setup() call itself passes -- an
open upstream bug (ronaldoussoren/py2app#560), not something fixable from
this project's side.
py2app 0.28.8 still imports pkg_resources directly; setuptools removed it entirely in v82.0.0. The setuptools maintainers' own stated recommendation for anyone still needing it is to pin setuptools<81.
The desktop app now runs on the React/pywebview frontend; src/norefund/gui/ is unused reference code, so tests exercising its views only added Tk-display flakiness and dead fixture maintenance with no product coverage.
to_jsonable() correctly returns a Path's native string form, but the test hardcoded a posix-style literal, which only matched on Linux/macOS. Removing the legacy Tk suite let the Windows CI job actually finish pytest instead of hanging, which is what surfaced this.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
packaging/build.py: one command to produce a distributable build on any platform, always rebuilding the frontend first and asserting it's not stale.packaging/norefund.specto target the React/pywebview desktop app instead of the legacy CustomTkinter app, and fixed two real bugs found by actually launching the frozen Linux build (not just compiling it): a missing PyGObject override module that left GTK silently uninitialized, and a glib-library version conflict between bundled and system copies. Both are documented indocs/packaging.md.packaging/macos_setup.py(py2app configuration) and amacosextras group, including two version pins needed to work around currently-open upstream py2app/setuptools compatibility bugs.packaging/README.mdcovering build prerequisites, commands, output locations, and end-user runtime requirements per platform.emailfield inpyproject.tomlthat only py2app's stricter validation caught.Verified
test_fit_check_view.py) on Windows specifically, after two earlier test failures with no visible traceback. This is the first time any workflow has run the suite on Windows at all, so it predates this branch and isn't caused by the packaging changes here; it needs real Windows access to reproduce and debug interactively. Bounded with an 8-minute step timeout so it fails fast and visibly instead of eating the whole job.Test plan
python -m pytest -q(325 passing locally)ruff check src/cd frontend && npm run typecheck && npm run test && npm run build(97 vitest passing)python packaging/build.pyon Linux, then launched and interacted with the real frozen build (all six views, a real download, PDF export)