Skip to content
Merged
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
32 changes: 26 additions & 6 deletions hermes_cli/cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,30 @@ def _cron_api(**kwargs):
return json.loads(cronjob_tool(**kwargs))


def _warn_if_gateway_not_running() -> None:
"""Warn that scheduled jobs won't fire unless the gateway is running.

The cron ticker only runs inside the gateway (``_start_cron_ticker`` in
gateway/run.py); there is no standalone cron daemon. Without a running
gateway, ``next_run_at`` passes but jobs never fire and ``last_run_at``
stays null — the most common cron support report (#51038). Surfacing this
at create/list time, when the user is right there, prevents it.
"""
try:
from hermes_cli.gateway import find_gateway_pids

if find_gateway_pids():
return
except Exception:
# If we can't determine gateway state, stay quiet rather than nag.
return

print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
print(color(" Start it with: hermes gateway install", Colors.DIM))
print(color(" sudo hermes gateway install --system # Linux servers", Colors.DIM))
print(color(" Check status: hermes cron status", Colors.DIM))


def cron_list(show_all: bool = False):
"""List all scheduled jobs."""
from cron.jobs import list_jobs
Expand Down Expand Up @@ -137,12 +161,7 @@ def cron_list(show_all: bool = False):

print()

from hermes_cli.gateway import find_gateway_pids
if not find_gateway_pids():
print(color(" ⚠ Gateway is not running — jobs won't fire automatically.", Colors.YELLOW))
print(color(" Start it with: hermes gateway install", Colors.DIM))
print(color(" sudo hermes gateway install --system # Linux servers", Colors.DIM))
print()
_warn_if_gateway_not_running()


def cron_tick():
Expand Down Expand Up @@ -276,6 +295,7 @@ def cron_create(args):
if job_data.get("workdir"):
print(f" Workdir: {job_data['workdir']}")
print(f" Next run: {result['next_run_at']}")
_warn_if_gateway_not_running()
return 0


Expand Down
57 changes: 57 additions & 0 deletions tests/hermes_cli/test_cron.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,60 @@ def test_list_does_not_crash_when_repeat_is_null(self, tmp_cron_dir, capsys):

out = capsys.readouterr().out
assert "Repeat: ∞" in out


class TestGatewayNotRunningWarning:
"""`cron create` / `cron list` must warn when the gateway (and thus the
cron ticker) isn't running, since jobs only fire inside the gateway.
Regression guard for #51038 — the most common cron 'jobs never fired'
report was simply a gateway that was never started.
"""

def test_create_warns_when_gateway_absent(self, tmp_cron_dir, capsys, monkeypatch):
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [])
cron_command(
Namespace(
cron_command="create",
schedule="0 11 * * *",
prompt="Daily report",
name="Daily 1130",
deliver=None,
repeat=None,
skill=None,
skills=None,
script=None,
workdir=None,
no_agent=False,
)
)
out = capsys.readouterr().out
assert "Created job" in out
assert "Gateway is not running" in out

def test_create_silent_when_gateway_running(self, tmp_cron_dir, capsys, monkeypatch):
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [4242])
cron_command(
Namespace(
cron_command="create",
schedule="0 11 * * *",
prompt="Daily report",
name="Daily 1130",
deliver=None,
repeat=None,
skill=None,
skills=None,
script=None,
workdir=None,
no_agent=False,
)
)
out = capsys.readouterr().out
assert "Created job" in out
assert "Gateway is not running" not in out

def test_list_warns_when_gateway_absent(self, tmp_cron_dir, capsys, monkeypatch):
create_job(prompt="Daily report", schedule="0 11 * * *")
monkeypatch.setattr("hermes_cli.gateway.find_gateway_pids", lambda: [])
cron_command(Namespace(cron_command="list", all=True))
out = capsys.readouterr().out
assert "Gateway is not running" in out
Loading