Skip to content

Commit 77b7ddf

Browse files
committed
cli/load(fix[here]): Reject multi-workspace here invocations
why: --here is a current-window workflow. Accepting multiple workspace files silently changes behavior for earlier entries and leaves behind unexpected sessions instead of failing fast. what: - reject --here when more than one workspace file is provided - clarify the parser help text for the single-workspace contract - add CLI coverage that exits before any workspace is loaded - document the single-workspace restriction in the load guide
1 parent 9c6354e commit 77b7ddf

3 files changed

Lines changed: 70 additions & 1 deletion

File tree

docs/cli/load.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ $ tmuxp load --here .
264264

265265
When used, tmuxp builds the workspace panes inside the current window rather than spawning a new session.
266266

267+
`--here` only supports a single workspace file per invocation.
268+
267269
```{note}
268270
When `--here` needs to provision a directory, environment, or shell, tmuxp uses tmux primitives (`set-environment` and `respawn-pane`) instead of typing `cd` / `export` into the pane. If provisioning is needed, tmux will replace the active pane process before the workspace commands run, so long-running child processes in that pane can be terminated.
269271
```

src/tmuxp/cli/load.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,10 @@ def create_load_subparser(parser: argparse.ArgumentParser) -> argparse.ArgumentP
10271027
"--here",
10281028
dest="here",
10291029
action="store_true",
1030-
help="use the current window for the first workspace window",
1030+
help=(
1031+
"use the current window for the first workspace window "
1032+
"(single workspace only)"
1033+
),
10311034
)
10321035
parser.add_argument(
10331036
"--no-shell-command-before",
@@ -1170,6 +1173,13 @@ def command_load(
11701173
sys.exit()
11711174
return
11721175

1176+
if args.here and len(args.workspace_files) > 1:
1177+
msg = "--here only supports one workspace file"
1178+
if parser is not None:
1179+
parser.error(msg)
1180+
tmuxp_echo(cli_colors.error("[Error]") + f" {msg}")
1181+
sys.exit(2)
1182+
11731183
# Parse --set KEY=VALUE args into template context
11741184
template_context: dict[str, str] | None = None
11751185
if args.set:

tests/cli/test_load.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
from tmuxp._internal.config_reader import ConfigReader
2020
from tmuxp._internal.private_path import PrivatePath
2121
from tmuxp.cli.load import (
22+
CLILoadNamespace,
2223
_dispatch_build,
2324
_load_append_windows_to_current_session,
2425
_load_attached,
26+
command_load,
2527
load_plugins,
2628
load_workspace,
2729
)
@@ -1688,6 +1690,61 @@ def test_load_here_and_detached_mutually_exclusive() -> None:
16881690
parser.parse_args(["load", "--here", "-d", "myconfig"])
16891691

16901692

1693+
class MultiWorkspaceHereFixture(t.NamedTuple):
1694+
"""Fixture for invalid multi-workspace --here invocations."""
1695+
1696+
test_id: str
1697+
cli_args: list[str]
1698+
expected_exit_code: int
1699+
expected_error: str
1700+
1701+
1702+
MULTI_WORKSPACE_HERE_FIXTURES: list[MultiWorkspaceHereFixture] = [
1703+
MultiWorkspaceHereFixture(
1704+
test_id="rejects-two-workspaces",
1705+
cli_args=["load", "--here", "first", "second"],
1706+
expected_exit_code=2,
1707+
expected_error="--here only supports one workspace file",
1708+
),
1709+
]
1710+
1711+
1712+
@pytest.mark.parametrize(
1713+
list(MultiWorkspaceHereFixture._fields),
1714+
MULTI_WORKSPACE_HERE_FIXTURES,
1715+
ids=[fixture.test_id for fixture in MULTI_WORKSPACE_HERE_FIXTURES],
1716+
)
1717+
def test_load_here_rejects_multiple_workspace_files(
1718+
monkeypatch: pytest.MonkeyPatch,
1719+
capsys: pytest.CaptureFixture[str],
1720+
test_id: str,
1721+
cli_args: list[str],
1722+
expected_exit_code: int,
1723+
expected_error: str,
1724+
) -> None:
1725+
"""--here exits before load_workspace when multiple files are provided."""
1726+
parser = cli.create_parser()
1727+
args = t.cast(CLILoadNamespace, parser.parse_args(cli_args))
1728+
load_calls: list[str] = []
1729+
1730+
monkeypatch.setattr(
1731+
"tmuxp.cli.load.util.oh_my_zsh_auto_title",
1732+
lambda: None,
1733+
)
1734+
monkeypatch.setattr(
1735+
"tmuxp.cli.load.load_workspace",
1736+
lambda *args, **kwargs: load_calls.append(test_id),
1737+
)
1738+
1739+
with pytest.raises(SystemExit) as excinfo:
1740+
command_load(args, parser=parser)
1741+
1742+
result = capsys.readouterr()
1743+
assert excinfo.value.code == expected_exit_code
1744+
assert expected_error in result.err
1745+
assert load_calls == []
1746+
1747+
16911748
def test_load_append_and_detached_mutually_exclusive() -> None:
16921749
"""--append and -d cannot be used together."""
16931750
from tmuxp.cli import create_parser

0 commit comments

Comments
 (0)