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
18 changes: 15 additions & 3 deletions cron/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -2345,6 +2345,19 @@ def _windows_cron_python_invocation(python_exe: str) -> tuple[str, dict[str, str
return str(interpreter), env_overlay


def _cron_script_argv(path: Path) -> str:
"""Return the best shell-script argument for cron.

Native Windows cron uses Git Bash for `.sh/.bash` scripts. Bash expects
forward-slash paths (for example ``C:/Users/...``), so normalise only on
win32 to avoid backslash-related launch failures while preserving normal
POSIX behavior.
"""
if sys.platform == "win32":
return path.as_posix()
return str(path)


def _run_job_script(
script_path: str,
workdir: Optional[str] = None,
Expand Down Expand Up @@ -2440,13 +2453,12 @@ def _run_job_script(
f"Cannot run .sh/.bash script {path.name!r}: bash not found on PATH. "
"On Windows, install Git for Windows (which ships Git Bash) "
"or rewrite the script as Python (.py)."
)
argv = [_bash, str(path)]
)
argv = [_bash, _cron_script_argv(path)]
env_overlay: dict[str, str] = {}
else:
python_exe, env_overlay = _windows_cron_python_invocation(sys.executable)
argv = [python_exe, str(path)]

try:
from tools.environments.local import build_subprocess_env

Expand Down
18 changes: 18 additions & 0 deletions tests/cron/test_cron_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,24 @@ def fake_run(argv, **kwargs):
assert str(site_packages) in env["PYTHONPATH"]


def test_windows_script_arg_normalizes_backslashes(self, monkeypatch):
from pathlib import PureWindowsPath
from cron.scheduler import _cron_script_argv

monkeypatch.setattr("cron.scheduler.sys.platform", "win32")

windows_path = PureWindowsPath(r"C:\\Users\\Example\\.hermes\\scripts\\watch.sh")
assert _cron_script_argv(windows_path) == "C:/Users/Example/.hermes/scripts/watch.sh"

def test_non_windows_script_arg_preserves_posix_path(self, monkeypatch):
from pathlib import Path
from cron.scheduler import _cron_script_argv

monkeypatch.setattr("cron.scheduler.sys.platform", "linux")

posix_path = Path("/Users/Example/.hermes/scripts/watch.sh")
assert _cron_script_argv(posix_path) == str(posix_path)

def test_non_windows_script_preserves_default_text_decoding(self, cron_env, monkeypatch):
from cron import scheduler as sched_mod
from cron.scheduler import _run_job_script
Expand Down
Loading