prole/docs/PROLE-HOME-DIRECTORY.md
chrisfu cbfe930b78 feat: add ncurses interface, build system, and embedded resources
Major feature additions and infrastructure improvements for the Prole
Database Installer, enabling command-line operation and packaged binary
distribution.

## Ncurses Terminal Interface

- Add installer/ncurses_ui.py: UI primitives (CursesWindow, TerminalConsole,
  NavFooter, InputField, Checkbox)
- Add installer/ncurses_installer.py: Complete terminal UI with all 11 screens
- Implement same screen flow as GUI (welcome, deps, network scan, env setup,
  kerberos, password, build, cluster, scripts, deploy, installer creation)
- Add keyboard navigation (arrows, hjkl, vim-style)
- Support both GUI and ncurses modes in single binary

## Automatic Display Detection

- Add has_display() function to detect GUI availability
- Auto-select GUI if display available, ncurses otherwise
- Add --gui and --no-gui command-line flags
- Fallback to ncurses on GUI failure

## Build System and Packaging

- Add Makefile with targets: build, package, clean, test, install
- Add scripts/generate_spec.py: PyInstaller spec generator
- Add installer.spec: PyInstaller configuration
- Automatic PNG to ICNS icon conversion
- Create self-contained macOS .app bundle with embedded icon
- Support both Intel (x86_64) and Apple Silicon (arm64)

## Embedded Resources

- Add get_resource_path() helper for PyInstaller compatibility
- Embed all images (proleIcon.png, proleLogo.png, proleLogoSepia.png)
- Embed prole-net/prole-scan binary (6.8 MB universal binary)
- Embed prole-app/dist/Prole Tools.app (12 MB app bundle)
- Embed prole-db/ Docker build context

## Writable Directory Fixes

- Create ~/.prole/build/prole-db/ for Docker builds (fixes read-only _MEIPASS)
- Create ~/.prole/scan/ for network scan output (fixes API call failures)
- Copy build context to writable location before Docker operations
- Run prole-scan from writable working directory

## Documentation

- docs/build-system.md: Complete build system guide
- docs/ncurses-installer.md: Ncurses interface documentation
- docs/RELEASE-NOTES.md: Feature overview and release notes
- docs/IMAGE-RESOURCES.md: Image resource management
- docs/EMBEDDED-RESOURCES.md: Binary and app bundle embedding
- docs/DOCKER-BUILD-FIX.md: Docker build hang solution
- docs/PROLE-HOME-DIRECTORY.md: ~/.prole directory structure
- BUILD.md: Quick build reference

## Key Changes

install.py:
- Add get_resource_path() for embedded resource resolution
- Update image paths to use get_resource_path()
- Update Docker build to use ~/.prole/build/prole-db/
- Update network scan to use ~/.prole/scan/
- Add display detection and mode selection
- Add --gui and --no-gui argument parsing

## Testing

All features tested and verified:
- Ncurses interface navigation
- Display auto-detection
- Resource path resolution
- Docker build from package
- Network scan from package
- Icon conversion and embedding

Package size: ~50-100 MB (includes Python runtime, all resources)
Disk usage: ~/.prole/ uses ~2-6 MB

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-19 23:10:37 -08:00

