#!/usr/bin/env python3 """ Run fish-tty-repro.js under a pseudo-TTY and drain output so Node never blocks on write. Usage (from anywhere): python3 packages/bare-os-booter/scripts/run-fish-repro-pty.py BARE_OS_REPRO_SKIP_STDERR=1 python3 packages/bare-os-booter/scripts/run-fish-repro-pty.py BARE_OS_REPRO_PTY_ASSERT=1 ... # exit 2 if green prompt not detected (CI guard) Requires: Unix (pty). Not for Windows CI. """ from __future__ import annotations import os import pty import select import sys import threading import time HERE = os.path.dirname(os.path.abspath(__file__)) BOOTER = os.path.dirname(HERE) def main() -> int: chunks: list[bytes] = [] def reader(master_fd: int, done: threading.Event) -> None: while not done.is_set(): try: r, _, _ = select.select([master_fd], [], [], 0.2) except (OSError, ValueError): break if done.is_set(): break if not r: continue try: data = os.read(master_fd, 65536) except OSError: break if not data: break chunks.append(data) pid, master_fd = pty.fork() if pid == 0: os.chdir(BOOTER) os.execlp("node", "node", os.path.join(HERE, "fish-tty-repro.js")) return 0 # unreachable done = threading.Event() t = threading.Thread(target=reader, args=(master_fd, done)) t.start() time.sleep(0.6) os.write(master_fd, b"hello\n") time.sleep(0.35) os.write(master_fd, b"exit\n") # Wait for Node to exit before closing the PTY master (avoids exit code 1 / hang). _, status = os.waitpid(pid, 0) done.set() t.join(timeout=5.0) try: os.close(master_fd) except OSError: pass raw = b"".join(chunks) try: sys.stdout.buffer.write(raw) except BrokenPipeError: pass text = raw.decode("utf-8", errors="replace") ok_green = "\x1b[32m[user@bare-os" in text or "[user@bare-os" in text sys.stderr.write( f"\n--- pty harness: exit status {status}, bytes {len(raw)}, " f"saw_full_prompt_hint={ok_green} ---\n" ) if os.environ.get("BARE_OS_REPRO_PTY_ASSERT") == "1" and not ok_green: sys.stderr.write("BARE_OS_REPRO_PTY_ASSERT: expected green [user@bare-os] prompt\n") return 2 if os.WIFEXITED(status): return os.WEXITSTATUS(status) return 1 if __name__ == "__main__": sys.exit(main())