Repository navigation
fix(cron): give POSIX cron scripts the generation environment - #123548
vladneshrus-cpu wants to merge 1 commit into
Conversation
The launcher starts the managed ("store") Python with PYTHONPATH popped, and that
interpreter's own site-packages carries only Hermes' own runtime. A cron .py script that
imports a third-party library therefore died with ModuleNotFoundError unless the script
re-added the paths itself: the Windows branch always installed a repo + generation
site-packages overlay, while the POSIX branch returned an empty dict.
Add _posix_dependency_env_overlay() (repo root + generation site-packages, plus any inherited
PYTHONPATH), gated on the managed environment actually existing so source checkouts and
portable runs keep today's behaviour. The interpreter stays sys.executable: it is already the
managed Python, so the Windows .pth bootstrap is not needed and stays Windows-only.
Covered by tests in tests/cron/test_cron_script.py, including an end-to-end case where a job
script imports a package that exists only in the generation environment.
|
🤖 Hermes Agent automated review: PR reviewed. Diff analyzed (164 lines changed). Check CI status and manual review recommended. |
|
Ran into the same behavior on a Linux install with a recent update (v46-era config). An hourly cron job using the bundled What I found while chasing it:
Workaround I used in the meantime, in case it helps others who can't wait for the merge: uv pip install --python ~/.hermes/installs/<hash>/environments/<hash>/venv/bin/python \
google-api-python-client google-auth-httplib2After that the job's search ran clean and delivered normally. (Worth noting for the fix: the doctor check for the staged venv passes as "Runtime venv staged" without listing which third-party packages are present, which is what made this hard to spot — a hint in the doctor output, or a re-probe of a known cron job, would catch regressions like this earlier.) 👍 to the PR. |
|
Thanks @vladneshrus-cpu. This is superseded by #124973, which is armed for rebase-merge. It fixes the same POSIX managed-store cron-script @gryknight9, thanks for the detailed report. After #124973 the script runs on that same staged venv's interpreter, so anything installed there imports fine, googleapiclient included. In your case the |
fix(cron): POSIX cron scripts lose the generation environment
Problem
On POSIX, a cron job whose script is Python (
.py) and imports a third-party library failswith
ModuleNotFoundError, even though the same script works when run by hand from thegeneration environment.
Root cause:
cron/scheduler_script.py._windows_cron_python_invocation()returns— an empty env overlay. The Windows branch instead builds
PYTHONPATH = <repo> + <generation site-packages>(plusVIRTUAL_ENVfor uv venvs).That overlay is what makes the child able to import anything beyond Hermes' own runtime:
the launcher (
<install>/.hermes/bin/hermes) starts the managed "store" Python withPYTHONPATHpopped (os.environ.pop('PYTHONPATH', None)), and that interpreter's ownsite-packages carries only Hermes itself. So the gateway's own process is fine (its
sys.pathis built by the launcher), but any child it spawns for a cron script inheritsneither the paths nor the overlay on POSIX.
Practical impact: every cron
.pyscript with third-party imports (Google API client,YAML, requests, …) had to re-add the paths itself at the top of the file — a maintenance
trap that breaks on every environment migration.
Fix
_posix_dependency_env_overlay(): returns{"PYTHONPATH": "<repo><sep><generation site-packages>[<sep><inherited PYTHONPATH>]"}using the same
pm.environmentsresolvers the Windows branch already trusts.It is gated on the generation environment actually existing, so source checkouts and
portable runs (no managed environment) keep today's behaviour instead of getting a
dangling
PYTHONPATH. Any probe failure returns{}— never fails a job._windows_cron_python_invocation()now returns that overlay insteadof
{}._script_argv()keeps the.pthbootstrap Windows-only (env_overlay and sys.platform == "win32"): on POSIX the interpreter is already the managed Python, so theplain invocation plus
PYTHONPATHis enough. Windows behaviour is byte-for-byte unchanged._run_job_script()already merged the overlay after the env sanitizer(
env.update(env_overlay)), so no change was needed there.Note on
.pthfilesPYTHONPATHentries are not processed as.pth. The only.pthin the generationenvironment that matters here is the editable install of Hermes itself, whose target (the
repo root) is added explicitly, so the overlay is sufficient on POSIX.
Verification
End-to-end, in a checkout whose
_run_job_scriptwas exercised by the same managedinterpreter the gateway uses (
HERMES_HOME= throwaway home,PYTHONPATHpopped, repo +generation site-packages only on
sys.path), with a probe job script doingimport googleapiclient, yaml:Script exited with code 1 / ModuleNotFoundError: No module named 'googleapiclient'PROBE_OK py=3.14.7 googleapiclient=<generation site-packages>/googleapiclient/__init__.py yaml=<…>/yaml/__init__.pyUnit/regression:
tests/cron/test_cron_script.py— newTestPosixScriptDependencyOverlaycovers the overlay contents, inherited
PYTHONPATH, the no-managed-environment case, theplain POSIX invocation (no
-cbootstrap) and an end-to-end child-process import. All fivefail without the fix (same
ModuleNotFoundErrorin the child) and pass with it.Scope / rollback
{}).Re-verified on current
mainPatch applied to
d0288be5b3(cleangit apply -3, no fuzz):tests/cron/test_cron_script.py -k posix→ 5 passed.tests/cron/{test_estop,test_execution_ledger,test_scheduled_occurrence,test_atomic_paused_creation,test_worktree_maintenance_isolation}.py→ 10 failed / 41 passed, byte-for-byte the same failure list as the unpatched tree — pre-existing, unrelated to this change.Fixes #123547