@@ -43,6 +43,17 @@ def _env_bool(name: str, default: bool = False) -> bool:
4343 return value in {"1" , "true" , "yes" , "y" , "on" }
4444
4545
46+ def _enabled_value (value : Any , * , default : bool = True ) -> bool :
47+ if value is None :
48+ return default
49+ text = str (value ).strip ().lower ()
50+ if not text :
51+ return default
52+ if text in {"0" , "false" , "no" , "n" , "off" }:
53+ return False
54+ return True
55+
56+
4657def _parse_day_of_month_field (raw : str ) -> set [int ] | None :
4758 text = str (raw or "" ).strip ()
4859 if not text or text in {"*" , "?" }:
@@ -79,19 +90,31 @@ def _parse_day_of_month_field(raw: str) -> set[int] | None:
7990 return days or None
8091
8192
82- def _runtime_target_scheduler () -> dict [str , Any ]:
93+ def _runtime_target_payload () -> dict [str , Any ]:
8394 raw = (os .environ .get ("RUNTIME_TARGET_JSON" ) or "" ).strip ()
8495 if not raw :
8596 return {}
8697 try :
8798 payload = json .loads (raw )
8899 except json .JSONDecodeError :
89100 return {}
101+ return payload if isinstance (payload , dict ) else {}
102+
103+
104+ def _runtime_target_enabled () -> bool :
105+ value : Any = os .environ .get ("RUNTIME_TARGET_ENABLED" )
106+ if value is None :
107+ value = _runtime_target_payload ().get ("runtime_target_enabled" )
108+ return _enabled_value (value , default = True )
109+
110+
111+ def _runtime_target_scheduler () -> dict [str , Any ]:
112+ payload = _runtime_target_payload ()
90113 scheduler = payload .get ("scheduler" ) if isinstance (payload , dict ) else None
91114 return scheduler if isinstance (scheduler , dict ) else {}
92115
93116
94- def _heartbeat_skip_reason_for_schedule (now : dt .datetime ) -> str | None :
117+ def _heartbeat_skip_reason_for_schedule (since : dt . datetime , now : dt .datetime ) -> str | None :
95118 scheduler = _runtime_target_scheduler ()
96119 cron = str (scheduler .get ("main_time" ) or "" ).strip ()
97120 fields = cron .split ()
@@ -106,13 +129,18 @@ def _heartbeat_skip_reason_for_schedule(now: dt.datetime) -> str | None:
106129 except ZoneInfoNotFoundError :
107130 timezone = dt .timezone .utc
108131 timezone_name = "UTC"
109- local_now = now .astimezone (timezone )
110- if local_now .day in expected_days :
111- return None
132+ local_since_date = since .astimezone (timezone ).date ()
133+ local_now_date = now .astimezone (timezone ).date ()
134+ cursor = local_since_date
135+ while cursor <= local_now_date :
136+ if cursor .day in expected_days :
137+ return None
138+ cursor += dt .timedelta (days = 1 )
112139 day_text = "," .join (str (day ) for day in sorted (expected_days ))
113140 return (
114141 f"runtime scheduler main_time is not due today "
115- f"({ timezone_name } day={ local_now .day } ; expected day(s)={ day_text } )"
142+ f"({ timezone_name } date_window={ local_since_date .isoformat ()} .."
143+ f"{ local_now_date .isoformat ()} ; expected day(s)={ day_text } )"
116144 )
117145
118146
@@ -434,18 +462,24 @@ def main(now: dt.datetime | None = None) -> int:
434462 or os .environ .get ("GOOGLE_CLOUD_PROJECT" )
435463 )
436464 name = os .environ .get ("RUNTIME_HEARTBEAT_NAME" ) or os .environ .get ("GITHUB_REPOSITORY" ) or "runtime"
465+ if not _runtime_target_enabled ():
466+ print (f"Execution report heartbeat skipped for { name } : runtime target is disabled" )
467+ return 0
437468 lookback_hours = float (os .environ .get ("RUNTIME_HEARTBEAT_LOOKBACK_HOURS" ) or "36" )
438469 max_reports = int (os .environ .get ("RUNTIME_HEARTBEAT_MAX_REPORTS_TO_READ" ) or "20" )
439470 fail_workflow = _env_bool ("RUNTIME_HEARTBEAT_FAIL_WORKFLOW_ON_ALERT" , True )
440471 required_services = _load_required_services ()
441472
442473 now = now or dt .datetime .now (dt .timezone .utc )
443- schedule_skip_reason = _heartbeat_skip_reason_for_schedule (now )
474+ if now .tzinfo is None :
475+ now = now .replace (tzinfo = dt .timezone .utc )
476+ now = now .astimezone (dt .timezone .utc )
477+ since = now - dt .timedelta (hours = lookback_hours )
478+ schedule_skip_reason = _heartbeat_skip_reason_for_schedule (since , now )
444479 if schedule_skip_reason :
445480 print (f"Execution report heartbeat skipped for { name } : { schedule_skip_reason } " )
446481 return 0
447482
448- since = now - dt .timedelta (hours = lookback_hours )
449483 globs = _report_globs (since , now )
450484 if not globs :
451485 raise SystemExit ("No heartbeat GCS report URI configured" )
0 commit comments