Skip to content

Commit 8c128e5

Browse files
committed
fix: propagate shell exit code instead of raising error (fixes #4)
1 parent 4d477a7 commit 8c128e5

4 files changed

Lines changed: 34 additions & 14 deletions

File tree

incant/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ def provision(ctx, name: Optional[str] = None):
7373
def shell(ctx, name: Optional[str]):
7474
"""Open a shell into an instance. If no name is given and there is only one instance, use it."""
7575
try:
76-
Incant(reporter=ctx.obj["REPORTER"], **ctx.obj["OPTIONS"]).shell(name)
76+
ret = Incant(reporter=ctx.obj["REPORTER"], **ctx.obj["OPTIONS"]).shell(name)
77+
sys.exit(ret)
7778
except IncantError as e:
7879
_handle_error(e, ctx.obj["REPORTER"])
7980

incant/incant.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def incant_init(self):
177177

178178
print(f"Example configuration written to {config_path}")
179179

180-
def shell(self, name: Optional[str] = None):
180+
def shell(self, name: Optional[str] = None) -> int:
181181
instance_name = name
182182
if not instance_name:
183183
instance_names = list(self.config_manager.instance_configs.keys())
@@ -189,4 +189,4 @@ def shell(self, name: Optional[str] = None):
189189
if instance_name not in self.config_manager.instance_configs:
190190
raise InstanceError(f"Instance '{instance_name}' not found in config")
191191

192-
self.incus.shell(instance_name)
192+
return self.incus.shell(instance_name)

incant/incus_cli.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -299,16 +299,14 @@ def file_push(self, file_push_config: FilePushConfig) -> None:
299299
)
300300
self._run_command(command, capture_output=False, quiet=file_push_config.quiet)
301301

302-
def shell(self, name: str) -> None:
302+
def shell(self, name: str) -> int:
303303
"""Opens an interactive shell in the specified Incus instance."""
304304
self.reporter.success(f"Opening shell in {name}...")
305-
try:
306-
subprocess.run( # nosec B603
307-
[self.incus_cmd, "shell", name],
308-
check=True,
309-
stdin=sys.stdin,
310-
stdout=sys.stdout,
311-
stderr=sys.stderr,
312-
)
313-
except subprocess.CalledProcessError as e:
314-
raise InstanceError(f"Failed to open shell in {name}: {e}") from e
305+
result = subprocess.run( # nosec B603
306+
[self.incus_cmd, "shell", name],
307+
check=False,
308+
stdin=sys.stdin,
309+
stdout=sys.stdout,
310+
stderr=sys.stderr,
311+
)
312+
return result.returncode

tests/test_incus_cli.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import subprocess
2+
from unittest.mock import patch
13
from incant.incus_cli import IncusCLI
24
from incant.reporter import Reporter
35

@@ -6,3 +8,22 @@ class TestIncusCLI:
68
def test_constructor(self):
79
reporter = Reporter()
810
IncusCLI(reporter=reporter)
11+
12+
def test_shell_handles_nonzero_exit(self):
13+
"""
14+
Verify that incus shell is called with check=False to avoid raising exception on exit code.
15+
"""
16+
reporter = Reporter()
17+
incus_cli = IncusCLI(reporter)
18+
19+
with patch("subprocess.run") as mock_run:
20+
mock_run.return_value.returncode = 1
21+
22+
# This should not raise any exception and return the exit code
23+
ret = incus_cli.shell("test-instance")
24+
assert ret == 1
25+
26+
# Verify called arguments
27+
args, kwargs = mock_run.call_args
28+
assert args[0] == ["incus", "shell", "test-instance"]
29+
assert kwargs.get("check") is False

0 commit comments

Comments
 (0)