prole/docs/EMBEDDED-RESOURCES.md

309 lines
7.6 KiB
Markdown

# Embedded Resources in Knoe Installer
## Overview
The Knoe Installer package includes several embedded resources that must be accessible both when running from source and when packaged as a standalone binary.
## Embedded Resources
### 1. Images (img/)
- **knoeIcon.png** (1.5 MB) - Application icon
- **knoeLogo.png** (2.2 MB) - Main logo
- **knoeLogoSepia.png** (2.7 MB) - Sepia background for GUI
- **knoeLogoBlueprint.png** (3.1 MB) - Blueprint variant
- **knoeIconblueprint.png** (1.6 MB) - Blueprint icon variant
### 2. Binary Executables
- **scan/network-agent** (6.8 MB) - Network scanner
- Universal binary (x86_64 + arm64)
- Used by network scan screen
- Detects Kerberos, Active Directory, etc.
### 3. Application Bundles
- **knoe-app/dist/Knoe Tools.app** (~12 MB) - Pre-built Knoe Tools
- Complete macOS .app bundle
- Used by installer creation screen
- Can be copied to DMG or USB installer
## How Embedding Works
### Resource Path Resolution
All resources use the `get_resource_path()` helper function:
```python
def get_resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller."""
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = Path(sys._MEIPASS)
except AttributeError:
# Running from source
base_path = PROJECT_ROOT
return base_path / relative_path
```
### Usage Examples
**Image Loading:**
```python
bg_path = get_resource_path('img/knoeLogoSepia.png')
if bg_path.exists():
image = Image.open(str(bg_path))
```
**Binary Execution:**
```python
scan_binary = get_resource_path("scan/network-agent")
if scan_binary.exists():
process = subprocess.Popen([str(scan_binary)], ...)
```
**App Bundle Access:**
```python
app_src = get_resource_path('knoe-app/dist/Knoe Tools.app')
if app_src.exists():
# Copy to destination
shutil.copytree(app_src, dest)
```
## PyInstaller Configuration
### Spec File (scripts/generate_spec.py)
**Data Files:**
```python
datas = [
('installer', 'installer'),
('conf', 'conf'),
('etc', 'etc'),
('img', 'img'),
('docs', 'docs'),
('knoe-app/dist/Knoe Tools.app', 'knoe-app/dist/Knoe Tools.app'),
]
```
**Binaries:**
```python
binaries = [
('scan/network-agent', 'scan'),
]
```
The `binaries` list ensures executable permissions are preserved.
## File Sizes
Total embedded resources: ~30-35 MB
Breakdown:
- Images: ~11 MB
- network-agent: 6.8 MB
- Knoe Tools.app: ~12 MB
- Other resources: ~5-10 MB
Final installer bundle: ~50-100 MB (includes Python runtime)
## Usage in Installer
### Network Scan Screen
The network scan screen uses `network-agent` to detect services:
```python
scan_binary = get_resource_path("scan/network-agent")
process = subprocess.Popen([str(scan_binary)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True)
```
Output is captured in real-time and displayed in the scan results text widget.
### Installer Creation Screen
The installer creation screen copies Knoe Tools.app to DMG:
```python
app_src = get_resource_path('knoe-app/dist/Knoe Tools.app')
if app_src.exists():
shutil.copytree(app_src, staging / 'Knoe Tools.app')
```
## Testing
### Verify Resources from Source
```bash
python3 test_embedded_resources.sh
```
This tests:
- ✓ All image files exist
- ✓ network-agent binary exists and is executable
- ✓ Knoe Tools.app bundle exists
- ✓ Spec file includes all resources
- ✓ Resource sizes
### Verify Resources in Built Package
After building:
```bash
# Check extracted resources
./dist/knoe-installer --help
# In another terminal, while installer is running:
ls -la /tmp/_MEI*/scan/
ls -la /tmp/_MEI*/img/
ls -la "/tmp/_MEI*/knoe-app/dist/Knoe Tools.app"
```
## Troubleshooting
### Binary Not Found Error
**Error:** `Scan binary not found at /var/folders/.../scan/network-agent`
**Cause:** Binary not included in package or path not using `get_resource_path()`
**Solution:**
1. Verify spec includes binary: `grep network-agent installer.spec`
2. Check code uses `get_resource_path()`: `grep "get_resource_path.*network-agent" install.py`
3. Rebuild: `make clean && make package`
### Binary Not Executable
**Error:** `Permission denied` when running network-agent
**Cause:** Binary permissions not preserved in package
**Solution:**
Ensure binary is in `binaries` list (not `datas`) in spec file:
```python
binaries = [
('scan/network-agent', 'scan'), # Correct - preserves +x
]
# NOT in datas:
# datas = [('scan/network-agent', 'scan')] # Wrong - loses +x
```
### App Bundle Not Found
**Error:** `Knoe Tools.app not found`
**Cause:** App not built before packaging installer
**Solution:**
1. Build Knoe Tools.app first (in knoe-app directory)
2. Verify it exists: `ls "knoe-app/dist/Knoe Tools.app"`
3. Then build installer: `make package`
### Large Bundle Size
**Cause:** Including large binary files significantly increases bundle size
**Optimization Options:**
1. **Compress app bundle:**
```bash
cd knoe-app/dist
zip -r "Knoe Tools.zip" "Knoe Tools.app"
# Include zip instead of .app
```
2. **Download on demand:**
Instead of embedding, download from server when needed
3. **Exclude debug symbols:**
Strip binaries before packaging:
```bash
strip scan/network-agent
```
## Build Process
When running `make package`:
1. **Icon conversion:** PNG → ICNS
2. **Spec generation:** Creates installer.spec with all resources
3. **PyInstaller:**
- Analyzes install.py
- Collects dependencies
- Copies data files (preserves directory structure)
- Copies binaries (preserves execute permissions)
- Creates single-file executable
4. **Bundle creation:** Packages into .app with icon
### Resource Extraction at Runtime
When the packaged installer runs:
1. PyInstaller extracts resources to `/tmp/_MEI<random>/`
2. Sets `sys._MEIPASS` to extraction directory
3. `get_resource_path()` uses `_MEIPASS` to find resources
4. Resources deleted automatically when installer exits
## Security Considerations
### Code Signing
Embedded binaries should be code signed:
```bash
codesign --force --sign "Developer ID Application: Your Name" scan/network-agent
```
Then build the installer - the signed binary will be included.
### Verification
Users can verify embedded binaries:
```bash
# Check signature of network-agent after extraction
codesign --verify --verbose /tmp/_MEI*/scan/network-agent
# Check installer bundle signature
codesign --verify --verbose "dist/Knoe Installer.app"
```
## Future Enhancements
### Lazy Loading
For large resources, consider lazy loading:
```python
def get_knoe_tools_app():
"""Download or extract Knoe Tools.app only when needed."""
app_path = get_resource_path('knoe-app/dist/Knoe Tools.app')
if not app_path.exists():
# Download from server
download_knoe_tools(app_path)
return app_path
```
### Compression
Compress large resources:
```python
# In spec file
datas = [
('knoe-app/dist/Knoe Tools.zip', 'knoe-app/dist'), # Compressed
]
# In code
def extract_knoe_tools():
zip_path = get_resource_path('knoe-app/dist/Knoe Tools.zip')
extract_dir = Path(tempfile.mkdtemp())
shutil.unpack_archive(zip_path, extract_dir)
return extract_dir / 'Knoe Tools.app'
```
## References
- [PyInstaller Data Files](https://pyinstaller.org/en/stable/spec-files.html#adding-data-files)
- [PyInstaller Binaries](https://pyinstaller.org/en/stable/spec-files.html#adding-binaries)
- [macOS App Bundle Structure](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html)