Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion packaging/model/project_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,10 +370,22 @@ def _create_project_runtime(self, project_dir: str, *, on_status=None) -> None:
return

# Dev mode: create a classic .venv
# Note: --copies is NOT used here. On macOS, venv --copies copies the Python
# executable but NOT libpython3.12.dylib, causing "Library not loaded" at runtime.
# Using symlinks instead: the python symlink resolves to the original binary,
# and @executable_path/../lib/libpython3.12.dylib resolves correctly from there.
venv_path = os.path.join(project_dir, ".venv")
if on_status:
on_status("Creating project virtual environment...")
_run_hidden([sys.executable, "-m", "venv", "--copies", venv_path], timeout=600)
_run_hidden([sys.executable, "-m", "venv", venv_path], timeout=600)

# Bootstrap pip into the venv using the host Python (avoids ensurepip issues).
venv_lib = os.path.join(venv_path, "lib", f"python{sys.version_info.major}.{sys.version_info.minor}", "site-packages")
_run_hidden(
[sys.executable, "-m", "pip", "install", "--no-input", "--disable-pip-version-check",
"--target", venv_lib, "pip"],
timeout=300,
)

def _install_infernux_in_runtime(self, project_dir: str, engine_version: str = "", *, on_status=None):
"""Install the Infernux wheel into the project's Python environment.
Expand Down
43 changes: 43 additions & 0 deletions packaging/stage_bundled_python_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,49 @@ def _ensure_pip(python_exe: str) -> None:
if completed.returncode == 0:
return

# ensurepip can fail with SIGABRT on copied Python binaries on macOS.
# Try a fallback: copy pip from the original venv that we know works.
import shutil as _shutil, os as _os, sys as _sys, subprocess as _subprocess
original_python = _sys.executable
if original_python and _os.path.dirname(original_python) != _os.path.dirname(python_exe):
import site as _site
orig_site_packages = next(
(p for p in _site.getsitepackages() if _os.path.isdir(p) and "site-packages" in p), None
)
target_site_packages = next(
(p for p in _site.getsitepackages() if _os.path.isdir(p) and "site-packages" in p), None
) if False else None
# Try to find target site-packages via python path
try:
result = _subprocess.run(
[python_exe, "-c", "import site; print(site.getsitepackages()[0])"],
capture_output=True, text=True, timeout=10
)
target_site_packages = result.stdout.strip() if result.returncode == 0 else None
except Exception:
target_site_packages = None

if orig_site_packages and target_site_packages:
pip_src = _os.path.join(orig_site_packages, "pip")
if _os.path.isdir(pip_src):
pip_dest = _os.path.join(target_site_packages, "pip")
try:
_shutil.rmtree(pip_dest, ignore_errors=True)
_shutil.copytree(pip_src, pip_dest)
# Also copy wheel & certifi dependencies if present
for dep in ("wheel", "certifi", "distlib"):
dep_src = _os.path.join(orig_site_packages, dep)
if _os.path.isdir(dep_src):
_shutil.rmtree(_os.path.join(target_site_packages, dep), ignore_errors=True)
_shutil.copytree(dep_src, _os.path.join(target_site_packages, dep))
# Verify it works now
verify = _subprocess.run([python_exe, "-m", "pip", "--version"], capture_output=True, timeout=30)
if verify.returncode == 0:
return
except Exception:
pass

# Last resort: try ensurepip normally
completed = _run([python_exe, "-m", "ensurepip", "--upgrade"], timeout=600)
if completed.returncode != 0:
raise SystemExit(
Expand Down