hte-cli 0.2.32__tar.gz → 0.2.33__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hte_cli-0.2.32 → hte_cli-0.2.33}/PKG-INFO +1 -1
- {hte_cli-0.2.32 → hte_cli-0.2.33}/pyproject.toml +1 -1
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/cli.py +52 -20
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/image_utils.py +0 -52
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/unit/test_image_utils.py +0 -55
- {hte_cli-0.2.32 → hte_cli-0.2.33}/uv.lock +1 -1
- {hte_cli-0.2.32 → hte_cli-0.2.33}/.gitignore +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/README.md +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/__init__.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/__main__.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/api_client.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/config.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/errors.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/events.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/runner.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/scorers.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/src/hte_cli/version_check.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/__init__.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/__init__.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/automated_runner.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/conftest.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/e2e_test.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/test_benchmark_flows.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/test_eval_logs.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/test_infrastructure.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/test_runtime_imports.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/test_session_lifecycle.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/e2e/verify_docker_deps.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/unit/__init__.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/unit/conftest.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/unit/test_runner.py +0 -0
- {hte_cli-0.2.32 → hte_cli-0.2.33}/tests/unit/test_scorers.py +0 -0
|
@@ -363,7 +363,7 @@ def session_join(ctx, session_id: str, force_setup: bool):
|
|
|
363
363
|
extract_image_platforms_from_compose,
|
|
364
364
|
pull_image_with_progress,
|
|
365
365
|
check_image_architecture_matches_host,
|
|
366
|
-
|
|
366
|
+
remove_image,
|
|
367
367
|
get_host_docker_platform,
|
|
368
368
|
is_running_in_linux_vm_on_arm,
|
|
369
369
|
)
|
|
@@ -487,34 +487,69 @@ def session_join(ctx, session_id: str, force_setup: bool):
|
|
|
487
487
|
cached_images.append(img)
|
|
488
488
|
continue
|
|
489
489
|
else:
|
|
490
|
-
# Architecture mismatch detected -
|
|
490
|
+
# Architecture mismatch detected - remove and re-pull with correct arch
|
|
491
491
|
console.print(
|
|
492
492
|
f" [yellow]⚠[/yellow] {short_name} [yellow]architecture mismatch![/yellow]"
|
|
493
493
|
)
|
|
494
494
|
console.print(
|
|
495
495
|
f" [dim]Cached image: {image_arch} | Host: {host_arch}[/dim]"
|
|
496
496
|
)
|
|
497
|
-
console.print(
|
|
498
|
-
" [dim]Removing cached image and re-pulling correct architecture...[/dim]"
|
|
499
|
-
)
|
|
500
497
|
|
|
501
|
-
|
|
502
|
-
if
|
|
498
|
+
# Remove the wrongly-cached image
|
|
499
|
+
if not remove_image(img):
|
|
500
|
+
console.print(
|
|
501
|
+
f" [red]✗[/red] {short_name} [dim](failed to remove cached image)[/dim]"
|
|
502
|
+
)
|
|
503
|
+
failed_images.append(img)
|
|
504
|
+
pull_errors[img] = "failed to remove cached image"
|
|
505
|
+
continue
|
|
506
|
+
|
|
507
|
+
# Re-pull with correct platform and progress display
|
|
508
|
+
last_status = ["connecting..."]
|
|
509
|
+
last_error = [""]
|
|
510
|
+
|
|
511
|
+
with console.status(
|
|
512
|
+
f"[yellow]↓[/yellow] {short_name} [dim]re-pulling for {host_arch}...[/dim]"
|
|
513
|
+
) as status:
|
|
514
|
+
|
|
515
|
+
def show_progress(image: str, line: str):
|
|
516
|
+
if ": " in line:
|
|
517
|
+
parts = line.split(": ", 1)
|
|
518
|
+
if len(parts) == 2:
|
|
519
|
+
layer_id = parts[0][-8:]
|
|
520
|
+
layer_status = parts[1][:85]
|
|
521
|
+
display = f"{layer_id}: {layer_status}"
|
|
522
|
+
if display != last_status[0]:
|
|
523
|
+
last_status[0] = display
|
|
524
|
+
status.update(
|
|
525
|
+
f"[yellow]↓[/yellow] {short_name} [dim]{display}[/dim]"
|
|
526
|
+
)
|
|
527
|
+
if "error" in line.lower() or "denied" in line.lower():
|
|
528
|
+
last_error[0] = line
|
|
529
|
+
|
|
530
|
+
success = pull_image_with_progress(
|
|
531
|
+
img, platform=host_platform, on_progress=show_progress
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
if success:
|
|
503
535
|
console.print(
|
|
504
|
-
f" [green]✓[/green] {short_name} [green]fixed![/green] [dim]({
|
|
536
|
+
f" [green]✓[/green] {short_name} [green]fixed![/green] [dim](re-pulled as {host_platform.split('/')[-1]})[/dim]"
|
|
505
537
|
)
|
|
506
538
|
pulled_images.append(img)
|
|
507
539
|
continue
|
|
508
|
-
|
|
509
|
-
#
|
|
510
|
-
# Re-pull the amd64 version and warn about QEMU
|
|
540
|
+
else:
|
|
541
|
+
# ARM re-pull failed - try without platform constraint (x86 fallback)
|
|
511
542
|
console.print(
|
|
512
|
-
" [dim]No ARM variant
|
|
543
|
+
" [dim]No ARM variant - trying x86 fallback...[/dim]"
|
|
513
544
|
)
|
|
514
|
-
|
|
545
|
+
with console.status(
|
|
546
|
+
f"[yellow]↓[/yellow] {short_name} [dim]pulling x86...[/dim]"
|
|
547
|
+
) as status:
|
|
548
|
+
success = pull_image_with_progress(img, on_progress=show_progress)
|
|
549
|
+
|
|
515
550
|
if success:
|
|
516
551
|
console.print(
|
|
517
|
-
f" [yellow]![/yellow] {short_name} [dim](x86-only
|
|
552
|
+
f" [yellow]![/yellow] {short_name} [dim](x86-only, needs QEMU)[/dim]"
|
|
518
553
|
)
|
|
519
554
|
x86_images_on_arm.append(img)
|
|
520
555
|
pulled_images.append(img)
|
|
@@ -523,14 +558,11 @@ def session_join(ctx, session_id: str, force_setup: bool):
|
|
|
523
558
|
console.print(
|
|
524
559
|
f" [red]✗[/red] {short_name} [dim](failed to pull)[/dim]"
|
|
525
560
|
)
|
|
561
|
+
if last_error[0]:
|
|
562
|
+
console.print(f" [dim]{last_error[0][:60]}[/dim]")
|
|
563
|
+
pull_errors[img] = last_error[0]
|
|
526
564
|
failed_images.append(img)
|
|
527
|
-
pull_errors[img] = "failed to pull x86 fallback"
|
|
528
565
|
continue
|
|
529
|
-
else:
|
|
530
|
-
console.print(f" [red]✗[/red] {short_name} [dim]({fix_msg})[/dim]")
|
|
531
|
-
failed_images.append(img)
|
|
532
|
-
pull_errors[img] = fix_msg
|
|
533
|
-
continue
|
|
534
566
|
|
|
535
567
|
# Need to pull - show progress
|
|
536
568
|
last_status = ["connecting..."]
|
|
@@ -222,58 +222,6 @@ def remove_image(image: str) -> bool:
|
|
|
222
222
|
return False
|
|
223
223
|
|
|
224
224
|
|
|
225
|
-
def fix_image_architecture(
|
|
226
|
-
image: str,
|
|
227
|
-
on_status: Callable[[str], None] | None = None,
|
|
228
|
-
) -> tuple[bool, str]:
|
|
229
|
-
"""
|
|
230
|
-
Check if a cached image has wrong architecture and fix it if needed.
|
|
231
|
-
|
|
232
|
-
For Linux ARM64 hosts (e.g., VM on Apple Silicon), this:
|
|
233
|
-
1. Checks if the cached image is amd64 when host is arm64
|
|
234
|
-
2. Removes the wrongly-cached image
|
|
235
|
-
3. Re-pulls with explicit --platform linux/arm64
|
|
236
|
-
|
|
237
|
-
Args:
|
|
238
|
-
image: Image name to check/fix
|
|
239
|
-
on_status: Callback for status updates
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
Tuple of (needed_fix, message):
|
|
243
|
-
- needed_fix: True if image was re-pulled
|
|
244
|
-
- message: Description of what happened
|
|
245
|
-
"""
|
|
246
|
-
matches, image_arch, host_arch = check_image_architecture_matches_host(image)
|
|
247
|
-
|
|
248
|
-
if matches:
|
|
249
|
-
if image_arch:
|
|
250
|
-
return (False, f"architecture OK ({image_arch})")
|
|
251
|
-
else:
|
|
252
|
-
return (False, "not cached")
|
|
253
|
-
|
|
254
|
-
# Architecture mismatch detected
|
|
255
|
-
host_platform = get_host_docker_platform()
|
|
256
|
-
if not host_platform:
|
|
257
|
-
return (False, f"unknown host architecture: {host_arch}")
|
|
258
|
-
|
|
259
|
-
if on_status:
|
|
260
|
-
on_status(f"Cached image is {image_arch}, host is {host_arch} - re-pulling...")
|
|
261
|
-
|
|
262
|
-
# Remove the wrongly-cached image
|
|
263
|
-
logger.info(f"Removing wrongly-cached {image_arch} image: {image}")
|
|
264
|
-
if not remove_image(image):
|
|
265
|
-
return (False, f"failed to remove cached {image_arch} image")
|
|
266
|
-
|
|
267
|
-
# Re-pull with correct platform
|
|
268
|
-
logger.info(f"Re-pulling {image} with platform {host_platform}")
|
|
269
|
-
success = pull_image_with_progress(image, platform=host_platform)
|
|
270
|
-
|
|
271
|
-
if success:
|
|
272
|
-
return (True, f"re-pulled as {host_platform.split('/')[-1]}")
|
|
273
|
-
else:
|
|
274
|
-
return (False, f"failed to re-pull with platform {host_platform}")
|
|
275
|
-
|
|
276
|
-
|
|
277
225
|
def pull_image_with_progress(
|
|
278
226
|
image: str,
|
|
279
227
|
platform: str | None = None,
|
|
@@ -15,7 +15,6 @@ from hte_cli.image_utils import (
|
|
|
15
15
|
check_image_architecture_matches_host,
|
|
16
16
|
is_running_in_linux_vm_on_arm,
|
|
17
17
|
remove_image,
|
|
18
|
-
fix_image_architecture,
|
|
19
18
|
)
|
|
20
19
|
|
|
21
20
|
|
|
@@ -569,57 +568,3 @@ class TestRemoveImage:
|
|
|
569
568
|
"""Returns False when docker rmi fails."""
|
|
570
569
|
mock_run.return_value = MagicMock(returncode=1)
|
|
571
570
|
assert remove_image("python:3.12-slim") is False
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
class TestFixImageArchitecture:
|
|
575
|
-
"""Tests for fix_image_architecture."""
|
|
576
|
-
|
|
577
|
-
@patch("hte_cli.image_utils.pull_image_with_progress")
|
|
578
|
-
@patch("hte_cli.image_utils.remove_image")
|
|
579
|
-
@patch("hte_cli.image_utils.check_image_architecture_matches_host")
|
|
580
|
-
def test_no_fix_needed_when_matches(self, mock_check, mock_remove, mock_pull):
|
|
581
|
-
"""Returns (False, message) when architecture already matches."""
|
|
582
|
-
mock_check.return_value = (True, "arm64", "aarch64")
|
|
583
|
-
|
|
584
|
-
needed_fix, message = fix_image_architecture("python:3.12-slim")
|
|
585
|
-
|
|
586
|
-
assert needed_fix is False
|
|
587
|
-
assert "architecture OK" in message
|
|
588
|
-
mock_remove.assert_not_called()
|
|
589
|
-
mock_pull.assert_not_called()
|
|
590
|
-
|
|
591
|
-
@patch("hte_cli.image_utils.pull_image_with_progress")
|
|
592
|
-
@patch("hte_cli.image_utils.remove_image")
|
|
593
|
-
@patch("hte_cli.image_utils.check_image_architecture_matches_host")
|
|
594
|
-
@patch("hte_cli.image_utils.platform.machine")
|
|
595
|
-
def test_fixes_mismatch_by_repulling(self, mock_machine, mock_check, mock_remove, mock_pull):
|
|
596
|
-
"""Removes and re-pulls when architecture mismatches."""
|
|
597
|
-
mock_machine.return_value = "aarch64"
|
|
598
|
-
mock_check.return_value = (False, "amd64", "aarch64") # Mismatch!
|
|
599
|
-
mock_remove.return_value = True
|
|
600
|
-
mock_pull.return_value = True
|
|
601
|
-
|
|
602
|
-
needed_fix, message = fix_image_architecture("python:3.12-slim")
|
|
603
|
-
|
|
604
|
-
assert needed_fix is True
|
|
605
|
-
assert "re-pulled" in message
|
|
606
|
-
mock_remove.assert_called_once_with("python:3.12-slim")
|
|
607
|
-
mock_pull.assert_called_once_with("python:3.12-slim", platform="linux/arm64")
|
|
608
|
-
|
|
609
|
-
@patch("hte_cli.image_utils.pull_image_with_progress")
|
|
610
|
-
@patch("hte_cli.image_utils.remove_image")
|
|
611
|
-
@patch("hte_cli.image_utils.check_image_architecture_matches_host")
|
|
612
|
-
@patch("hte_cli.image_utils.platform.machine")
|
|
613
|
-
def test_returns_false_when_repull_fails(
|
|
614
|
-
self, mock_machine, mock_check, mock_remove, mock_pull
|
|
615
|
-
):
|
|
616
|
-
"""Returns (False, message) when re-pull fails."""
|
|
617
|
-
mock_machine.return_value = "aarch64"
|
|
618
|
-
mock_check.return_value = (False, "amd64", "aarch64")
|
|
619
|
-
mock_remove.return_value = True
|
|
620
|
-
mock_pull.return_value = False # Pull fails
|
|
621
|
-
|
|
622
|
-
needed_fix, message = fix_image_architecture("python:3.12-slim")
|
|
623
|
-
|
|
624
|
-
assert needed_fix is False
|
|
625
|
-
assert "failed to re-pull" in message
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|