Skip to content

Commit dfaff01

Browse files
committed
fix: safely revert working tree to stable state (e93985a)
1 parent a96760d commit dfaff01

11 files changed

Lines changed: 232 additions & 429 deletions

.github/workflows/demo-matrix-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ jobs:
293293
if: always()
294294
run: |
295295
8pkg daemon stop || true
296-
pkill -f "_work/omnipkg/omnipkg/.venv" || true
296+
pkill -f "omnipkg" || true
297297
298298
# ─────────────────────────────────────────────────────────────────────────────
299299
# 7. PYPI RELEASE GATE

.github/workflows/windows-concurrency-test.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,7 @@ jobs:
6464
- name: Dump daemon log after demo run 1
6565
if: always()
6666
shell: pwsh
67-
run: |
68-
$logFile = Get-ChildItem -Path "$env:TEMP\omnipkg" -Filter "omnipkg_daemon.log" -Recurse | Select-Object -First 1
69-
if ($logFile) {
70-
Get-Content $logFile.FullName
71-
} else {
72-
Write-Error "Could not find daemon log in $env:TEMP\omnipkg"
73-
}
67+
run: type "$env:TEMP\omnipkg\omnipkg_daemon.log"
7468

7569
- name: Run Demo Again (Verify Caching)
7670
shell: cmd

