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

Commit 7f6c784

Browse files
authored
Merge pull request #695 from jumpstarter-dev/backport-690-to-release-0.7
[Backport release-0.7] Custom driver descriptions
2 parents 4c012ce + 2e9f71b commit 7f6c784

29 files changed

Lines changed: 1020 additions & 213 deletions

File tree

packages/jumpstarter-driver-composite/jumpstarter_driver_composite/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from rich import traceback
66

77
from jumpstarter.client import DriverClient
8+
from jumpstarter.client.decorators import driver_click_group
89

910

1011
def _opt_log_level_callback(ctx, param, value):
@@ -34,7 +35,7 @@ def close(self):
3435
v.close()
3536

3637
def cli(self):
37-
@click.group
38+
@driver_click_group(self)
3839
@click.option(
3940
"--log-level",
4041
"log_level",

packages/jumpstarter-driver-flashers/jumpstarter_driver_flashers/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
from jumpstarter_driver_flashers.bundle import FlasherBundleManifestV1Alpha1
2323

24+
from jumpstarter.client.decorators import driver_click_group
2425
from jumpstarter.common.exceptions import ArgumentError
2526

2627
debug_console_option = click.option("--console-debug", is_flag=True, help="Enable console debug mode")
@@ -611,7 +612,7 @@ def manifest(self):
611612
return self._manifest
612613

613614
def cli(self):
614-
@click.group
615+
@driver_click_group(self)
615616
def base():
616617
"""Software-defined flasher interface"""
617618
pass

packages/jumpstarter-driver-gpiod/jumpstarter_driver_gpiod/client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from jumpstarter_driver_power.client import PowerClient
66

77
from jumpstarter.client import DriverClient
8+
from jumpstarter.client.decorators import driver_click_group
89

910

1011
class PinState(Enum):
@@ -34,7 +35,7 @@ def read(self):
3435
return PinState(int(self.call("read_pin")))
3536

3637
def cli(self):
37-
@click.group()
38+
@driver_click_group(self)
3839
def gpio():
3940
"""GPIO power control commands."""
4041
pass
@@ -79,7 +80,7 @@ def read(self):
7980
return PinState(int(self.call("read_pin")))
8081

8182
def cli(self):
82-
@click.group()
83+
@driver_click_group(self)
8384
def gpio():
8485
"""GPIO input commands."""
8586
pass

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .driver import DbusNetwork
1313
from jumpstarter.client import DriverClient
1414
from jumpstarter.client.core import DriverMethodNotImplemented
15+
from jumpstarter.client.decorators import driver_click_group
1516

1617

1718
class NetworkClient(DriverClient):
@@ -20,7 +21,7 @@ def address(self):
2021
return self.call("address")
2122

2223
def cli(self):
23-
@click.group
24+
@driver_click_group(self)
2425
def base():
2526
"""Generic Network Connection"""
2627
pass

packages/jumpstarter-driver-opendal/jumpstarter_driver_opendal/client.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from .adapter import OpendalAdapter
2020
from .common import Capability, HashAlgo, Metadata, Mode, PathBuf, PresignedRequest
2121
from jumpstarter.client import DriverClient
22+
from jumpstarter.client.decorators import driver_click_group
2223
from jumpstarter.common.exceptions import ArgumentError
2324
from jumpstarter.streams.encoding import Compression
2425

@@ -413,9 +414,10 @@ def cli(self): # noqa: C901
413414
arg_dst = click.argument("dst", type=click.Path())
414415
opt_expire_second = click.option("--expire-second", type=int, required=True)
415416

416-
@click.group
417+
@driver_click_group(self)
417418
def base():
418419
"""Opendal Storage"""
420+
pass
419421

420422
@base.command
421423
@arg_path
@@ -548,7 +550,7 @@ def dump(
548550
...
549551

550552
def cli(self):
551-
@click.group
553+
@driver_click_group(self)
552554
def base():
553555
"""Generic flasher interface"""
554556
pass
@@ -716,7 +718,10 @@ def read_local_file(self, filepath):
716718

717719
def cli(self, base=None):
718720
if base is None:
719-
base = click.group(lambda: None)
721+
@driver_click_group(self)
722+
def base():
723+
"""Storage operations"""
724+
pass
720725

721726
@base.command()
722727
def host():

packages/jumpstarter-driver-power/jumpstarter_driver_power/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from .common import PowerReading
77
from jumpstarter.client import DriverClient
8+
from jumpstarter.client.decorators import driver_click_group
89

910

1011
class PowerClient(DriverClient):
@@ -35,7 +36,7 @@ def read(self) -> Generator[PowerReading, None, None]:
3536
yield PowerReading.model_validate(v, strict=True)
3637

3738
def cli(self):
38-
@click.group
39+
@driver_click_group(self)
3940
def base():
4041
"""Generic power"""
4142
pass

packages/jumpstarter-driver-probe-rs/jumpstarter_driver_probe_rs/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from opendal import Operator
77

88
from jumpstarter.client import DriverClient
9+
from jumpstarter.client.decorators import driver_click_group
910
from jumpstarter.common.exceptions import ArgumentError
1011

1112

@@ -60,7 +61,7 @@ def read(self, width: int, address: int, words: int) -> list[int]:
6061
return [int(data, 16) for data in data_strs]
6162

6263
def cli(self): # noqa: C901
63-
@click.group
64+
@driver_click_group(self)
6465
def base():
6566
"""probe-rs client"""
6667
pass

packages/jumpstarter-driver-pyserial/jumpstarter_driver_pyserial/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from .console import Console
88
from jumpstarter.client import DriverClient
9+
from jumpstarter.client.decorators import driver_click_group
910

1011

1112
class PySerialClient(DriverClient):
@@ -36,7 +37,7 @@ def pexpect(self):
3637
yield adapter
3738

3839
def cli(self):
39-
@click.group
40+
@driver_click_group(self)
4041
def base():
4142
"""Serial port client"""
4243
pass

packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
from pathlib import Path
33
from typing import Dict, Optional
44

5-
import click
65
from jumpstarter_driver_composite.client import CompositeClient
76
from jumpstarter_driver_opendal.client import FlasherClient, operator_for_path
87
from jumpstarter_driver_power.client import PowerClient
98
from opendal import Operator
109

10+
from jumpstarter.client.decorators import driver_click_group
11+
1112
PROMPT = "CMD >> "
1213

1314

@@ -102,8 +103,9 @@ def flash(
102103
def cli(self):
103104
generic_cli = FlasherClient.cli(self)
104105

105-
@click.group()
106+
@driver_click_group(self)
106107
def storage():
108+
"""RideSX storage operations"""
107109
pass
108110

109111
for name, cmd in generic_cli.commands.items():

packages/jumpstarter-driver-shell/README.md

Lines changed: 95 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ $ pip3 install --extra-index-url {{index_url}} jumpstarter-driver-shell
1111

1212
## Configuration
1313

14-
Example configuration:
14+
The shell driver supports two configuration formats for methods:
15+
16+
### Format 1: Simple String e.g. for self-descriptive short commands
1517

1618
```yaml
1719
export:
@@ -20,12 +22,40 @@ export:
2022
config:
2123
methods:
2224
ls: "ls"
23-
method2: "echo 'Hello World 2'"
24-
#multi line method
25-
method3: |
26-
echo 'Hello World $1'
27-
echo 'Hello World $2'
28-
env_var: "echo $1,$2,$ENV_VAR"
25+
echo_hello: "echo 'Hello World'"
26+
```
27+
28+
### Format 2: Unified Format with Descriptions
29+
30+
```yaml
31+
export:
32+
shell:
33+
type: jumpstarter_driver_shell.driver.Shell
34+
config:
35+
methods:
36+
ls:
37+
command: "ls -la"
38+
description: "List directory contents with details"
39+
deploy:
40+
command: "ansible-playbook deploy.yml"
41+
description: "Deploy application using Ansible"
42+
# Multi-line commands work too
43+
setup:
44+
command: |
45+
echo 'Setting up environment'
46+
export PATH=$PATH:/usr/local/bin
47+
./setup.sh
48+
description: "Set up the development environment"
49+
# Description-only (uses default "echo Hello" command)
50+
placeholder:
51+
description: "Placeholder method for testing"
52+
# Custom timeout for long-running operations
53+
long_backup:
54+
command: "tar -czf backup.tar.gz /data && rsync backup.tar.gz remote:/backups/"
55+
description: "Create and sync backup (may take a while)"
56+
timeout: 1800 # 30 minutes instead of default 5 minutes
57+
# You can mix both formats
58+
simple_echo: "echo 'simple'"
2959
# optional parameters
3060
cwd: "/tmp"
3161
log_level: "INFO"
@@ -34,6 +64,25 @@ export:
3464
- "-c"
3565
```
3666
67+
### Configuration Parameters
68+
69+
| Parameter | Description | Type | Required | Default |
70+
|-----------|-------------|------|----------|---------|
71+
| `methods` | Dictionary of methods. Values can be:<br/>- String: just the command<br/>- Dict: `{command: "...", description: "...", timeout: ...}` | `dict[str, str \| dict]` | Yes | - |
72+
| `cwd` | Working directory for shell commands | `str` | No | `None` |
73+
| `log_level` | Logging level | `str` | No | `"INFO"` |
74+
| `shell` | Shell command to execute scripts | `list[str]` | No | `["bash", "-c"]` |
75+
| `timeout` | Command timeout in seconds | `int` | No | `300` |
76+
77+
**Method Configuration Options:**
78+
79+
For the dict format, each method supports:
80+
- `command`: The shell command to execute (optional, defaults to `"echo Hello"`)
81+
- `description`: CLI help text (optional, defaults to `"Execute the {method_name} shell method"`)
82+
- `timeout`: Command-specific timeout in seconds (optional, defaults to global `timeout` value)
83+
84+
**Note:** You can mix both formats in the same configuration - use string format for simple commands and dict format when you want custom descriptions or timeouts.
85+
3786
## API Reference
3887

3988
Assuming the exporter driver is configured as in the example above, the client
@@ -66,7 +115,11 @@ methods will be generated dynamically, and they will be available as follows:
66115

67116
## CLI Usage
68117

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:
118+
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.
119+
120+
### CLI Help Output
121+
122+
With unified format (custom descriptions):
70123

71124
```console
72125
$ jmp shell --exporter shell-exporter
@@ -76,10 +129,40 @@ Usage: j shell [OPTIONS] COMMAND [ARGS]...
76129
Shell command executor
77130
78131
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
132+
deploy Deploy application using Ansible
133+
ls List directory contents with details
134+
setup Set up the development environment
135+
```
136+
137+
With simple string format (default descriptions):
138+
139+
```console
140+
$ j shell
141+
Usage: j shell [OPTIONS] COMMAND [ARGS]...
142+
143+
Shell command executor
144+
145+
Commands:
146+
deploy Execute the deploy shell method
147+
ls Execute the ls shell method
148+
setup Execute the setup shell method
149+
```
150+
151+
**Mixed format example:**
152+
153+
```yaml
154+
methods:
155+
deploy:
156+
command: "ansible-playbook deploy.yml"
157+
description: "Deploy using Ansible"
158+
restart: "systemctl restart myapp" # Simple format
159+
```
160+
161+
Results in:
162+
```console
163+
Commands:
164+
deploy Deploy using Ansible
165+
restart Execute the restart shell method
83166
```
84167

85168
### CLI Command Usage

0 commit comments

Comments
 (0)