Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions pmoves/tools/local_cert_runners.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""Manage local-certification GitHub runners via Docker containers.

This keeps the local-cert lanes (`ai-lab` and `vps`) reproducible across
Windows/WSL/Linux without manual shell sequences.
This keeps the local-cert lanes (`ai-lab`, `vps`, and `hotfix`) reproducible
across Windows/WSL/Linux without manual shell sequences.
"""

from __future__ import annotations
Expand Down Expand Up @@ -37,16 +37,37 @@ class RunnerLane:
runner_name="pmoves-vps-runner",
labels="self-hosted,vps,Linux,X64",
),
RunnerLane(
lane="hotfix",
container_name="gha-runner-hotfix",
runner_name="pmoves-hotfix-runner",
labels="self-hosted,hotfix,Linux,X64",
),
)


def run_cmd(args: list[str], check: bool = True) -> subprocess.CompletedProcess[str]:
return subprocess.run(
args,
check=check,
text=True,
capture_output=True,
)
def run_cmd(args: list[str], check: bool = True,
timeout: int = 120) -> subprocess.CompletedProcess[str]:
"""Run a subprocess command with timeout handling.

TimeoutExpired is re-raised when check=True but returns a synthetic
CompletedProcess(returncode=-1) when check=False so callers like
docker_rm and _runner_log_args degrade gracefully.
"""
try:
return subprocess.run(
args,
check=check,
text=True,
capture_output=True,
timeout=timeout,
)
except subprocess.TimeoutExpired:
if check:
raise
return subprocess.CompletedProcess(
args, returncode=-1, stdout="", stderr="timed out",
)


def require_tool(name: str) -> None:
Expand Down Expand Up @@ -98,6 +119,11 @@ def docker_rm(container_name: str) -> None:
"memory": os.getenv("RUNNER_VPS_MEMORY", "4g"),
"gpus": "",
},
"hotfix": {
"cpus": os.getenv("RUNNER_HOTFIX_CPUS", "2"),
"memory": os.getenv("RUNNER_HOTFIX_MEMORY", "4g"),
"gpus": "",
},
}


Expand Down Expand Up @@ -144,6 +170,7 @@ def _runner_log_args(lane: RunnerLane) -> list[str]:
info = run_cmd(
["docker", "info", "--format", "{{json .Plugins.Log}}"],
check=False,
timeout=15,
)
if info.returncode == 0:
payload = (info.stdout or "").strip()
Expand Down
Loading