Add tmux pane target for container commands
This commit is contained in:
@@ -460,6 +460,17 @@ Dla pobranej karty `inf bss` możesz uruchomić debugowanie w obu profilach:
|
|||||||
./rvctl debug host 4
|
./rvctl debug host 4
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Jeżeli pracujesz już w tmuksie na hoście, możesz wysłać start kontenera do
|
||||||
|
konkretnego panelu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./rvctl debug rv32i 4 pane 0
|
||||||
|
./rvctl debug rv32i 4 --pane node:0
|
||||||
|
```
|
||||||
|
|
||||||
|
`pane 0` oznacza panel `0` w bieżącym oknie tmuxa. `node:0` oznacza okno
|
||||||
|
`node`, panel `0`.
|
||||||
|
|
||||||
Profil `host` ma też szybkie uruchomienie bez sesji debuggera:
|
Profil `host` ma też szybkie uruchomienie bez sesji debuggera:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -109,9 +109,18 @@ Przydatne warianty:
|
|||||||
./rvctl debug host bss 4
|
./rvctl debug host bss 4
|
||||||
./rvctl debug host 4 --editor vim
|
./rvctl debug host 4 --editor vim
|
||||||
./rvctl debug host 4 --instance host-bss-t4
|
./rvctl debug host 4 --instance host-bss-t4
|
||||||
|
./rvctl debug rv32i 4 --pane 0
|
||||||
|
./rvctl debug rv32i 4 pane node:0
|
||||||
./rvctl debug host 4 --dry-run
|
./rvctl debug host 4 --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`--pane TARGET` oraz forma `pane TARGET` wysyłają wygenerowaną komendę do
|
||||||
|
istniejącego panelu tmuxa na hoście przez `tmux send-keys`. Skróty targetów:
|
||||||
|
|
||||||
|
- `0` - panel `0` w bieżącym oknie,
|
||||||
|
- `node:0` - okno `node`, panel `0`,
|
||||||
|
- `%3` albo `:node.0` - natywny target tmuxa.
|
||||||
|
|
||||||
## `run`
|
## `run`
|
||||||
|
|
||||||
Na razie `run` jest wdrożone dla profilu `host`. Buduje wybrany task natywnie i
|
Na razie `run` jest wdrożone dla profilu `host`. Buduje wybrany task natywnie i
|
||||||
|
|||||||
@@ -667,6 +667,8 @@ Uruchamia debugowanie zadania w kontenerze. Selektor może być pełny albo kró
|
|||||||
./rvctl debug host inf bss 1
|
./rvctl debug host inf bss 1
|
||||||
./rvctl debug rv32i 4
|
./rvctl debug rv32i 4
|
||||||
./rvctl debug host 4
|
./rvctl debug host 4
|
||||||
|
./rvctl debug rv32i 4 --pane 0
|
||||||
|
./rvctl debug rv32i 4 pane node:0
|
||||||
```
|
```
|
||||||
|
|
||||||
`rv32i` uruchamia symulator Hazard3, `gdb-multiarch` i `nvim`. `host` kompiluje
|
`rv32i` uruchamia symulator Hazard3, `gdb-multiarch` i `nvim`. `host` kompiluje
|
||||||
@@ -679,8 +681,14 @@ Przełączniki:
|
|||||||
- `--task TASK` - nadpisuje zadanie z selektora albo z `workspace.json`
|
- `--task TASK` - nadpisuje zadanie z selektora albo z `workspace.json`
|
||||||
- `--instance NAME` - ustawia nazwę instancji kontenera/tmuxa
|
- `--instance NAME` - ustawia nazwę instancji kontenera/tmuxa
|
||||||
- `--editor nvim|vim` - wybiera edytor w kontenerze
|
- `--editor nvim|vim` - wybiera edytor w kontenerze
|
||||||
|
- `--pane TARGET` - wysyła wygenerowaną komendę do istniejącego panelu tmuxa
|
||||||
|
na hoście
|
||||||
- `--dry-run` - pokazuje komendę bez uruchamiania Dockera
|
- `--dry-run` - pokazuje komendę bez uruchamiania Dockera
|
||||||
|
|
||||||
|
`--pane 0` oznacza panel `0` w bieżącym oknie tmuxa. `pane node:0` oznacza
|
||||||
|
okno `node`, panel `0`. Natywne targety tmuxa, na przykład `%3` albo
|
||||||
|
`:node.0`, też są obsługiwane.
|
||||||
|
|
||||||
## `run host`
|
## `run host`
|
||||||
|
|
||||||
Buduje i uruchamia wybrane zadanie natywnie w kontenerze `host`.
|
Buduje i uruchamia wybrane zadanie natywnie w kontenerze `host`.
|
||||||
@@ -688,6 +696,7 @@ Buduje i uruchamia wybrane zadanie natywnie w kontenerze `host`.
|
|||||||
```bash
|
```bash
|
||||||
./rvctl run host inf bss 1
|
./rvctl run host inf bss 1
|
||||||
./rvctl run host 4
|
./rvctl run host 4
|
||||||
|
./rvctl run host 4 --pane node:0
|
||||||
```
|
```
|
||||||
|
|
||||||
Profil `rv32i` pracuje obecnie przez `debug rv32i`, bo wymaga symulatora
|
Profil `rv32i` pracuje obecnie przez `debug rv32i`, bo wymaga symulatora
|
||||||
@@ -701,6 +710,7 @@ Otwiera shell w wybranym profilu kontenera z podmontowaną kartą.
|
|||||||
./rvctl shell rv32i inf bss
|
./rvctl shell rv32i inf bss
|
||||||
./rvctl shell host inf bss
|
./rvctl shell host inf bss
|
||||||
./rvctl shell host --dry-run
|
./rvctl shell host --dry-run
|
||||||
|
./rvctl shell rv32i --pane 0
|
||||||
```
|
```
|
||||||
|
|
||||||
## `list-series`
|
## `list-series`
|
||||||
|
|||||||
@@ -3533,6 +3533,60 @@ def resolve_env_target(
|
|||||||
return series_name, card_name, task_name
|
return series_name, card_name, task_name
|
||||||
|
|
||||||
|
|
||||||
|
def split_pane_selector(values: list[str], explicit_pane: str | None = None) -> tuple[list[str], str | None]:
|
||||||
|
if "pane" not in values:
|
||||||
|
return values, explicit_pane
|
||||||
|
if explicit_pane:
|
||||||
|
raise SystemExit("Pass pane target either as '--pane TARGET' or as 'pane TARGET', not both.")
|
||||||
|
|
||||||
|
index = values.index("pane")
|
||||||
|
pane_values = values[index + 1 :]
|
||||||
|
if len(pane_values) != 1:
|
||||||
|
raise SystemExit("Use: pane TARGET, for example 'pane 0' or 'pane node:0'.")
|
||||||
|
return values[:index], pane_values[0]
|
||||||
|
|
||||||
|
|
||||||
|
def tmux_pane_target(raw_target: str) -> str:
|
||||||
|
target = raw_target.strip()
|
||||||
|
if not target:
|
||||||
|
raise SystemExit("Empty tmux pane target.")
|
||||||
|
if re.fullmatch(r"\d+", target):
|
||||||
|
return f":.{target}"
|
||||||
|
if re.fullmatch(r"%\d+", target):
|
||||||
|
return target
|
||||||
|
window_pane_match = re.fullmatch(r"([^:.]+):(\d+)", target)
|
||||||
|
if window_pane_match:
|
||||||
|
window_name, pane_index = window_pane_match.groups()
|
||||||
|
return f":{window_name}.{pane_index}"
|
||||||
|
return target
|
||||||
|
|
||||||
|
|
||||||
|
def send_command_to_tmux_pane(shell_command: str, raw_target: str, dry_run: bool = False) -> dict[str, str]:
|
||||||
|
target = tmux_pane_target(raw_target)
|
||||||
|
details = {
|
||||||
|
"pane": raw_target,
|
||||||
|
"tmux_target": target,
|
||||||
|
"tmux_send": f"tmux send-keys -t {shlex.quote(target)} -l {shlex.quote(shell_command)}; tmux send-keys -t {shlex.quote(target)} C-m",
|
||||||
|
}
|
||||||
|
if dry_run:
|
||||||
|
return details
|
||||||
|
|
||||||
|
resolved = subprocess.run(
|
||||||
|
["tmux", "display-message", "-p", "-t", target, "#{session_name}:#{window_name}.#{pane_index}"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if resolved.returncode != 0:
|
||||||
|
raise SystemExit(f"Missing tmux pane target '{raw_target}' ({target}): {resolved.stderr.strip()}")
|
||||||
|
|
||||||
|
subprocess.run(["tmux", "send-keys", "-t", target, "-l", shell_command], check=True)
|
||||||
|
subprocess.run(["tmux", "send-keys", "-t", target, "C-m"], check=True)
|
||||||
|
details["tmux_resolved"] = resolved.stdout.strip()
|
||||||
|
return details
|
||||||
|
|
||||||
|
|
||||||
def default_env_instance(profile: str, series_name: str, card_name: str, task_name: str, action: str) -> str:
|
def default_env_instance(profile: str, series_name: str, card_name: str, task_name: str, action: str) -> str:
|
||||||
task_part = task_branch_part(task_name).lower()
|
task_part = task_branch_part(task_name).lower()
|
||||||
return slug_value(f"{profile}-{series_name}-{card_name}-{task_part}-{action}", "environment instance")
|
return slug_value(f"{profile}-{series_name}-{card_name}-{task_part}-{action}", "environment instance")
|
||||||
@@ -3619,8 +3673,23 @@ def env_action_command(
|
|||||||
|
|
||||||
|
|
||||||
def print_env_plan(details: dict[str, str]) -> None:
|
def print_env_plan(details: dict[str, str]) -> None:
|
||||||
for key in ["profile", "service", "selector", "task", "card_path", "tools_root", "instance", "command"]:
|
keys = [
|
||||||
print(f"{key}\t{details.get(key, '')}")
|
"profile",
|
||||||
|
"service",
|
||||||
|
"selector",
|
||||||
|
"task",
|
||||||
|
"card_path",
|
||||||
|
"tools_root",
|
||||||
|
"instance",
|
||||||
|
"pane",
|
||||||
|
"tmux_target",
|
||||||
|
"tmux_resolved",
|
||||||
|
"tmux_send",
|
||||||
|
"command",
|
||||||
|
]
|
||||||
|
for key in keys:
|
||||||
|
if key == "command" or details.get(key):
|
||||||
|
print(f"{key}\t{details.get(key, '')}")
|
||||||
|
|
||||||
|
|
||||||
def run_env(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
def run_env(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
||||||
@@ -3650,7 +3719,8 @@ def run_env(config: WorkspaceConfig, args: argparse.Namespace) -> None:
|
|||||||
|
|
||||||
def run_profile_action(config: WorkspaceConfig, args: argparse.Namespace, action: str) -> None:
|
def run_profile_action(config: WorkspaceConfig, args: argparse.Namespace, action: str) -> None:
|
||||||
profile = normalize_env_profile(args.profile)
|
profile = normalize_env_profile(args.profile)
|
||||||
series_name, card_name, task_name = resolve_env_target(config, args.selector, getattr(args, "task", None))
|
selector_values, pane_target = split_pane_selector(args.selector, getattr(args, "pane", None))
|
||||||
|
series_name, card_name, task_name = resolve_env_target(config, selector_values, getattr(args, "task", None))
|
||||||
shell_command, details = env_action_command(
|
shell_command, details = env_action_command(
|
||||||
config,
|
config,
|
||||||
profile,
|
profile,
|
||||||
@@ -3661,6 +3731,14 @@ def run_profile_action(config: WorkspaceConfig, args: argparse.Namespace, action
|
|||||||
instance=getattr(args, "instance", None),
|
instance=getattr(args, "instance", None),
|
||||||
editor=getattr(args, "editor", None),
|
editor=getattr(args, "editor", None),
|
||||||
)
|
)
|
||||||
|
if pane_target:
|
||||||
|
pane_details = send_command_to_tmux_pane(shell_command, pane_target, dry_run=args.dry_run)
|
||||||
|
details.update(pane_details)
|
||||||
|
if args.dry_run:
|
||||||
|
print_env_plan(details)
|
||||||
|
return
|
||||||
|
print_env_plan(details)
|
||||||
|
return
|
||||||
if args.dry_run:
|
if args.dry_run:
|
||||||
print_env_plan(details)
|
print_env_plan(details)
|
||||||
return
|
return
|
||||||
@@ -3975,6 +4053,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
debug_parser.add_argument("--task", help="Task override. Defaults to selector or defaults.task.")
|
debug_parser.add_argument("--task", help="Task override. Defaults to selector or defaults.task.")
|
||||||
debug_parser.add_argument("--instance", help="Container/tmux instance name.")
|
debug_parser.add_argument("--instance", help="Container/tmux instance name.")
|
||||||
debug_parser.add_argument("--editor", choices=["nvim", "vim"], help="Editor inside the container.")
|
debug_parser.add_argument("--editor", choices=["nvim", "vim"], help="Editor inside the container.")
|
||||||
|
debug_parser.add_argument("--pane", help="Send the command to a host tmux pane, for example 0, node:0, :node.0 or %3.")
|
||||||
debug_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
debug_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
||||||
|
|
||||||
run_parser = subparsers.add_parser("run", help="Build and run selected task inside a container profile.")
|
run_parser = subparsers.add_parser("run", help="Build and run selected task inside a container profile.")
|
||||||
@@ -3982,6 +4061,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
run_parser.add_argument("selector", nargs="*", help="Optional selector: [task], [card task] or [series card task].")
|
run_parser.add_argument("selector", nargs="*", help="Optional selector: [task], [card task] or [series card task].")
|
||||||
run_parser.add_argument("--task", help="Task override. Defaults to selector or defaults.task.")
|
run_parser.add_argument("--task", help="Task override. Defaults to selector or defaults.task.")
|
||||||
run_parser.add_argument("--instance", help="Container instance name.")
|
run_parser.add_argument("--instance", help="Container instance name.")
|
||||||
|
run_parser.add_argument("--pane", help="Send the command to a host tmux pane, for example 0, node:0, :node.0 or %3.")
|
||||||
run_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
run_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
||||||
|
|
||||||
shell_parser = subparsers.add_parser("shell", help="Open a shell inside a container profile.")
|
shell_parser = subparsers.add_parser("shell", help="Open a shell inside a container profile.")
|
||||||
@@ -3989,6 +4069,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|||||||
shell_parser.add_argument("selector", nargs="*", help="Optional selector: [card] or [series card].")
|
shell_parser.add_argument("selector", nargs="*", help="Optional selector: [card] or [series card].")
|
||||||
shell_parser.add_argument("--task", help="Task used only for instance naming.")
|
shell_parser.add_argument("--task", help="Task used only for instance naming.")
|
||||||
shell_parser.add_argument("--instance", help="Container instance name.")
|
shell_parser.add_argument("--instance", help="Container instance name.")
|
||||||
|
shell_parser.add_argument("--pane", help="Send the command to a host tmux pane, for example 0, node:0, :node.0 or %3.")
|
||||||
shell_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
shell_parser.add_argument("--dry-run", action="store_true", help="Print command without starting Docker.")
|
||||||
|
|
||||||
tasks_parser = subparsers.add_parser("tasks", help="Shortcut for tasks in the default card.")
|
tasks_parser = subparsers.add_parser("tasks", help="Shortcut for tasks in the default card.")
|
||||||
|
|||||||
Reference in New Issue
Block a user