# Packaging Standalone executables are built with [PyInstaller] from a single cross-platform spec: `packaging/neotalk.spec`. > PyInstaller does **not** cross-compile: a macOS bundle must be built on macOS > and a Windows executable on Windows. ## macOS (`.app`) ```sh uv run --extra build pyinstaller \ --noconfirm --clean \ --distpath dist --workpath build/pyinstaller \ packaging/neotalk.spec ``` Result: `dist/neotalk.app`. ## Windows (`.exe`) On a Windows machine with Python and [uv]: ```powershell uv run --extra build pyinstaller ` --noconfirm --clean ` --distpath dist --workpath build/pyinstaller ` packaging/neotalk.spec ``` Result: `dist/neotalk.exe` (a single self-contained file). The Windows binary can also be produced in CI: see `.gitea/workflows/build-windows.yaml` (requires a Windows runner). ## Icons `assets/icon.svg` is the source. Regenerate `icon.icns` / `icon.ico` with `packaging/make-icons.sh`. The spec embeds them automatically. ## Signing, notarizing and releasing (macOS) - `sign-macos.sh` signs a built `neotalk.app` with the Developer ID under the hardened runtime (`entitlements.plist`). - `release-macos.sh ` builds, signs, notarizes, staples and optionally uploads the `.zip` to the Gitea releases page. Credentials come from the environment (never committed): ```sh # one-time: store notarization credentials in the keychain xcrun notarytool store-credentials neotalk \ --apple-id you@example.com --team-id M3K3T47KXF --password APP-SPECIFIC-PW export NEOTALK_NOTARY_PROFILE=neotalk export NEOTALK_GITEA_TOKEN=... # optional, to publish the release ./packaging/release-macos.sh v0.1.0 ``` Signing alone is not enough for a smooth download: an un-notarized app is still blocked by Gatekeeper. Notarization is what lets a friend open it normally. [PyInstaller]: https://pyinstaller.org/ [uv]: https://docs.astral.sh/uv/