src/omnipkg/common_utils.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -747,26 +747,10 @@ def _worker_read_reply() -> dict:
747747
The daemon writes to worker.process.stdin (also text mode) after
748748
receiving the stdin_line from the C dispatcher.
749749
"""
750-
import threading
751-
result = [None]
752-
exc = [None]
753-
754-
def _read():
755-
try:
756-
result[0] = sys.stdin.readline()
757-
except Exception as e:
758-
exc[0] = e
759-
760-
t = threading.Thread(target=_read, daemon=True)
761-
t.start()
762-
t.join(5.0)
763-
if t.is_alive():
764-
raise TimeoutError("no stdin reply within 5s")
765-
if exc[0]:
766-
raise exc[0]
767-
if not result[0]:
750+
line = sys.stdin.readline()
751+
if not line:
768752
raise EOFError("daemon closed stdin")
769-
return json.loads(result[0].strip())
753+
return json.loads(line.strip())
770754

771755

772756
def safe_input(prompt: str, default: str = "", auto_value: str = None) -> str:

src/omnipkg/core.py

Lines changed: 27 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -2823,13 +2823,6 @@ def _first_time_setup(self, interactive=True) -> Dict:
28232823
safe_print(_(" 💡 Future startups will be instant!"))
28242824

28252825
# Initialize knowledge base
2826-
rebuild_cmd = [
2827-
str(final_config["python_executable"]),
2828-
"-m",
2829-
"omnipkg.cli",
2830-
"reset",
2831-
"-y",
2832-
]
28332826
is_windows = platform.system() == "Windows"
28342827
creationflags = subprocess.CREATE_NO_WINDOW if is_windows else 0
28352828
win_env = {**os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
@@ -4247,9 +4240,6 @@ def _create_bubble_from_editable_install(
42474240
source_path: str,
42484241
python_context_version: str,
42494242
) -> bool:
4250-
# --- ADD THIS LINE HERE ---
4251-
import site as site_module
4252-
42534243
"""
42544244
Creates a bubble from an editable install by copying files from the live source.
42554245
This preserves the current dev version WITH ALL DEPENDENCIES as a fallback.
@@ -4488,7 +4478,6 @@ def _try_pip_dry_run(self, package_name: str, version: str) -> Optional[List[str
44884478
pass
44894479

44904480
def _try_pypi_api(self, package_name: str, version: str) -> Optional[List[str]]:
4491-
import requests as http_requests
44924481
try:
44934482
pass
44944483
except ImportError:
@@ -4501,11 +4490,11 @@ def _try_pypi_api(self, package_name: str, version: str) -> Optional[List[str]]:
45014490
"User-Agent": "omnipkg-package-manager/1.0",
45024491
"Accept": "application/json",
45034492
}
4504-
response = http_requests.get(url, timeout=10, headers=headers)
4493+
response = requests.get(url, timeout=10, headers=headers)
45054494
if response.status_code == 404:
45064495
if clean_version != version:
45074496
url = f"https://pypi.org/pypi/{package_name}/{version}/json"
4508-
response = http_requests.get(url, timeout=10, headers=headers)
4497+
response = requests.get(url, timeout=10, headers=headers)
45094498
if response.status_code != 200:
45104499
return None
45114500
if not response.text.strip():
@@ -4532,7 +4521,7 @@ def _try_pypi_api(self, package_name: str, version: str) -> Optional[List[str]]:
45324521
version_spec = match.group(2) or ""
45334522
dependencies.append(_("{}{}").format(dep_name, version_spec))
45344523
return dependencies
4535-
except http_requests.exceptions.RequestException:
4524+
except requests.exceptions.RequestException:
45364525
return None
45374526
except Exception:
45384527
return None
@@ -6825,15 +6814,11 @@ def _check_sync_status_ultra_fast(self, master_version: str) -> List[Tuple[str,
68256814
if p.name != expected_dist_info and p.name != expected_editable_dist_info
68266815
]
68276816

6817+
# egg-link = legacy setuptools editable; always needs re-sync to modern format
68286818
has_egg_link = (site_packages / "omnipkg.egg-link").exists()
68296819
if has_egg_link:
6830-
_marker = Path(exe_path).parent / f".omnipkg_synced_{master_version}"
6831-
if _marker.exists():
6832-
# Already synced, egg-link is stale but harmless — skip
6833-
pass
6834-
else:
6835-
sync_needed.append((py_ver, str(exe_path)))
6836-
continue
6820+
sync_needed.append((py_ver, str(exe_path)))
6821+
continue
68376822

68386823
# If we found an old/conflicting .dist-info directory, sync is ALWAYS needed.
68396824
# This is non-negotiable and overrides any .pth file check.
@@ -7020,31 +7005,8 @@ def sync_interpreter(py_ver, target_exe):
70207005
_already_synced = _marker.exists()
70217006
if _already_synced:
70227007
# Still need to check uv_ffi even if omnipkg is already synced
7023-
if _already_synced:
7024-
# Still need to check uv_ffi even if omnipkg is already synced
7025-
if os.environ.get("OMNIPKG_DEBUG"):
7026-
safe_print(f" [HEAL] {py_ver} omnipkg marker exists, checking uv_ffi only...")
7027-
# Clean up stale egg-link even if already synced (editable installs on old Python)
7028-
try:
7029-
_ir = Path(target_exe).parent.parent
7030-
sp_path = _ir / "Lib" if (_ir / "Lib").exists() else _ir / "lib"
7031-
import glob as _glob
7032-
_sp_dirs = _glob.glob(str(sp_path / "python*/site-packages")) or _glob.glob(str(sp_path / "site-packages"))
7033-
for sp_dir in _sp_dirs:
7034-
sp_dir = Path(sp_dir)
7035-
egg_link = sp_dir / "omnipkg.egg-link"
7036-
if egg_link.exists():
7037-
egg_link.unlink()
7038-
easy_pth = sp_dir / "easy-install.pth"
7039-
if easy_pth.exists():
7040-
lines = easy_pth.read_text(encoding="utf-8").splitlines()
7041-
cleaned = [l for l in lines if "omnipkg" not in l]
7042-
if cleaned:
7043-
easy_pth.write_text("\n".join(cleaned) + "\n", encoding="utf-8")
7044-
else:
7045-
easy_pth.unlink()
7046-
except Exception:
7047-
pass
7008+
if os.environ.get("OMNIPKG_DEBUG"):
7009+
safe_print(f" [HEAL] {py_ver} omnipkg marker exists, checking uv_ffi only...")
70487010
# Detect egg-based pip (e.g. pip 20.2.2 on Python 3.7)
70497011
# and inject it into PYTHONPATH so -m pip works in subprocess
70507012
import glob, os
@@ -16128,28 +16090,21 @@ def _run_pip_install(
1612816090
# ── PATH 1: FFI in-process ─────────────────────────────────
1612916091
if self._uv_ffi_run is not None:
1613016092
daemon_client = None
16131-
_ffi_installed, _ffi_removed = [], []
16132-
_op_marker = None
16133-
# ✅ HOISTED: Define before the try so `finally` can always reference it safely
16134-
_is_target_call = "--target" in _uv_args
16135-
_target_val = _uv_args[_uv_args.index("--target") + 1] if _is_target_call else None
16136-
16137-
# Signal the START of our operation (Marker File + IPC)
16138-
# 🔥 BUBBLE FIX: Only signal the daemon if we're touching the MAIN env.
16139-
# A bubble install goes to a temp --target dir; the watcher isn't
16140-
# watching that dir, so lying to it causes spurious invalidations.
16141-
if not _is_target_call:
16142-
try:
16143-
_sp_path = Path(self.config.get("site_packages_path"))
16144-
_op_marker = _sp_path / ".omnipkg_op.lock"
16145-
_op_marker.touch()
16093+
_ffi_installed, _ffi_removed = [], [] # Initialize for finally block
16094+
_op_marker = None # Initialize for finally block
1614616095

16147-
from omnipkg.isolation.worker_daemon import DaemonClient
16148-
daemon_client = DaemonClient(auto_start=False)
16149-
daemon_client.start_omnipkg_op()
16150-
except Exception as e:
16151-
_dbg(f"[FS-WATCHER-CLIENT] Failed to signal start_op: {e}")
16152-
daemon_client = None
16096+
# 1. Signal the START of our operation (Marker File + IPC)
16097+
try:
16098+
_sp_path = Path(self.config.get("site_packages_path"))
16099+
_op_marker = _sp_path / ".omnipkg_op.lock"
16100+
_op_marker.touch()
16101+
16102+
from omnipkg.isolation.worker_daemon import DaemonClient
16103+
daemon_client = DaemonClient(auto_start=False)
16104+
daemon_client.start_omnipkg_op()
16105+
except Exception as e:
16106+
_dbg(f"[FS-WATCHER-CLIENT] Failed to signal start_op: {e}")
16107+
daemon_client = None
1615316108

1615416109
try:
1615516110
from omnipkg.isolation.fs_watcher import FfiWriteGuard
@@ -16202,26 +16157,22 @@ def _run_pip_install(
1620216157
safe_print(f"[UV-PATH] FFI error ({_ffi_ex}) after {_ffi_ms:.2f}ms — trying daemon", file=sys.stderr)
1620316158

1620416159
finally:
16205-
# 🔥 BUBBLE FIX: Only tell the daemon if we actually touched the MAIN env.
16206-
# If _is_target_call, this was a bubble — daemon knows nothing about it,
16207-
# and we don't want it to think the main site-packages changed.
16208-
if not _is_target_call and daemon_client:
16209-
_dbg(f"[CORE-SENDER] Sending to Daemon: INST={_ffi_installed}, REM={_ffi_removed}")
16160+
if daemon_client:
16161+
print(f" [CORE-SENDER] Sending to Daemon: INST={_ffi_installed}, REM={_ffi_removed}", file=sys.stderr)
1621016162
try:
1621116163
daemon_client.end_omnipkg_op(installed=_ffi_installed, removed=_ffi_removed)
1621216164
except Exception as e:
1621316165
_dbg(f"[FS-WATCHER-CLIENT] Failed to signal end_op to daemon: {e}")
1621416166

16215-
# Always clean up the lock file (it lives in main site-packages,
16216-
# we touched it, we clean it — even if the bubble path somehow set it)
16167+
# ✅ Delete AFTER IPC is sent — not before
1621716168
if _op_marker and _op_marker.exists():
1621816169
try:
1621916170
_op_marker.unlink()
1622016171
except Exception as e:
16221-
_dbg(f"[FS-WATCHER-CLIENT] Failed to unlink op_marker: {e}")
16172+
_dbg(f"[FS-WATCHER-CLIENT] Failed to signal end_op to daemon: {e}")
1622216173

1622316174
else:
16224-
safe_print(f"[UV-PATH] FFI skipped (unavailable) — trying daemon", file=sys.stderr)
16175+
safe_print(f"[UV-PATH] FFI skipped (unavailable or --target) — trying daemon", file=sys.stderr)
1622516176

1622616177
# ── PATH 2: daemon run_uv (~IPC overhead) ──────────────
1622716178
_t0 = time.perf_counter()

src/omnipkg/dispatcher.c

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,16 +2055,7 @@ int main(int argc, char **argv) {
20552055
* that contain the word python (e.g. "info python-dotenv"). */
20562056
int info_python = (is_info && argc >= 3 &&
20572057
strcmp(argv[2], "python") == 0);
2058-
int is_logs_follow = 0;
2059-
if (argc >= 3 && strcmp(argv[1], "daemon") == 0 && strcmp(argv[2], "logs") == 0) {
2060-
for (int _i = 3; _i < argc; _i++) {
2061-
if (strcmp(argv[_i], "-f") == 0 || strcmp(argv[_i], "--follow") == 0) {
2062-
is_logs_follow = 1;
2063-
break;
2064-
}
2065-
}
2066-
}
2067-
is_interactive_command = ((is_info && !info_python) || is_config || is_logs_follow);
2058+
is_interactive_command = ((is_info && !info_python) || is_config);
20682059
}
20692060
if (!is_swap_python && !is_interactive_command) {
20702061
try_daemon_cli(target_python, argc, argv, version_injected, forced_version);

src/omnipkg/dispatcher.py

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,7 @@ def main():
225225
and argv_commands[1] in ("start", "stop", "restart")
226226
)
227227

228-
is_info_command = (
229-
len(argv_commands) >= 1
230-
and argv_commands[0] == "info"
231-
and not (len(argv_commands) >= 2 and argv_commands[1] == "python")
232-
)
233-
is_config_command = len(argv_commands) >= 1 and argv_commands[0] == "config"
234-
is_logs_follow = (
235-
len(argv_commands) >= 2
236-
and argv_commands[0] == "daemon"
237-
and argv_commands[1] == "logs"
238-
and "-f" in sys.argv
239-
)
240-
is_interactive_command = is_info_command or is_config_command or is_logs_follow
241-
242-
if not is_swap_command and not is_daemon_lifecycle and not is_interactive_command:
228+
if not is_swap_command and not is_daemon_lifecycle:
243229
try:
244230
import socket
245231
import tempfile
@@ -254,7 +240,6 @@ def main():
254240
host, port = conn_str[6:].split(":")
255241
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
256242
sock.connect((host, int(port)))
257-
sock.settimeout(300)
258243
else:
259244
raise ValueError()
260245
else:
@@ -263,7 +248,6 @@ def main():
263248
if os.path.exists(sock_path):
264249
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
265250
sock.connect(sock_path)
266-
sock.settimeout(300)
267251
else:
268252
raise ValueError()
269253

0 commit comments

Comments
 (0)