-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclaimwright.spec
More file actions
152 lines (135 loc) · 5.29 KB
/
Copy pathclaimwright.spec
File metadata and controls
152 lines (135 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# -*- mode: python ; coding: utf-8 -*-
"""PyInstaller spec for the ClaimWright Claims desktop app.
Freezes ``launcher.py`` — a thin **native window onto the hosted site**
(``claimwright.up.railway.app``). The app is not a local server: it loads the live
site in a pywebview window, so it carries no Django backend, no SQLite, and no
bundled SPA. That keeps the bundle small and means the app is always whatever is
deployed — same auth, same database, same everything as the website.
Build (from the repo root, with the build extra installed)::
pip install -e ".[build]" # installs pyinstaller + pywebview + pillow
pyinstaller claimwright.spec --noconfirm
Output: ``dist/ClaimWright.app`` (macOS) / ``dist/claimwright`` (else). This spec is
cross-platform — run it on each target OS to produce that OS's binary
(PyInstaller does not cross-compile).
The only runtime files the app writes are its webview profile (cookies, so login
sticks) and a launch log, under ``core.config``'s per-OS user data dir. The site
URL is overridable at runtime via ``CLAIMWRIGHT_APP_URL``. See docs/PACKAGING.md.
"""
import os
import sys
from PyInstaller.utils.hooks import collect_submodules
from core.version import __version__
block_cipher = None
# --- Read-only bundled resources --------------------------------------------
# The shell needs nothing bundled: no rulebook, no SPA, no API templates. Those
# all live on the hosted site. `datas` stays empty on purpose.
datas = []
# --- App icon ---------------------------------------------------------------
# Generated from web/public/claimwright-logo.png by scripts/make_icons.py (build_app.py
# regenerates them). PyInstaller wants the OS-native container: .icns on macOS,
# .ico on Windows. Guard so a missing icon just yields a default-icon build
# rather than failing.
if sys.platform == "darwin":
_icon = os.path.join("build", "icons", "claimwright.icns")
elif sys.platform.startswith("win"):
_icon = os.path.join("build", "icons", "claimwright.ico")
else:
_icon = None
if _icon and not os.path.exists(_icon):
print(f"claimwright.spec: WARNING icon {_icon} missing — run scripts/make_icons.py.")
_icon = None
# --- Hidden imports ----------------------------------------------------------
# The launcher imports only `webview`, `core.config`, and `core.version`. We list
# exactly those two core modules (NOT collect_submodules("core"), which would drag
# in core.pipeline/adjudication and their heavy deps for no reason) and let
# PyInstaller follow their real imports. pydantic ships compiled internals that
# are imported dynamically, so collect those defensively. PyInstaller's own
# pywebview hook pulls the native backend (pyobjc/WebKit on macOS), so it's not
# listed here.
hiddenimports = [
"core.config",
"core.version",
"pydantic_settings",
]
hiddenimports += collect_submodules("pydantic")
a = Analysis(
["launcher.py"],
pathex=[],
binaries=[],
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
# Heavy / dev-only deps the shell never needs. The old build froze the whole
# Django+uvicorn+anthropic backend; the shell loads the hosted site instead,
# so exclude that stack to keep the bundle small even if something pulls it
# in transitively.
excludes=[
"pytest",
"_pytest",
"django",
"ninja",
"uvicorn",
"anthropic",
"httpx",
],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name="claimwright",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
# Windowed (no console): a downloadable consumer app shouldn't spawn a
# Terminal on double-click. The launcher mirrors its startup line/errors to
# <data_dir>/claimwright-launch.log so a failed start stays diagnosable.
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=_icon,
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name="claimwright",
)
# --- macOS .app bundle ------------------------------------------------------
# Wrap the one-folder build into a double-clickable ClaimWright.app with the claimwright
# dock icon. A stable name + bundle id means every rebuild is a drop-in
# replacement (Finder/Dock treat it as the same app); only the stamped version
# changes. No-op on non-macOS, where COLLECT's `dist/claimwright/` is the artifact.
if sys.platform == "darwin":
app = BUNDLE(
coll,
name="ClaimWright.app",
icon=_icon,
bundle_identifier="com.claimwright.claims",
version=__version__,
info_plist={
"CFBundleName": "ClaimWright",
"CFBundleDisplayName": "ClaimWright Claims",
"CFBundleShortVersionString": __version__,
"CFBundleVersion": __version__,
"NSHighResolutionCapable": True,
# A normal foreground app (it opens a window), not a background agent.
"LSBackgroundOnly": False,
},
)