Haiku subagent build under decision 129. Candidate manifest 9ac3a56e names packages/tasks/src/fake.mjs and packages/tasks/tests/tree.test.mjs. Gate and both named mutants ran in Sage's slot. Co-Authored-By: Claude Opus 5.5 <[email protected]>
56 lines
1.5 KiB
Python
56 lines
1.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Run one command in its own session and process group, with a timeout.
|
|
|
|
usage: run_group.py TIMEOUT_S LOGFILE CWD -- CMD [ARGS...]
|
|
|
|
Child stdout and stderr go to LOGFILE. On timeout the whole process group gets
|
|
SIGTERM, then SIGKILL after 5 s. After the child exits, anything still left in
|
|
its group is killed too, so nothing from one step outlives it.
|
|
Exit status: the child's, or 124 on timeout.
|
|
"""
|
|
import os
|
|
import signal
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def group_kill(pgid, sig):
|
|
try:
|
|
os.killpg(pgid, sig)
|
|
except ProcessLookupError:
|
|
pass
|
|
|
|
|
|
def main():
|
|
timeout = float(sys.argv[1])
|
|
log = sys.argv[2]
|
|
cwd = sys.argv[3]
|
|
cmd = sys.argv[sys.argv.index('--') + 1:]
|
|
with open(log, 'w') as out:
|
|
p = subprocess.Popen(
|
|
cmd,
|
|
cwd=cwd,
|
|
stdout=out,
|
|
stderr=subprocess.STDOUT,
|
|
stdin=subprocess.DEVNULL,
|
|
start_new_session=True,
|
|
)
|
|
try:
|
|
rc = p.wait(timeout=timeout)
|
|
except subprocess.TimeoutExpired:
|
|
group_kill(p.pid, signal.SIGTERM)
|
|
try:
|
|
p.wait(timeout=5)
|
|
except subprocess.TimeoutExpired:
|
|
pass
|
|
group_kill(p.pid, signal.SIGKILL)
|
|
p.wait()
|
|
out.write(f"\nTIMEOUT after {timeout:.0f}s: process group {p.pid} killed\n")
|
|
rc = 124
|
|
group_kill(p.pid, signal.SIGKILL)
|
|
sys.exit(rc)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|