diff --git a/evalharness/sandbox/prefetch.py b/evalharness/sandbox/prefetch.py index f0d31c3..aa2b081 100644 --- a/evalharness/sandbox/prefetch.py +++ b/evalharness/sandbox/prefetch.py @@ -109,14 +109,41 @@ def _pull_one(image: str, verbose: bool = True) -> None: print(f'· pulling sandbox image {image} via {src} ...', file=_sys.stderr, flush=True) t0 = _time.monotonic() - r = subprocess.run(['docker', 'pull', ref], capture_output=True, text=True, - timeout=3600) + # stream progress: a multi-GB pull with captured output is minutes + # of silence that reads as a hang. Non-TTY docker pull emits clean + # per-layer lines; forward the informative ones (throttled). + proc = subprocess.Popen(['docker', 'pull', ref], + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + text=True, bufsize=1) + last_line = '' + last_show = 0.0 + import os as _os + for line in proc.stdout: + last_line = line.strip() + now = _time.monotonic() + if verbose and (any(k in line for k in + ('Pulling from', 'Pull complete', 'Status', + 'error', 'denied')) + or now - last_show > 10): + print(f' {last_line[:100]}', file=_sys.stderr, flush=True) + last_show = now + proc.wait(timeout=3600) + r = subprocess.CompletedProcess(proc.args, proc.returncode, + stdout='', stderr=last_line) if r.returncode == 0 and verbose: print(f'✓ image ready in {_time.monotonic() - t0:.0f}s', file=_sys.stderr, flush=True) if r.returncode == 0: - if ref != image: # retag the mirrored pull to the canonical name - subprocess.run(['docker', 'tag', ref, image], check=False) + if ref != image: + # retag BEFORE rmi and VERIFY: a silent tag failure followed + # by rmi deleted the only tag and lost a 342s multi-GB pull + tag = subprocess.run(['docker', 'tag', ref, image], + capture_output=True, text=True) + if tag.returncode != 0: + print(f'!! docker tag {ref} -> {image} failed: ' + f'{(tag.stderr or "")[:120]} (keeping the mirror tag)', + file=_sys.stderr, flush=True) + return # do NOT rmi: the mirror tag is the only handle subprocess.run(['docker', 'rmi', ref], check=False) return last_err = (ref, (r.stderr or '').strip()[:150])