lookout-cli 5.1.0__tar.gz → 5.2.0__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.
Files changed (32) hide show
  1. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/PKG-INFO +1 -1
  2. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/cli.py +1 -0
  3. lookout_cli-5.2.0/lookout_cli/docker/docker-compose.cpu.yaml +11 -0
  4. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.yaml +1 -0
  5. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/groups/base.py +116 -12
  6. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/PKG-INFO +1 -1
  7. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/SOURCES.txt +1 -0
  8. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/pyproject.toml +1 -1
  9. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/README.md +0 -0
  10. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/__init__.py +0 -0
  11. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/auth_helpers.py +0 -0
  12. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/banner.py +0 -0
  13. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.cache.yaml +0 -0
  14. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.dev.yaml +0 -0
  15. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.jetson.yaml +0 -0
  16. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.network-host.yaml +0 -0
  17. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.network-proxy.yaml +0 -0
  18. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.prod.yaml +0 -0
  19. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker/docker-compose.x86.yaml +0 -0
  20. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/docker_helpers.py +0 -0
  21. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/groups/__init__.py +0 -0
  22. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/groups/model.py +0 -0
  23. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/groups/setup.py +0 -0
  24. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/helpers.py +0 -0
  25. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli/test/lookout_cli_test.py +0 -0
  26. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/dependency_links.txt +0 -0
  27. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/entry_points.txt +0 -0
  28. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/requires.txt +0 -0
  29. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/top_level.txt +0 -0
  30. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/lookout_cli.egg-info/zip-safe +0 -0
  31. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/setup.cfg +0 -0
  32. {lookout_cli-5.1.0 → lookout_cli-5.2.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lookout_cli
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: A CLI for interacting with Lookout+
5
5
  Author-email: Greenroom Robotics <team@greenroomrobotics.com>
6
6
  Maintainer-email: David Revay <david.revay@greenroomrobotics.com>
@@ -38,6 +38,7 @@ def cli():
38
38
  if dev_mode:
39
39
  lookout_cli.add_command(base.build)
40
40
  lookout_cli.add_command(base.lint)
41
+ lookout_cli.add_command(base.test)
41
42
  lookout_cli.add_command(base.bake)
42
43
  lookout_cli.add_command(base.manifest)
43
44
  lookout_cli.add_command(base.sbom)
@@ -0,0 +1,11 @@
1
+ name: lookout
2
+
3
+ # Drops the NVIDIA device reservations so the stack can start on machines
4
+ # without a GPU (e.g. CI runners). Selected automatically by the CLI when
5
+ # nvidia-smi is absent.
6
+ services:
7
+ lookout_core:
8
+ deploy: !reset null
9
+
10
+ lookout_greenstream:
11
+ deploy: !reset null
@@ -23,6 +23,7 @@ services:
23
23
  - ZENOH_CONFIG_OVERRIDE
24
24
  - LOOKOUT_CONFIG
25
25
  - LOOKOUT_CONFIG_DIR
26
+ - LOOKOUT_TEST_MODE
26
27
  - NVIDIA_VISIBLE_DEVICES=all
27
28
  - NVIDIA_DRIVER_CAPABILITIES=compute,utility,video,graphics
28
29
  - DISPLAY
@@ -1,6 +1,9 @@
1
1
  import os
2
2
  import platform
3
+ import shutil
4
+ import socket
3
5
  import subprocess
6
+ import time
4
7
  from pathlib import Path
5
8
  from typing import List, Optional, cast
6
9
 
@@ -56,6 +59,7 @@ DOCKER_NETWORK_HOST = docker_compose_path("./docker-compose.network-host.yaml")
56
59
  DOCKER_NETWORK_PROXY = docker_compose_path("./docker-compose.network-proxy.yaml")
57
60
  DOCKER_JETSON = docker_compose_path("./docker-compose.jetson.yaml")
58
61
  DOCKER_X86 = docker_compose_path("./docker-compose.x86.yaml")
62
+ DOCKER_CPU = docker_compose_path("./docker-compose.cpu.yaml")
59
63
 
60
64
  SERVICES = [
61
65
  "lookout_core",
@@ -95,6 +99,9 @@ def _get_compose_files(
95
99
  compose_files.append(DOCKER_JETSON)
96
100
  else:
97
101
  compose_files.append(DOCKER_X86)
102
+ if shutil.which("nvidia-smi") is None:
103
+ # no NVIDIA driver (e.g. CI runners): drop the GPU device reservations
104
+ compose_files.append(DOCKER_CPU)
98
105
 
99
106
  if proxy:
100
107
  compose_files.append(DOCKER_NETWORK_PROXY)
@@ -153,6 +160,18 @@ def log_config(config: LookoutConfig):
153
160
  )
154
161
 
155
162
 
163
+ def _prepare_mounted_dirs(config: LookoutConfig) -> None:
164
+ """Create the host directories the containers bind-mount; docker would
165
+ otherwise create missing mount sources root-owned (e.g. on CI runners)."""
166
+ get_config_io().get_path().chmod(0o777)
167
+ greenroom_tmp_directory = Path("/tmp/greenroom")
168
+ make_dir_set_permission(Path(config.log_directory).expanduser())
169
+ make_dir_set_permission(Path(config.recording_directory).expanduser())
170
+ make_dir_set_permission(Path(config.models_directory).expanduser())
171
+ make_dir_set_permission(greenroom_tmp_directory)
172
+ make_dir_set_permission(greenroom_tmp_directory / "sockets")
173
+
174
+
156
175
  def set_initial_env(config_dir: str):
157
176
  version = get_version()
158
177
  os.environ["LOOKOUT_CONFIG_DIR"] = config_dir
@@ -239,18 +258,7 @@ def up(
239
258
  if config.prod and build:
240
259
  raise click.UsageError("Cannot build in production mode. Run `lookout build` instead")
241
260
 
242
- # Make the log and recordings directories
243
- log_directory = Path(config.log_directory).expanduser()
244
- recording_directory = Path(config.recording_directory).expanduser()
245
- models_directory = Path(config.models_directory).expanduser()
246
- greenroom_tmp_directory = Path("/tmp/greenroom")
247
- sockets_directory = greenroom_tmp_directory / "sockets"
248
- get_config_io().get_path().chmod(0o777)
249
- make_dir_set_permission(log_directory)
250
- make_dir_set_permission(recording_directory)
251
- make_dir_set_permission(models_directory)
252
- make_dir_set_permission(greenroom_tmp_directory)
253
- make_dir_set_permission(sockets_directory)
261
+ _prepare_mounted_dirs(config)
254
262
  set_env_from_config(config)
255
263
 
256
264
  services_list = list(services) if services else None
@@ -452,6 +460,102 @@ def lint():
452
460
  call("pre-commit run --all")
453
461
 
454
462
 
463
+ def _wait_for_port(port: int, timeout: float) -> bool:
464
+ deadline = time.time() + timeout
465
+ while time.time() < deadline:
466
+ try:
467
+ with socket.create_connection(("localhost", port), timeout=0.5):
468
+ return True
469
+ except OSError:
470
+ time.sleep(0.5)
471
+ return False
472
+
473
+
474
+ def _dump_container_logs(container: str, tail: int = 150) -> None:
475
+ """Print a container's recent logs so a failure is diagnosable (e.g. in CI)."""
476
+ click.echo(click.style(f"--- last {tail} log lines from {container} ---", fg="yellow"))
477
+ try:
478
+ subprocess.run(["docker", "logs", "--tail", str(tail), container], check=False, timeout=15)
479
+ except (OSError, subprocess.TimeoutExpired) as error:
480
+ click.echo(f"could not fetch logs: {error}")
481
+
482
+
483
+ def _container_env(container: str) -> str | None:
484
+ """The container's environment, or None when it can't be inspected."""
485
+ try:
486
+ result = subprocess.run(
487
+ ["docker", "inspect", container, "--format", "{{json .Config.Env}}"],
488
+ capture_output=True,
489
+ text=True,
490
+ check=False,
491
+ timeout=15,
492
+ )
493
+ except (OSError, subprocess.TimeoutExpired):
494
+ return None
495
+ return result.stdout if result.returncode == 0 else None
496
+
497
+
498
+ @click.command(name="test")
499
+ @click.option(
500
+ "--up",
501
+ type=bool,
502
+ default=False,
503
+ is_flag=True,
504
+ help="Bring up lookout_core first instead of requiring a running stack",
505
+ )
506
+ @config_dir_option
507
+ @click.argument("args", nargs=-1)
508
+ def test(up: bool, config_dir: str, args: tuple[str, ...]) -> None:
509
+ """Runs the platform bridge smoke tests against a running Lookout.
510
+
511
+ Bring the stack up first (`lookout up`), or pass --up. Extra ARGS are
512
+ forwarded to pytest (e.g. `-- -k tracks`).
513
+ """
514
+ set_initial_env(config_dir)
515
+ dev_mode = is_dev_version()
516
+
517
+ config = get_config_io().read()
518
+ config.prod = maybe_ignore_prod(dev_mode, config.prod)
519
+ set_env_from_config(config)
520
+
521
+ # the bridge trusts the committed test keypair so the tests can mint tokens (keys/README.md)
522
+ os.environ["LOOKOUT_TEST_MODE"] = "true"
523
+
524
+ if up:
525
+ _prepare_mounted_dirs(config)
526
+ docker = _get_docker_client(prod=config.prod, proxy=config.proxy.enabled)
527
+ services = ["lookout_core"] + (["lookout_proxy"] if config.proxy.enabled else [])
528
+ docker.compose.up(services, detach=True)
529
+
530
+ click.echo("Waiting for the platform bridge on localhost:50051...")
531
+ if not _wait_for_port(50051, timeout=180):
532
+ _dump_container_logs("lookout_core")
533
+ raise click.UsageError(
534
+ "The platform bridge is not reachable on localhost:50051 — run "
535
+ "`LOOKOUT_TEST_MODE=true lookout up` first (or pass --up)"
536
+ )
537
+ if not up:
538
+ env = _container_env("lookout_core")
539
+ if env is not None and "LOOKOUT_TEST_MODE=true" not in env:
540
+ raise click.UsageError(
541
+ "lookout_core is running without LOOKOUT_TEST_MODE=true, so the bridge "
542
+ "will reject test tokens — restart with `LOOKOUT_TEST_MODE=true lookout up` "
543
+ "or pass --up"
544
+ )
545
+ if config.proxy.enabled and not _wait_for_port(config.proxy.https_port, timeout=60):
546
+ _dump_container_logs("lookout_proxy")
547
+ raise click.UsageError(
548
+ f"The proxy is not reachable on localhost:{config.proxy.https_port}"
549
+ )
550
+
551
+ # missing client deps fail instead of skipping; see tests/integration/README.md
552
+ os.environ["LOOKOUT_INTEGRATION_REQUIRE"] = "1"
553
+ tests_dir = Path(get_project_root()) / "tests" / "integration"
554
+ result = subprocess.run(["python3", "-m", "pytest", "-v", str(tests_dir), *args], check=False)
555
+ if result.returncode != 0:
556
+ raise SystemExit(result.returncode)
557
+
558
+
455
559
  @click.command(name="generate")
456
560
  @config_dir_option
457
561
  @click.option(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lookout_cli
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: A CLI for interacting with Lookout+
5
5
  Author-email: Greenroom Robotics <team@greenroomrobotics.com>
6
6
  Maintainer-email: David Revay <david.revay@greenroomrobotics.com>
@@ -15,6 +15,7 @@ lookout_cli.egg-info/requires.txt
15
15
  lookout_cli.egg-info/top_level.txt
16
16
  lookout_cli.egg-info/zip-safe
17
17
  lookout_cli/docker/docker-compose.cache.yaml
18
+ lookout_cli/docker/docker-compose.cpu.yaml
18
19
  lookout_cli/docker/docker-compose.dev.yaml
19
20
  lookout_cli/docker/docker-compose.jetson.yaml
20
21
  lookout_cli/docker/docker-compose.network-host.yaml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lookout_cli"
7
- version = "5.1.0"
7
+ version = "5.2.0"
8
8
  description = "A CLI for interacting with Lookout+"
9
9
  readme = "README.md"
10
10
  license = {text = "Copyright (C) 2025, Greenroom Robotics"}
File without changes
File without changes
File without changes