321 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prole Home Directory Structure
## Overview
The Prole Installer creates and uses `$HOME/.prole/` for writable storage when running from a packaged binary. This is necessary because PyInstaller extracts resources to a read-only temporary directory.
## Directory Structure
```
$HOME/.prole/
├── build/ # Docker build contexts
│ └── prole-db/ # PostgreSQL Docker build
│ ├── Dockerfile
│ ├── conf/
│ └── ...
└── scan/ # Network scan output and cache
└── (scan results, temporary files)
```
## Purpose of Each Directory
### build/
**Purpose:** Writable location for Docker build contexts
**Why:** PyInstaller extracts resources to `/tmp/_MEIxxxxxx/` which is read-only. Docker requires a writable build context directory to create images.
**Usage:**
```python
prole_home = Path.home() / ".prole"
build_dir = prole_home / "build" / "prole-db"
build_dir.mkdir(parents=True, exist_ok=True)
# Copy build context from embedded resources
source_dir = get_resource_path("prole-db")
shutil.copytree(source_dir, build_dir)
# Docker build
subprocess.Popen(['docker', 'build', '-t', 'prole-db:TAG', '.'], cwd=build_dir)
```
**Contents:**
- Complete copy of `prole-db/` directory
- Dockerfile and all dependencies
- Refreshed on each build (old content removed)
**Size:** ~1-5 MB
### scan/
**Purpose:** Writable working directory for network scan operations
**Why:** The `prole-scan` binary may need to write output files, cache data, or store temporary results. Running from a read-only directory causes failures.
**Usage:**
```python
prole_home = Path.home() / ".prole"
scan_dir = prole_home / "scan"
scan_dir.mkdir(parents=True, exist_ok=True)
# Run scan with writable cwd
subprocess.Popen([str(scan_binary)], cwd=str(scan_dir))
```
**Contents:**
- Network scan results (temporary)
- Ollama API interaction cache
- Any intermediate files created by prole-scan
**Size:** Varies, typically < 1 MB
## Creation and Cleanup
### Automatic Creation
All directories are created automatically when needed:
```python
# Build directory
(Path.home() / ".prole" / "build" / "prole-db").mkdir(parents=True, exist_ok=True)
# Scan directory
(Path.home() / ".prole" / "scan").mkdir(parents=True, exist_ok=True)
```
### Manual Cleanup
To remove all Prole working directories:
```bash
rm -rf ~/.prole
```
Or from Python:
```python
import shutil
from pathlib import Path
prole_home = Path.home() / ".prole"
if prole_home.exists():
shutil.rmtree(prole_home)
```
### Automatic Cleanup (Future)
Consider adding cleanup options to the installer:
```python
def cleanup_prole_home():
"""Clean up .prole working directories."""
prole_home = Path.home() / ".prole"
if prole_home.exists():
# Keep or remove based on user preference
if messagebox.askyesno("Cleanup", "Remove temporary files?"):
shutil.rmtree(prole_home)
```
## Disk Space
### Expected Usage
| Directory | Size | When Created | Persistent |
|-----------|------|--------------|------------|
| `build/prole-db/` | 1-5 MB | First Docker build | Yes |
| `scan/` | < 1 MB | First network scan | Yes |
| **Total** | **~2-6 MB** | On first use | Yes |
### Growth
- **build/** – Overwritten on each build, doesn't grow
- **scan/** – May accumulate cache files over time
## Troubleshooting
### Permission Errors
**Error:** `Permission denied` creating `.prole` directory
**Cause:** Home directory not writable
**Solution:**
```bash
ls -ld ~
chmod u+w ~
```
### Disk Space Issues
**Error:** `No space left on device`
**Cause:** Disk full
**Solution:**
```bash
df -h ~
rm -rf ~/.prole # Free up space
```
### Stale Build Context
**Issue:** Docker build uses old files
**Solution:**
```python
# Build directory is refreshed automatically
if build_dir.exists():
shutil.rmtree(build_dir)
shutil.copytree(source_dir, build_dir)
```
## Security Considerations
### File Permissions
The `.prole` directory inherits user's home directory permissions:
```bash
ls -ld ~/.prole
# drwxr-xr-x user group ~/.prole
```
Only the user should have write access.
### Sensitive Data
Avoid storing sensitive data in `.prole/`:
- ✓ Build contexts (public)
- ✓ Scan results (network info, semi-sensitive)
- ✗ Passwords, keys, credentials
### Cleanup on Uninstall
If distributing the installer, consider:
1. **Document cleanup:**
```
To completely remove Prole:
1. Delete the app: rm -rf /Applications/Prole\ Installer.app
2. Clean up data: rm -rf ~/.prole
```
2. **Provide uninstall script:**
```bash
#!/bin/bash
# uninstall-prole.sh
rm -rf /Applications/Prole\ Installer.app
rm -rf ~/.prole
echo "Prole uninstalled"
```
## Related Issues
### Docker Build Hang
**Problem:** Docker build hangs when running from packaged binary
**Solution:** Copy build context to `~/.prole/build/prole-db/`
**See:** [DOCKER-BUILD-FIX.md](DOCKER-BUILD-FIX.md)
### Network Scan Failure
**Problem:** Network scan fails to make API calls from packaged binary
**Solution:** Run scan with `cwd=~/.prole/scan`
**Reason:** Scan binary needs writable directory for output/cache
## Implementation Details
### Code Location
**Build directory creation:** `install.py`, `run_db_build()` method (line ~1791)
```python
prole_home = Path.home() / ".prole"
build_dir = prole_home / "build" / "prole-db"
build_dir.mkdir(parents=True, exist_ok=True)
```
**Scan directory creation:** `install.py`, network scan worker (line ~1102)
```python
prole_home = Path.home() / ".prole"
scan_dir = prole_home / "scan"
scan_dir.mkdir(parents=True, exist_ok=True)
```
### Resource Path Resolution
Both use `get_resource_path()` to find embedded resources:
```python
def get_resource_path(relative_path):
"""Get absolute path to resource, works for dev and for PyInstaller."""
try:
base_path = Path(sys._MEIPASS) # PyInstaller temp dir
except AttributeError:
base_path = PROJECT_ROOT # Running from source
return base_path / relative_path
```
## Future Enhancements
### Persistent Cache
Store network scan results between runs:
```python
scan_cache = Path.home() / ".prole" / "scan" / "cache.json"
if scan_cache.exists():
# Load previous scan
results = json.loads(scan_cache.read_text())
else:
# Run new scan
results = run_scan()
scan_cache.write_text(json.dumps(results))
```
### Build Artifacts
Store built Docker images as tarballs:
```python
image_cache = Path.home() / ".prole" / "build" / "prole-db-TAG.tar"
if not image_cache.exists():
# Build and save
subprocess.run(['docker', 'build', '-t', 'prole-db:TAG', '.'])
subprocess.run(['docker', 'save', '-o', str(image_cache), 'prole-db:TAG'])
else:
# Load cached image
subprocess.run(['docker', 'load', '-i', str(image_cache)])
```
### Configuration Storage
Store user preferences:
```python
config_file = Path.home() / ".prole" / "config.json"
config = {
'cluster_env': 'development',
'kerberos_enabled': False,
'last_scan': '2025-01-19',
}
config_file.write_text(json.dumps(config, indent=2))
```
## Summary
The `~/.prole/` directory provides:
- ✓ Writable storage for packaged binary operations
- ✓ Separate from extracted read-only resources
- ✓ User-specific, secure location
- ✓ Easy to clean up manually
- ✓ Small disk footprint (< 10 MB)
This architecture ensures the installer works correctly whether running from source or from a PyInstaller package.