1"""Runs one command and appends an execution record to results/experiment-log.jsonl.23usage: python3 code/tools/logrun.py NAME [--log results/NAME.log] -- CMD ARG...45The record has the fields required by the AutoScholar artifact contract:6name, command, startedAt, endedAt, exitCode (plus durationSeconds, log path,7cwd and worker). Output of the command is passed through to stdout/stderr and,8if --log is given, also appended to that file. The exit code of the command is9propagated so that `set -e` scripts stop on failure. Nothing is ever logged as10successful unless the child process really exited with status 0.11"""12import datetime13import json14import os15import subprocess16import sys1718ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))19LOG = os.environ.get("AUTOSCHOLAR_EXPLOG") or os.path.join(ROOT, "results", "experiment-log.jsonl")202122def utc():23 return datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")242526def main(argv):27 name = argv[0]28 log = None29 i = 130 if argv[i] == "--log":31 log = argv[i + 1]32 i += 233 assert argv[i] == "--", "usage: logrun.py NAME [--log FILE] -- CMD..."34 cmd = argv[i + 1:]35 os.chdir(ROOT)36 started = utc()37 t0 = datetime.datetime.now()38 lf = open(log, "a") if log else None39 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)40 for line in proc.stdout:41 sys.stdout.write(line)42 sys.stdout.flush()43 if lf:44 lf.write(line)45 lf.flush()46 code = proc.wait()47 if lf:48 lf.close()49 ended = utc()50 rec = {"name": name, "command": " ".join(cmd), "startedAt": started, "endedAt": ended, "exitCode": code,51 "durationSeconds": round((datetime.datetime.now() - t0).total_seconds(), 3),52 "ok": code == 0, "cwd": ".", "log": log, "worker": os.environ.get("AUTOSCHOLAR_WORKER", "M4M64c")}53 with open(LOG, "a") as f:54 f.write(json.dumps(rec, sort_keys=True) + "\n")55 sys.exit(code)565758if __name__ == "__main__":59 main(sys.argv[1:])60