46. Packaging and distributing GUI apps
46.1 Fyne: fyne package
go install fyne.io/fyne/v2/cmd/fyne@latest
fyne package -os darwin -icon icon.png # produces a .app bundle
fyne package -os windows -icon icon.png # produces a .exe with an embedded icon
fyne package -os linux -icon icon.png # produces a portable Linux binary
As with Wails, pin the packaging tool version in CI once you've validated the release.
46.2 Wails: wails build
wails build -platform darwin/universal,windows/amd64,linux/amd64
Wails cross-compiling to Windows/Linux from macOS (and vice versa) requires the target platform's WebView runtime considerations — check Wails' current cross-build docs before assuming a single command covers every OS combination; native builds per-OS in CI remain the most reliable path for a real release pipeline.
46.3 Code signing and notarization (macOS), and why it matters
An unsigned macOS .app triggers Gatekeeper's "unidentified developer"
warning; unsigned Windows executables trigger SmartScreen warnings. For
anything beyond a personal tool, budget time for an Apple Developer ID
(codesign + notarytool) and a Windows code-signing certificate — this
is packaging friction that has nothing to do with Go itself but blocks
real-world distribution just as much as any code bug would.
46.4 Real-world example
The reason Wails apps and Electron apps both bundle noticeably larger binaries than a pure-Fyne app is the embedded web engine: Wails reuses the OS's system WebView (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux) rather than bundling its own Chromium like Electron — meaningfully smaller, but still a browser engine, versus Fyne/Gio's "just draw the pixels ourselves in Go" approach with no embedded browser at all.