* Revert "fix(cron): scope job execution to its owning profile (#32091 follow-up) (#50993)" This reverts commit660e36f097. * Revert "fix(cron): anchor cron storage at the default root home (not the active profile)" This reverts commita5c09fd176.
This commit is contained in:
parent
2a10b8384a
commit
bb7ff7dc30
8 changed files with 14 additions and 423 deletions
|
|
@ -316,17 +316,9 @@ def _get_hermes_home() -> Path:
|
|||
|
||||
|
||||
def _get_lock_paths() -> tuple[Path, Path]:
|
||||
"""Resolve cron lock paths at call time so profile/env changes are honored.
|
||||
|
||||
Anchored on the DEFAULT ROOT home (not the active profile), matching the
|
||||
jobs store in cron.jobs (which uses get_default_hermes_root). The tick lock
|
||||
is storage-coordination — it must live next to the single jobs.json so that
|
||||
tickers running under different profiles share one lock and can't
|
||||
double-fire the relocated store (#32091). Execution context (.env,
|
||||
config.yaml, scripts) stays profile-aware via _get_hermes_home().
|
||||
"""
|
||||
from hermes_constants import get_default_hermes_root
|
||||
lock_dir = (_hermes_home or get_default_hermes_root()) / "cron"
|
||||
"""Resolve cron lock paths at call time so profile/env changes are honored."""
|
||||
hermes_home = _get_hermes_home()
|
||||
lock_dir = hermes_home / "cron"
|
||||
return lock_dir, lock_dir / ".tick.lock"
|
||||
|
||||
|
||||
|
|
@ -1857,32 +1849,6 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
|
|||
os.environ["TERMINAL_CWD"] = _job_workdir
|
||||
logger.info("Job '%s': using workdir %s", job_id, _job_workdir)
|
||||
|
||||
# Scope this job's execution to its owning profile's HERMES_HOME (#32091).
|
||||
# The shared root store holds every profile's jobs, but a job must run with
|
||||
# the .env / config.yaml / credentials of the profile that created it — not
|
||||
# whichever profile's ticker happened to pick it up. We set both the
|
||||
# in-process ContextVar override (consumed by _get_hermes_home() for the
|
||||
# config/.env/script loads below) AND os.environ["HERMES_HOME"] (inherited
|
||||
# by any child subprocess the agent spawns). tick() routes profile-scoped
|
||||
# jobs to the single-worker sequential pool, so mutating os.environ here is
|
||||
# safe — they never overlap. Restored in the finally block.
|
||||
from cron.jobs import resolve_profile_home
|
||||
from hermes_constants import set_hermes_home_override
|
||||
_job_profile = (job.get("profile") or "default").strip() or "default"
|
||||
_profile_home = resolve_profile_home(_job_profile)
|
||||
_prior_hermes_home = os.environ.get("HERMES_HOME", "_UNSET_")
|
||||
_hermes_home_token = None
|
||||
if _profile_home is not None and _profile_home != _get_hermes_home().resolve():
|
||||
os.environ["HERMES_HOME"] = str(_profile_home)
|
||||
_hermes_home_token = set_hermes_home_override(str(_profile_home))
|
||||
logger.info("Job '%s': executing under profile %r (HERMES_HOME=%s)",
|
||||
job_id, _job_profile, _profile_home)
|
||||
elif _profile_home is None and _job_profile != "default":
|
||||
logger.warning(
|
||||
"Job '%s': profile %r no longer exists — running under the "
|
||||
"ticker's profile instead", job_id, _job_profile,
|
||||
)
|
||||
|
||||
try:
|
||||
# Re-read .env and config.yaml fresh every run so provider/key
|
||||
# changes take effect without a gateway restart.
|
||||
|
|
@ -2294,19 +2260,6 @@ def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
|
|||
os.environ.pop("TERMINAL_CWD", None)
|
||||
else:
|
||||
os.environ["TERMINAL_CWD"] = _prior_terminal_cwd
|
||||
# Restore HERMES_HOME to the ticker's value when this job overrode it
|
||||
# for profile-scoped execution (#32091). Mirrors the TERMINAL_CWD
|
||||
# restore above; the sequential pool guarantees no overlap.
|
||||
if _hermes_home_token is not None:
|
||||
try:
|
||||
from hermes_constants import reset_hermes_home_override
|
||||
reset_hermes_home_override(_hermes_home_token)
|
||||
except Exception:
|
||||
pass
|
||||
if _prior_hermes_home == "_UNSET_":
|
||||
os.environ.pop("HERMES_HOME", None)
|
||||
else:
|
||||
os.environ["HERMES_HOME"] = _prior_hermes_home
|
||||
# Clean up ContextVar session/delivery state for this job.
|
||||
clear_session_vars(_ctx_tokens)
|
||||
for _var_name in _cron_delivery_vars:
|
||||
|
|
@ -2512,26 +2465,12 @@ def tick(verbose: bool = True, adapters=None, loop=None, sync: bool = True) -> i
|
|||
body."""
|
||||
return run_one_job(job, adapters=adapters, loop=loop, verbose=verbose)
|
||||
|
||||
# Partition due jobs: those that mutate process-global os.environ
|
||||
# inside run_job MUST run sequentially to avoid corrupting each other.
|
||||
# Two cases mutate env:
|
||||
# - a per-job workdir sets os.environ["TERMINAL_CWD"].
|
||||
# - a per-job profile whose HERMES_HOME differs from the ticker's
|
||||
# sets os.environ["HERMES_HOME"] to scope execution (#32091).
|
||||
# Jobs that need neither leave env untouched and stay parallel-safe.
|
||||
def _needs_sequential(j: dict) -> bool:
|
||||
if (j.get("workdir") or "").strip():
|
||||
return True
|
||||
prof = (j.get("profile") or "default").strip() or "default"
|
||||
try:
|
||||
from cron.jobs import resolve_profile_home
|
||||
phome = resolve_profile_home(prof)
|
||||
except Exception:
|
||||
phome = None
|
||||
return phome is not None and phome != _get_hermes_home().resolve()
|
||||
|
||||
sequential_jobs = [j for j in due_jobs if _needs_sequential(j)]
|
||||
parallel_jobs = [j for j in due_jobs if not _needs_sequential(j)]
|
||||
# Partition due jobs: those with a per-job workdir mutate
|
||||
# os.environ["TERMINAL_CWD"] inside run_job, which is process-global —
|
||||
# so they MUST run sequentially to avoid corrupting each other. Jobs
|
||||
# without a workdir leave env untouched and stay parallel-safe.
|
||||
sequential_jobs = [j for j in due_jobs if (j.get("workdir") or "").strip()]
|
||||
parallel_jobs = [j for j in due_jobs if not (j.get("workdir") or "").strip()]
|
||||
|
||||
_results: list = []
|
||||
_all_futures: list = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue