fix(ci): make pre-push single-flight runner start on hosts without POSIX signals (#9724)#9730
fix(ci): make pre-push single-flight runner start on hosts without POSIX signals (#9724)#9730kodjima33 wants to merge 1 commit into
Conversation
…SIX signals (#9724) run_owned() built its handler map from (SIGINT, SIGTERM, SIGHUP) unconditionally. Windows Python has no SIGHUP, so every git push died with AttributeError before the pre-push checks started. Close the whole class of POSIX-only process APIs in this file: - owned_signals() registers only signals the host defines (and picks up SIGBREAK). - signal_child() forwards via os.killpg where process groups exist, else terminate(). - process_exists() no longer probes with os.kill(pid, 0) on Windows, where os.kill TerminateProcess()es the target — a liveness probe would have killed the lock holder the moment the runner became reachable on Windows. Verification: - python3 .github/scripts/test_preflight_runner.py -> 9 tests OK - python3 .github/scripts/test_run_checks.py -> 9 tests OK (manifest contract) - make preflight -> 8 checks passed - Real run: `preflight_runner.py --name wd -- sh -c 'echo "==> phase-one"; exit 3'` streamed the phase, wrote result.json, exited 3. - A/B against HEAD: SIGINT to the runner kills the child process group in both the old and new code -> POSIX forwarding is unchanged. - Old expression vs a Windows-shaped signal module raises AttributeError: 'SIGHUP'; owned_signals() returns (SIGINT, SIGTERM, SIGBREAK).
tianmind-studio
left a comment
There was a problem hiding this comment.
Blocking on a real Windows host: bash scripts/pre-push-singleflight fork still exits before any pre-push check starts. After the signal fix succeeds, the wrapper hands the extensionless Bash script scripts/pre-push directly to Python's subprocess.Popen; Windows CreateProcess cannot launch it, and CPython 3.11.15 raises FileNotFoundError: [WinError 2] at run_owned.
Please pass the active Bash executable explicitly (for example, -- "$BASH" scripts/pre-push "$@" in scripts/pre-push-singleflight) and cover that wrapper contract. As written, this changes the first Windows failure but does not restore git push.
|
Thanks for the signal/process work here. I reproduced the next Windows failure on this head and opened #9804 directly against your branch; it passes the active Bash executable to the runner, makes the existing POSIX simulations runnable on Windows, and adds the wrapper to the portability check trigger. |
Fixes #9724.
Bug
On Windows, every
git pushfails before any pre-push check runs:scripts/pre-push-singleflightroutes through.github/scripts/preflight_runner.py, whoserun_owned()built its handler map from(signal.SIGINT, signal.SIGTERM, signal.SIGHUP)unconditionally. Windows Python does not defineSIGHUP, so the wrapper exits 1 and Git rejects the push.Root cause / failure class
The runner assumes POSIX process APIs are always present. Fixing only the signal tuple would leave two more landmines on the same code path, so this PR closes the class:
owned_signals()registers only signals the host actually defines, and picks upSIGBREAKwhere it exists (Windows' console-interrupt signal).signal_child()usesos.killpgwhere process groups exist (POSIX children get their own session viastart_new_session), and falls back tochild.terminate()elsewhere.process_exists()usedos.kill(pid, 0). On Windowsos.killdoes not send a signal; it callsTerminateProcess. The moment the runner became reachable on Windows, the stale-lock probe would have killed the live lock holder. Windows now probes withOpenProcess/GetExitCodeProcess(access-denied ⇒ alive, mirroring the POSIXPermissionErrorbranch).Regression test
.github/scripts/test_preflight_runner.py(9 tests) exercises signal selection against Windows- and POSIX-shaped signal modules, forwarding on hosts with and without process groups, and assertsprocess_existsnever reachesos.killon Windows — no real signal needed to interrupt the test process, per the issue. Registered in.github/checks-manifest.yaml(preflight-runner-portability, local + ci lanes), so it is diff-scoped-selected whenever the runner changes.Verification
python3 .github/scripts/test_preflight_runner.py→ 9 tests OKpython3 .github/scripts/test_run_checks.py→ 9 tests OK (manifest contract guard)make preflighton this branch → 9 checks passed, withpreflight-runner-portabilitySELECTED by the diffpreflight_runner.py --name wd -- sh -c 'echo "==> phase-one"; echo hello; exit 3'streamed the phase line, wroteresult.json, exited 3HEAD: SIGINT to the runner kills the child process group in both the old and the new code — POSIX forwarding behavior is unchangedAttributeError: 'SIGHUP';owned_signals()returns(SIGINT, SIGTERM, SIGBREAK)Not verified on a real Windows host (no Windows machine available) — the Windows paths are covered by the hermetic tests above and by CPython's documented
os.kill/OpenProcesssemantics.🤖 automated by hourly watchdog; opened for review, not merged.