prole/docs/build-system.md

350 lines
9.3 KiB
Markdown

# Knoe Installer Build System
## Overview
The Knoe Database Installer includes a comprehensive build system that creates self-contained, universal binaries for distribution. The build system produces a macOS application bundle (.app) that:
- **Double-click launch**: Opens the GUI installer when launched from Finder
- **Command-line capable**: Can be run from terminal with full argument support
- **Auto-detects display**: Automatically switches to ncurses mode when no GUI is available
- **Self-contained**: Includes all dependencies and resources
- **Icon embedded**: Uses the Knoe icon for the application bundle
## Quick Start
```bash
# Install build dependencies
make install
# Build everything (creates .app bundle)
make package
# Test the build
make test
# Clean build artifacts
make clean
```
## Makefile Targets
### `make help` (default)
Display help information about available targets.
### `make install`
Install required build dependencies:
- PyInstaller (for creating standalone executables)
- Pillow (for image processing, if needed)
### `make build`
Build the static binary using PyInstaller:
1. Generates icon in .icns format
2. Creates PyInstaller spec file
3. Builds standalone executable
4. Creates macOS .app bundle
**Output:**
- `dist/knoe-installer` - Standalone executable
- `dist/Knoe Installer.app` - macOS application bundle
### `make package`
Alias for `make build` - creates the complete package.
### `make test`
Run basic tests on the built binary:
- Test `--help` output
- Verify app bundle structure
- Test executable within app bundle
### `make clean`
Remove all build artifacts:
- `build/` directory
- `dist/` directory
- `installer.spec` file
- Python cache files
### `make rebuild`
Clean and rebuild from scratch (`make clean build`).
### `make spec`
Generate only the PyInstaller spec file (intermediate step).
## Build Process Details
### 1. Icon Conversion
The build system converts `img/knoeIcon.png` to macOS `.icns` format with multiple resolutions:
- 16x16, 32x32 (standard and @2x)
- 128x128, 256x256 (standard and @2x)
- 512x512, 1024x1024 (standard and @2x)
**Tool:** macOS `sips` and `iconutil` commands
**Output:** `build/knoe.icns`
### 2. Spec File Generation
A Python script (`scripts/generate_spec.py`) creates the PyInstaller specification:
**Included Data Files:**
- `installer/` - Installer Python modules
- `conf/` - Configuration files
- `etc/` - Scripts and utilities
- `img/` - Images and icons (including knoeIcon.png, knoeLogoSepia.png, knoeLogo.png)
- `docs/` - Documentation
- `knoe-db/` - Docker build context for PostgreSQL database
- `knoe-app/dist/Knoe Tools.app` - Pre-built Knoe Tools application bundle (entire .app)
**Included Binaries:**
- `scan/network-agent` - Network scanner binary (universal: x86_64 + arm64, 6.8 MB)
**Resource Path Resolution:**
The installer uses a `get_resource_path()` helper function that automatically resolves paths correctly whether running from source or from a PyInstaller bundle:
- **From source**: Uses `PROJECT_ROOT / relative_path`
- **From bundle**: Uses `sys._MEIPASS / relative_path` (PyInstaller temp directory)
This ensures all images and resources are found correctly in both development and production.
**Docker Build Context:**
Docker builds require a writable directory. When running from a PyInstaller bundle, the extracted resources are in a read-only temporary directory. To solve this:
- Docker build context is copied to `$HOME/.knoe/build/knoe-db/`
- This provides a writable location for Docker to operate
- See [DOCKER-BUILD-FIX.md](DOCKER-BUILD-FIX.md) for details
**Hidden Imports:**
PyInstaller can't always auto-detect imports, so we explicitly include:
- All installer modules
- curses/ncurses libraries
**Output:** `installer.spec`
### 3. Binary Creation
PyInstaller processes the spec file to create:
**Single-file Executable:**
- All Python code and dependencies bundled
- Extracts to temporary directory at runtime
- Console-enabled (works with both GUI and ncurses)
**macOS App Bundle:**
- Standard `.app` directory structure
- Icon embedded in bundle
- Info.plist with metadata
- Executable in `Contents/MacOS/`
## Using the Built Application
### GUI Mode (Double-click)
Simply double-click `Knoe Installer.app` in Finder to launch the graphical installer.
### Command-line Mode
```bash
# Run with auto-detection (GUI if available, otherwise ncurses)
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer
# Force ncurses mode
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer --no-gui
# Force GUI mode
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer --gui
# Show help
./dist/Knoe\ Installer.app/Contents/MacOS/knoe-installer --help
```
### Installation to /Applications
```bash
cp -r "dist/Knoe Installer.app" /Applications/
```
After installation, the app is available:
- In Finder under Applications
- Via Spotlight search
- From command line as `/Applications/Knoe\ Installer.app/Contents/MacOS/knoe-installer`
## Display Auto-detection
The installer automatically detects whether a GUI display is available:
1. **macOS**: Attempts to create a Tk root window
2. **Linux/Unix**: Checks for `DISPLAY` environment variable
3. **Fallback**: Uses ncurses mode if GUI unavailable
**Override behavior:**
- `--gui` flag: Force GUI mode (fails if no display)
- `--no-gui` flag: Force ncurses mode
## Architecture Support
The build system detects the host architecture:
- **Apple Silicon (arm64)**: Native ARM64 binary
- **Intel (x86_64)**: Native x86_64 binary
For universal binaries supporting both architectures, you would need to:
1. Build on arm64 machine
2. Build on x86_64 machine
3. Use `lipo` to combine binaries
## Build Requirements
### System Requirements
- macOS 10.13 or later
- Python 3.8 or later
- Make build system
### Python Dependencies
- pyinstaller >= 5.0
- pillow (optional, for advanced image processing)
- All runtime dependencies of install.py
### macOS Developer Tools
- Command Line Tools (for `sips`, `iconutil`, `codesign`)
Install with: `xcode-select --install`
## Troubleshooting
### Build Fails with "No module named 'installer'"
**Cause:** PyInstaller can't find the installer package.
**Solution:** Ensure you're running from project root and `installer/` directory exists.
### Icon Not Showing in App Bundle
**Cause:** Icon conversion failed or .icns file is invalid.
**Solution:**
```bash
make clean
make build/knoe.icns
file build/knoe.icns # Should say "Mac OS X icon"
```
### Binary Won't Run on Other Machines
**Cause:** PyInstaller doesn't include all dependencies, or architecture mismatch.
**Solutions:**
- Check target machine has same or newer macOS version
- Verify architecture matches (arm64 vs x86_64)
- Check Console.app for error messages
- Rebuild with `--debug` in spec file for verbose output
### GUI Doesn't Launch When Double-clicking
**Cause:** App not signed, or console mode preventing GUI launch.
**Solutions:**
- Right-click → Open (first time only, to bypass Gatekeeper)
- Check that `console=True` in spec allows both modes
- Run from Terminal to see error messages
### Ncurses Mode Garbled Output
**Cause:** Terminal doesn't support required features.
**Solution:** Use a modern terminal emulator (Terminal.app, iTerm2, etc.)
## Advanced Configuration
### Custom Icon
Replace `img/knoeIcon.png` with your icon (PNG format, preferably 1024x1024).
### Additional Data Files
Edit `scripts/generate_spec.py` and add to `datas` list:
```python
datas = [
('installer', 'installer'),
('your_data', 'your_data'), # Add this
]
```
### Code Signing
For distribution outside development:
1. Get Apple Developer certificate
2. Modify Makefile to add signing step:
```makefile
codesign --deep --force --verify --verbose \
--sign "Developer ID Application: Your Name" \
"dist/Knoe Installer.app"
```
### Notarization
For Gatekeeper approval on macOS 10.15+:
1. Sign the app
2. Create a DMG or ZIP
3. Submit to Apple:
```bash
xcrun notarytool submit knoe-installer.zip \
--apple-id your@email.com \
--password app-specific-password \
--team-id TEAMID
```
## Distribution
### DMG Creation
```bash
hdiutil create -volname "Knoe Installer" \
-srcfolder "dist/Knoe Installer.app" \
-ov -format UDZO \
knoe-installer.dmg
```
### ZIP Archive
```bash
cd dist
zip -r ../knoe-installer.zip "Knoe Installer.app"
```
## Performance Considerations
**Build Time:** 30-60 seconds typical
**Binary Size:** 50-100 MB (includes Python runtime + dependencies)
**Startup Time:** 1-3 seconds (PyInstaller extraction overhead)
To improve startup time, consider using PyInstaller's `--onedir` mode instead of `--onefile`, though this increases distribution complexity.
## CI/CD Integration
Example GitHub Actions workflow:
```yaml
name: Build Installer
on: [push]
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.11'
- run: make install
- run: make package
- uses: actions/upload-artifact@v2
with:
name: knoe-installer
path: dist/Knoe Installer.app
```
## References
- [PyInstaller Documentation](https://pyinstaller.org/)
- [macOS App Bundle Structure](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html)
- [macOS Code Signing](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution)