Skip to content
This repository was archived by the owner on Jan 23, 2026. It is now read-only.

Commit 1681555

Browse files
authored
Merge branch 'main' into fix-index-generation-workflow
2 parents 45a31b6 + b960370 commit 1681555

22 files changed

Lines changed: 523 additions & 183 deletions

File tree

__templates__/driver/pyproject.toml.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = [
99
]
1010
requires-python = ">=3.11"
1111
dependencies = [
12-
"anyio>=4.6.2.post1",
12+
"anyio>=4.10.0",
1313
"jumpstarter",
1414
]
1515

packages/jumpstarter-driver-energenie/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = [
99
]
1010
requires-python = ">=3.11"
1111
dependencies = [
12-
"anyio>=4.6.2.post1",
12+
"anyio>=4.10.0",
1313
"jumpstarter",
1414
"jumpstarter-driver-power"
1515
]

packages/jumpstarter-driver-flashers/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ authors = [
1111
requires-python = ">=3.11"
1212
dependencies = [
1313
"oras>=0.2.25",
14-
"anyio>=4.6.2.post1",
14+
"anyio>=4.10.0",
1515
"jumpstarter",
1616
"jumpstarter-driver-opendal",
1717
"jumpstarter-driver-pyserial",

packages/jumpstarter-driver-http-power/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = [
99
]
1010
requires-python = ">=3.11"
1111
dependencies = [
12-
"anyio>=4.6.2.post1",
12+
"anyio>=4.10.0",
1313
"jumpstarter",
1414
"jumpstarter-driver-power",
1515
]

packages/jumpstarter-driver-http/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "Apache-2.0"
77
authors = [{ name = "Benny Zlotnik", email = "bzlotnik@redhat.com" }]
88
requires-python = ">=3.11"
99
dependencies = [
10-
"anyio>=4.6.2.post1",
10+
"anyio>=4.10.0",
1111
"jumpstarter",
1212
"jumpstarter-driver-composite",
1313
"jumpstarter-driver-opendal",

packages/jumpstarter-driver-iscsi/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
authors = [{ name = "Benny Zlotnik", email = "bzlotnik@redhat.com" }]
77
requires-python = ">=3.11"
88
dependencies = [
9-
"anyio>=4.6.2.post1",
9+
"anyio>=4.10.0",
1010
"jumpstarter",
1111
"jumpstarter-driver-composite",
1212
"jumpstarter-driver-opendal",

packages/jumpstarter-driver-network/jumpstarter_driver_network/client.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
from contextlib import AbstractContextManager
1+
from collections.abc import Generator
2+
from contextlib import contextmanager
23
from ipaddress import IPv6Address, ip_address
34
from threading import Event
5+
from typing import Any
46

57
import click
8+
from anyio import ContextManagerMixin
69

710
from .adapters import DbusAdapter, TcpPortforwardAdapter, UnixPortforwardAdapter
811
from .driver import DbusNetwork
@@ -62,13 +65,11 @@ def forward_unix(path: str | None):
6265
return base
6366

6467

65-
class DbusNetworkClient(NetworkClient, AbstractContextManager):
66-
def __enter__(self):
67-
self.adapter = DbusAdapter(client=self)
68-
self.adapter.__enter__()
69-
70-
def __exit__(self, exc_type, exc_value, traceback):
71-
self.adapter.__exit__(exc_type, exc_value, traceback)
68+
class DbusNetworkClient(NetworkClient, ContextManagerMixin):
69+
@contextmanager
70+
def __contextmanager__(self) -> Generator[Any]:
71+
with DbusAdapter(client=self) as value:
72+
yield value
7273

7374
@property
7475
def kind(self):

packages/jumpstarter-driver-probe-rs/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "Apache-2.0"
77
authors = [{ name = "Miguel Angel Ajo", email = "miguelangel@ajo.es" }]
88
requires-python = ">=3.11"
99
dependencies = [
10-
"anyio>=4.6.2.post1",
10+
"anyio>=4.10.0",
1111
"click>=8.1.7.2",
1212
"jumpstarter",
1313
"jumpstarter-driver-opendal",

packages/jumpstarter-driver-shell/README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,53 @@ methods will be generated dynamically, and they will be available as follows:
6363

6464
:returns: A tuple(stdout, stderr, return_code)
6565
```
66+
67+
## CLI Usage
68+
69+
The shell driver also provides a CLI when using `jmp shell`. All configured methods become available as CLI commands, except for methods starting with `_` which are considered private and hidden from the end user:
70+
71+
```console
72+
$ jmp shell --exporter shell-exporter
73+
$ j shell
74+
Usage: j shell [OPTIONS] COMMAND [ARGS]...
75+
76+
Shell command executor
77+
78+
Commands:
79+
env_var Execute the env_var shell method
80+
ls Execute the ls shell method
81+
method2 Execute the method2 shell method
82+
method3 Execute the method3 shell method
83+
```
84+
85+
### CLI Command Usage
86+
87+
Each configured method becomes a CLI command with the following options:
88+
89+
```console
90+
$ j shell ls --help
91+
Usage: j shell ls [OPTIONS] [ARGS]...
92+
93+
Execute the ls shell method
94+
95+
Options:
96+
-e, --env TEXT Environment variables in KEY=VALUE format
97+
--help Show this message and exit.
98+
```
99+
100+
### Examples
101+
102+
```console
103+
# Execute simple commands
104+
$ j shell ls
105+
file1.txt file2.txt directory/
106+
107+
# Pass arguments to shell methods
108+
$ j shell method3 "first arg" "second arg"
109+
Hello World first arg
110+
Hello World second arg
111+
112+
# Set environment variables
113+
$ j shell env_var arg1 arg2 --env ENV_VAR=myvalue
114+
arg1,arg2,myvalue
115+
```

packages/jumpstarter-driver-shell/jumpstarter_driver_shell/client.py

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import sys
12
from dataclasses import dataclass
23

4+
import click
5+
36
from jumpstarter.client import DriverClient
47

58

@@ -11,8 +14,8 @@ class ShellClient(DriverClient):
1114
Client interface for Shell driver.
1215
1316
This client dynamically checks that the method is configured
14-
on the driver, and if it is, it will call it and get the results
15-
in the form of (stdout, stderr, returncode).
17+
on the driver, and if it is, it will call it with live streaming output.
18+
Output chunks are displayed as they arrive.
1619
"""
1720

1821
def _check_method_exists(self, method):
@@ -24,4 +27,59 @@ def _check_method_exists(self, method):
2427
## capture any method calls dynamically
2528
def __getattr__(self, name):
2629
self._check_method_exists(name)
27-
return lambda *args, **kwargs: tuple(self.call("call_method", name, kwargs, *args))
30+
def execute(*args, **kwargs):
31+
returncode = 0
32+
for stdout, stderr, code in self.streamingcall("call_method", name, kwargs, *args):
33+
if stdout:
34+
print(stdout, end='', flush=True)
35+
if stderr:
36+
print(stderr, end='', file=sys.stderr, flush=True)
37+
if code is not None:
38+
returncode = code
39+
return returncode
40+
return execute
41+
42+
def cli(self):
43+
"""Create CLI interface for dynamically configured shell methods"""
44+
@click.group
45+
def base():
46+
"""Shell command executor"""
47+
pass
48+
49+
# Get available methods from the driver
50+
if self._methods is None:
51+
self._methods = self.call("get_methods")
52+
53+
# Create a command for each configured method
54+
for method_name in self._methods:
55+
self._add_method_command(base, method_name)
56+
57+
return base
58+
59+
def _add_method_command(self, group, method_name):
60+
"""Add a Click command for a specific shell method"""
61+
@group.command(
62+
name=method_name,
63+
context_settings={"ignore_unknown_options": True, "allow_interspersed_args": False},
64+
)
65+
@click.argument('args', nargs=-1, type=click.UNPROCESSED)
66+
@click.option('--env', '-e', multiple=True,
67+
help='Environment variables in KEY=VALUE format')
68+
def method_command(args, env):
69+
# Parse environment variables
70+
env_dict = {}
71+
for env_var in env:
72+
if '=' in env_var:
73+
key, value = env_var.split('=', 1)
74+
env_dict[key] = value
75+
else:
76+
raise click.BadParameter(f"Invalid --env value '{env_var}'. Use KEY=VALUE.")
77+
78+
returncode = getattr(self, method_name)(*args, **env_dict)
79+
80+
# Exit with the same return code as the shell command
81+
if returncode != 0:
82+
raise click.exceptions.Exit(returncode)
83+
84+
# Update the docstring dynamically
85+
method_command.__doc__ = f"Execute the {method_name} shell method"

0 commit comments

Comments
 (0)