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>
5.7 KiB
Docker Build Fix for PyInstaller Bundle
Problem
When the Prole Installer is packaged with PyInstaller and run as a standalone binary, Docker builds fail with the error:
Building prole-db:17.7-037 in /var/folders/rt/pywlnmxj3dn7t5552vwdcpp80000gn/T/_MEIGozuj4/prole-db...
The build hangs or fails because:
- PyInstaller extracts resources to a temporary read-only directory (
/tmp/_MEIxxxxxx/) - Docker build requires a writable context directory
- The temporary directory has restrictive permissions
- Docker cannot create the build context properly
Solution
Copy the Docker build context to a writable user directory before building.
Implementation
Location: install.py, run_db_build() method (line ~1791)
Before:
def worker():
tag = self.get_prole_db_version()
cwd = PROJECT_ROOT / "prole-db" # ✗ Points to read-only _MEIPASS
cmd = ['docker', 'build', '-t', f"prole-db:{tag}", '.']
proc = subprocess.Popen(cmd, cwd=cwd, ...)
After:
def worker():
tag = self.get_prole_db_version()
# Use $HOME/.prole/build for Docker build context
prole_home = Path.home() / ".prole"
build_dir = prole_home / "build" / "prole-db"
build_dir.mkdir(parents=True, exist_ok=True)
# Copy prole-db directory to writable location
source_dir = get_resource_path("prole-db")
if source_dir.exists():
import shutil
if build_dir.exists():
shutil.rmtree(build_dir)
shutil.copytree(source_dir, build_dir)
cwd = build_dir # ✓ Writable user directory
cmd = ['docker', 'build', '-t', f"prole-db:{tag}", '.']
proc = subprocess.Popen(cmd, cwd=cwd, ...)
Directory Structure
The installer now creates and uses:
$HOME/.prole/
└── build/
└── prole-db/ # Docker build context
├── Dockerfile
├── conf/
├── scripts/
└── ...
Why This Works
- Writable Location:
$HOME/.prole/is user-writable - Persistent: Files remain between runs (can be cached)
- Clean State: Each build starts fresh (old dir removed)
- Docker Compatible: Standard directory Docker can access
Resource Path Resolution
The prole-db directory is:
- Included in spec:
('prole-db', 'prole-db') - Extracted by PyInstaller: To
_MEIPASS/prole-db/ - Copied to writable location:
$HOME/.prole/build/prole-db/ - Used for build: Docker builds from writable copy
Performance
First Build:
- Copy prole-db (~1-5 MB): < 1 second
- Docker build: 30-60 seconds (varies)
Subsequent Builds:
- Directory recreation: < 1 second
- Docker build: May use layer cache
Testing
Test from Source
python3 install.py --gui
# Navigate to "Build Container" screen
# Click "Build Database Image"
# Should succeed without hanging
Test from Package
make package
./dist/Prole\ Installer.app/Contents/MacOS/prole-installer --gui
# Navigate to "Build Container" screen
# Click "Build Database Image"
# Should create ~/.prole/build/prole-db and succeed
Verify Directory Creation
# After build starts
ls -la ~/.prole/build/prole-db/
# Should show Dockerfile and other files
Cleanup
The build directory persists after the installer exits. To clean up:
rm -rf ~/.prole/build
Or include in the installer:
def cleanup_build_dirs():
"""Clean up temporary build directories."""
build_dir = Path.home() / ".prole" / "build"
if build_dir.exists():
shutil.rmtree(build_dir)
Other Potential Issues
If Docker build still hangs, check:
-
Docker daemon running:
docker info -
Docker context accessible:
ls -la ~/.prole/build/prole-db/Dockerfile -
Disk space:
df -h ~ -
Docker permissions:
docker ps # Should not require sudo
Alternative Solutions Considered
1. Use /tmp with Unique Names
import tempfile
build_dir = Path(tempfile.mkdtemp(prefix="prole-build-"))
Pros: Automatic cleanup Cons: Lost between runs, no caching
2. Build in Place (_MEIPASS)
cwd = get_resource_path("prole-db")
Pros: No copying needed Cons: ✗ Fails due to read-only permissions
3. Use PROJECT_ROOT for Both
cwd = PROJECT_ROOT / "prole-db"
Pros: Works from source Cons: ✗ Fails from package (_MEIPASS is read-only)
4. Chosen: Copy to ~/.prole/build ✓
Pros: Works from both source and package, writable, persistent Cons: Requires disk space, manual cleanup
Related Changes
- Spec file updated: Added
('prole-db', 'prole-db')to data files - Resource path: Uses
get_resource_path("prole-db")for source - Build output: Console shows build directory location
Future Enhancements
-
Add cleanup option:
# In exit handler or menu if messagebox.askyesno("Cleanup", "Remove build directories?"): cleanup_build_dirs() -
Progress indication:
self._db_build_console.write("Copying build context...\n") shutil.copytree(source_dir, build_dir) self._db_build_console.write("Build context ready.\n") -
Cache detection:
if build_dir.exists() and not force_clean: self._db_build_console.write("Using cached build context...\n") else: shutil.copytree(source_dir, build_dir)
Summary
The Docker build now works correctly from both source and packaged binary by:
- Creating
~/.prole/build/prole-db/directory - Copying build context from embedded resources
- Running
docker buildin the writable directory - Avoiding PyInstaller's read-only temporary extraction directory