Skip to content

Fix/windows pip mode - #94

Merged
rboni-dk merged 3 commits into
mainfrom
fix/windows-pip-mode
Sep 1, 2026
Merged

Fix/windows pip mode#94
rboni-dk merged 3 commits into
mainfrom
fix/windows-pip-mode

Conversation

@rboni-dk

@rboni-dk rboni-dk commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Testing pip (standalone) mode on a clean Windows box turned up three defects. They compound: the first leaves a process behind, the second then reports a failed uninstall as a success, and the third makes an unrelated failure unreadable.

Ctrl+C left the UI running, and it broke everything after it. A stop left 2 of 23 processes alive — both Streamlit, still holding port 8501 — after printing TestGen stopped. That one orphan then made the next tg start bind nothing (while wait_for_tcp_port connected to the orphan and reported success), and made tg delete fail, since it held /Scripts/python.exe open.

Two independent causes, and fixing either alone changes nothing — the sweep could not see them (image-name match on testgen.exe, but every child is python; the POSIX pattern missed Streamlit for the same reason), and the sweep was never reached (stop_app_tree returned early on a dead parent, which on Windows is the normal case since a console Ctrl+C kills it along with us).

tg delete reported a success it had not verified — printing TestGen uninstalled. while leaving the tool environment behind, with the underlying error softened to "it may already be uninstalled". It now checks the disk and keeps the install marker, so the retry it recommends can actually do something.

A failed embedded database was unreadable — initdb misreports a failed launch as a missing file. We now run postgres -V first and point at --docker.

Then a Verified table (Linux 16 → 0, Windows 2 → 0), and a Not covered section noting Docker mode is untouched and that the database failure's actual cause lives outside this repo.

rboni-dk and others added 3 commits August 31, 2026 19:48
… not see

A Ctrl+C in pip mode on Windows left 2 of 23 processes running -- both Streamlit,
holding port 8501. Two separate reasons, and fixing either alone changes nothing.

The sweep could not see them. On Windows it matched the image name `testgen.exe`,
but every child is `python`. On POSIX it matched `testgen.*run-app`, but `run_ui`
spawns the UI as `python -m streamlit run .../testgen/ui/app.py`, which carries no
`run-app` -- and `start_new_session` puts it outside the group `killpg` targets.
Match on the uv tool environment path instead: it appears in argv[0] of every child
and in postgres' own command line, so it identifies the install without depending on
what any process is called. Separators are normalised, since postgres writes its
command line with forward slashes and the python children with back. The Windows
clause is generated from the same patterns so the platforms cannot drift.

The sweep was also never reached. `stop_app_tree` returned early on
`proc.poll() is not None`, and on Windows the parent is usually gone by then: a
console Ctrl+C reaches every process in the console's group, and the app is spawned
without CREATE_NEW_PROCESS_GROUP (`start_new_session` is POSIX-only), so it dies
with us. The UI does not -- `run_ui` gives it a group of its own. Run the force-kill
path on Windows regardless of whether the parent is alive; a dead parent there says
nothing about its children. POSIX keeps the early return, since there the parent
forwards the signal before exiting.

Windows enumerates and kills each match by PID rather than `taskkill /T`, which
walks the tree as it stands -- killing a parent re-parents its children mid-walk and
they escape. A failed sweep now logs: PowerShell can be absent or blocked by policy,
and silence there left no trace of why the next start found the port taken.

Verified on both platforms with real process trees. Linux, via a container: 16
orphans after a SIGKILLed installer, 0 after. Windows, on a real console: 2
survivors before, 0 after. Test fixtures use the command lines captured from both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On Windows `tg delete` printed "TestGen uninstalled." while leaving the uv tool
environment and the `testgen` entry point on disk. A leaked UI process held
`<tool env>/Scripts/python.exe` open, `uv tool uninstall` could not remove it, and
the resulting error was caught and softened to "it may already be uninstalled" --
which is exactly backwards. It had not been uninstalled at all.

Check the disk instead of trusting the exit code, and name what survived:

    TestGen was only partly uninstalled. These were left behind:
      ~/.local/share/uv/tools/dataops-testgen
      ~/.local/bin/testgen

    This usually means a TestGen process was still running and held a file open.
    Close any running TestGen, then run this command again.

That retry has to be able to do something, so an incomplete uninstall now keeps the
install marker -- dropping it makes the next `tg delete` report "nothing to delete"
-- and keeps the installer-local uv, which the retry needs to uninstall with.

The leftover check runs before the rest of the cleanup, so it tolerates any failure
rather than only `CommandFailed`: uv can fail to spawn outright, and aborting there
left more behind than doing nothing would. The pre-existing uninstall call is
widened for the same reason.

uv's bin directory is shared, commonly ~/.local/bin, so a `testgen` shim there is
not necessarily ours. It is only reported alongside a surviving tool environment;
on its own it would call a clean uninstall a failure and then strand the user
behind the marker we now keep.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A pip install on Windows failed inside `standalone-setup` with:

    initdb: error: program "postgres" is needed by initdb but was not found in
    the same directory as ".../initdb.exe"

postgres.exe was present the whole time. initdb runs `postgres -V`, that launch
failed, and initdb reported the failed launch as a missing file -- naming the one
thing that was not wrong. (The cause was a pgserver build shipped without a library
it links against, but the class is wider than that one bug.)

Run `postgres -V` ourselves first. If the bundled binary will not run, the install
cannot succeed, so stop and point at `--docker`. The exit code goes to the session
log, where support can use it; the console gets the way forward instead, since
neither an NT status code nor a library name helps the person installing. Under
`upgrade` it says to delete first, because `tg install` refuses while an install
marker exists and would otherwise walk the user into a refusal.

Checking before `standalone-setup` matters for a second reason: a partial setup
leaves `config.env` behind, and every retry then stops on an interactive
"Overwrite?" prompt the installer has no terminal to answer. One failed install
otherwise wedges every attempt after it.

The check runs after the tool environment is built -- it has to, since the binary
ships inside it -- so it saves seconds, not minutes. Its value is a legible failure
and a clean retry, not speed. Guidance is emitted from `on_action_fail` rather than
`execute`, which runs inside a partial console line. Skips rather than fails when
the binary cannot be located: not finding it says nothing about whether it works.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rboni-dk
rboni-dk merged commit 036e4b8 into main Sep 1, 2026
3 checks passed
@rboni-dk
rboni-dk deleted the fix/windows-pip-mode branch September 1, 2026 01:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants