Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion site-docs/docs/commands/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Show the current state of a deployment instance: git info and systemd unit statu
## Signature

```bash
deploy [--config FILE] status <instance_name> [<ssh_host>] [-p PORT]
deploy [--config FILE] status <instance_name> [<ssh_host>] [-p PORT] [--watch]
```

## Arguments
Expand All @@ -20,6 +20,7 @@ deploy [--config FILE] status <instance_name> [<ssh_host>] [-p PORT]
| Option | Default | Description |
|--------|---------|-------------|
| `-p`, `--port` | — | SSH port on the remote host |
| `--watch` | `False` | Stream service logs with journalctl after showing status |

## Output

Expand All @@ -38,6 +39,7 @@ Unit: active (running) since 2026-03-09 08:12:03
- `git rev-parse --abbrev-ref HEAD` → branch
- `git rev-parse --short HEAD` → commit hash
3. **Unit status** — via `systemctl --user show`.
4. If **`--watch`**, stream live logs via `journalctl --user -u <instance_name> -f`. Press `Ctrl+C` to exit.

## Exit codes

Expand All @@ -58,4 +60,7 @@ deploy status odoo-myproject-production deploy@myserver.example.com

# Custom SSH port
deploy status odoo-myproject-production -p 2222

# Show status then tail the logs
deploy status odoo-myproject-production --watch
```
62 changes: 62 additions & 0 deletions tests/test_status_watch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest
from click.testing import CliRunner

from trobz_deploy.command.status import status


@pytest.fixture
def runner():
return CliRunner()


def _executor_mock():
mock = MagicMock()
mock.capture.side_effect = [
"git@github.com:trobz/myapp.git", # remote url
"main", # branch
"abc1234", # commit
"ActiveState=active\nSubState=running\nActiveEnterTimestamp=", # systemd props
]
return mock


def _invoke(runner, extra_args: list[str], side_effect=None):
with (
patch("trobz_deploy.command.status.Executor") as MockExecutor,
patch("trobz_deploy.command.status.load_config", return_value={}),
):
mock_exec = _executor_mock()
if side_effect:
mock_exec.stream.side_effect = side_effect
MockExecutor.return_value = mock_exec
result = runner.invoke(
status,
["service-myapp-production", *extra_args],
obj={"config": "", "verbose": False},
)
return result, mock_exec


def test_watch_streams_journalctl_after_status(runner):
result, mock_exec = _invoke(runner, ["--watch"])

assert result.exit_code == 0
mock_exec.stream.assert_called_once_with("journalctl --user -u service-myapp-production -f")


def test_no_watch_does_not_stream(runner):
result, mock_exec = _invoke(runner, [])

assert result.exit_code == 0
mock_exec.stream.assert_not_called()


def test_watch_handles_keyboard_interrupt(runner):
result, mock_exec = _invoke(runner, ["--watch"], side_effect=KeyboardInterrupt)

assert result.exit_code == 0
mock_exec.stream.assert_called_once()
15 changes: 15 additions & 0 deletions trobz_deploy/command/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,20 @@
default=None,
help="SSH port on the remote host.",
)
@click.option(
"--watch",
"watch",
is_flag=True,
default=False,
help="Stream service logs with journalctl after showing status.",
)
@click.pass_context
def status(
ctx: click.Context,
instance_name: str,
ssh_host: str | None,
ssh_port: int | None,
watch: bool,
) -> None:
"""Show status of a deployment instance."""
cfg = load_config(ctx.obj["config"], instance_name)
Expand Down Expand Up @@ -77,3 +85,10 @@ def status(
click.echo(f"Remote: {remote_url}")
click.echo(f"Branch: {branch} ({commit})")
click.echo(f"Unit: {unit_line}")

if watch:
click.secho("\nWatching service logs (Ctrl+C to stop)…", fg="cyan")
try:
executor.stream(f"journalctl --user -u {instance_name} -f")
except KeyboardInterrupt:
click.echo()