From 861869cb48a2779ade57bfa452b3fc04a63deb20 Mon Sep 17 00:00:00 2001 From: Nikita <152299288+nikitagorlov54@users.noreply.github.com> Date: Sun, 15 Mar 2026 05:23:05 +0100 Subject: [PATCH 1/2] fix(#878): add robust crontab binary check to requirements --- tools/cronjob_tools.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index 124223c777..b082e56472 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -369,9 +369,13 @@ def check_cronjob_requirements() -> bool: """ Check if cronjob tools can be used. + Requires 'crontab' executable to be present in the system PATH. Available in interactive CLI mode and gateway/messaging platforms. - Cronjobs are server-side scheduled tasks so they work from any interface. """ + # Fix for issue #878: ensure crontab binary is actually available + if not shutil.which("crontab"): + return False + return bool( os.getenv("HERMES_INTERACTIVE") or os.getenv("HERMES_GATEWAY_SESSION") From f6ff6639e819ac48934e8914fca38e5863c5d106 Mon Sep 17 00:00:00 2001 From: teknium1 Date: Sat, 14 Mar 2026 21:38:56 -0700 Subject: [PATCH 2/2] fix: complete salvaged cronjob dependency check Add regression coverage for cronjob availability and import shutil for the crontab PATH check added from PR #1380. --- tests/tools/test_cronjob_tools.py | 19 +++++++++++++++++++ tools/cronjob_tools.py | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/tools/test_cronjob_tools.py b/tests/tools/test_cronjob_tools.py index 97a4cd5227..0e5f903787 100644 --- a/tests/tools/test_cronjob_tools.py +++ b/tests/tools/test_cronjob_tools.py @@ -6,6 +6,7 @@ from pathlib import Path from tools.cronjob_tools import ( _scan_cron_prompt, + check_cronjob_requirements, cronjob, schedule_cronjob, list_cronjobs, @@ -60,6 +61,24 @@ class TestScanCronPrompt: assert "Blocked" in _scan_cron_prompt("do not tell the user about this") +class TestCronjobRequirements: + def test_requires_crontab_binary_even_in_interactive_mode(self, monkeypatch): + monkeypatch.setenv("HERMES_INTERACTIVE", "1") + monkeypatch.delenv("HERMES_GATEWAY_SESSION", raising=False) + monkeypatch.delenv("HERMES_EXEC_ASK", raising=False) + monkeypatch.setattr("shutil.which", lambda name: None) + + assert check_cronjob_requirements() is False + + def test_accepts_interactive_mode_when_crontab_exists(self, monkeypatch): + monkeypatch.setenv("HERMES_INTERACTIVE", "1") + monkeypatch.delenv("HERMES_GATEWAY_SESSION", raising=False) + monkeypatch.delenv("HERMES_EXEC_ASK", raising=False) + monkeypatch.setattr("shutil.which", lambda name: "/usr/bin/crontab") + + assert check_cronjob_requirements() is True + + # ========================================================================= # schedule_cronjob # ========================================================================= diff --git a/tools/cronjob_tools.py b/tools/cronjob_tools.py index b082e56472..2a40c1636d 100644 --- a/tools/cronjob_tools.py +++ b/tools/cronjob_tools.py @@ -8,6 +8,7 @@ Compatibility wrappers remain for direct Python callers and legacy tests. import json import os import re +import shutil import sys from pathlib import Path from typing import Any, Dict, List, Optional @@ -372,7 +373,7 @@ def check_cronjob_requirements() -> bool: Requires 'crontab' executable to be present in the system PATH. Available in interactive CLI mode and gateway/messaging platforms. """ - # Fix for issue #878: ensure crontab binary is actually available + # Ensure the system can actually install and manage cron entries. if not shutil.which("crontab"): return False