# 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: 1. PyInstaller extracts resources to a temporary read-only directory (`/tmp/_MEIxxxxxx/`) 2. Docker build requires a writable context directory 3. The temporary directory has restrictive permissions 4. 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:** ```python 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:** ```python 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 1. **Writable Location:** `$HOME/.prole/` is user-writable 2. **Persistent:** Files remain between runs (can be cached) 3. **Clean State:** Each build starts fresh (old dir removed) 4. **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 ```bash python3 install.py --gui # Navigate to "Build Container" screen # Click "Build Database Image" # Should succeed without hanging ``` ### Test from Package ```bash 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 ```bash # 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: ```bash rm -rf ~/.prole/build ``` Or include in the installer: ```python 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: 1. **Docker daemon running:** ```bash docker info ``` 2. **Docker context accessible:** ```bash ls -la ~/.prole/build/prole-db/Dockerfile ``` 3. **Disk space:** ```bash df -h ~ ``` 4. **Docker permissions:** ```bash docker ps # Should not require sudo ``` ## Alternative Solutions Considered ### 1. Use /tmp with Unique Names ```python import tempfile build_dir = Path(tempfile.mkdtemp(prefix="prole-build-")) ``` **Pros:** Automatic cleanup **Cons:** Lost between runs, no caching ### 2. Build in Place (_MEIPASS) ```python cwd = get_resource_path("prole-db") ``` **Pros:** No copying needed **Cons:** ✗ Fails due to read-only permissions ### 3. Use PROJECT_ROOT for Both ```python 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 1. **Add cleanup option:** ```python # In exit handler or menu if messagebox.askyesno("Cleanup", "Remove build directories?"): cleanup_build_dirs() ``` 2. **Progress indication:** ```python self._db_build_console.write("Copying build context...\n") shutil.copytree(source_dir, build_dir) self._db_build_console.write("Build context ready.\n") ``` 3. **Cache detection:** ```python 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: 1. Creating `~/.prole/build/prole-db/` directory 2. Copying build context from embedded resources 3. Running `docker build` in the writable directory 4. Avoiding PyInstaller's read-only temporary extraction directory