Skip to content

Commit 2eb4ab0

Browse files
committed
Revert "Use strategy Telegram alert target for workflow alerts"
This reverts commit c3b8789.
1 parent c3b8789 commit 2eb4ab0

6 files changed

Lines changed: 72 additions & 184 deletions

.github/workflows/execution-report-heartbeat.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,6 @@ jobs:
4545
FIRSTRADE_GCS_STATE_BUCKET: ${{ vars.FIRSTRADE_GCS_STATE_BUCKET }}
4646
FIRSTRADE_STATE_PREFIX: ${{ vars.FIRSTRADE_STATE_PREFIX }}
4747
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
48-
STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS: ${{ vars.STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS }}
49-
STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN: ${{ secrets.STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN }}
50-
STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME: ${{ vars.STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME }}
5148
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
5249
TELEGRAM_TOKEN_SECRET_NAME: ${{ vars.TELEGRAM_TOKEN_SECRET_NAME }}
5350
steps:

.github/workflows/runtime-guard.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,6 @@ jobs:
5353
RUNTIME_GUARD_SCHEDULER_JOB_PATTERN: ${{ vars.RUNTIME_GUARD_SCHEDULER_JOB_PATTERN }}
5454
CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}
5555
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
56-
STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS: ${{ vars.STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS }}
57-
STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN: ${{ secrets.STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN }}
58-
STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME: ${{ vars.STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME }}
5956
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
6057
TELEGRAM_TOKEN_SECRET_NAME: ${{ vars.TELEGRAM_TOKEN_SECRET_NAME }}
6158
steps:

scripts/cloud_run_runtime_guard.py

Lines changed: 20 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ def _summarize(entry: dict[str, Any]) -> str:
169169
suffix = f" {text}" if text else ""
170170
return f"- {timestamp} {target or '<unknown>'} severity={severity}{status_text}{suffix}"
171171

172+
173+
174+
172175
def _telegram_secret_project() -> str | None:
173176
return (
174177
os.environ.get("RUNTIME_HEARTBEAT_GCP_PROJECT_ID")
@@ -178,76 +181,42 @@ def _telegram_secret_project() -> str | None:
178181
)
179182

180183

181-
def _load_telegram_token_from_secret(secret_env_name: str) -> str:
182-
secret_name = (os.environ.get(secret_env_name) or "").strip()
184+
def _load_telegram_token_from_secret() -> str:
185+
secret_name = (os.environ.get("TELEGRAM_TOKEN_SECRET_NAME") or "").strip()
183186
if not secret_name:
184187
return ""
185-
command = [
186-
"gcloud",
187-
"secrets",
188-
"versions",
189-
"access",
190-
"latest",
191-
"--secret",
192-
secret_name,
193-
]
188+
command = ["gcloud", "secrets", "versions", "access", "latest", "--secret", secret_name]
194189
project = _telegram_secret_project()
195190
if project:
196191
command.extend(["--project", project])
197192
result = _run_gcloud(command)
198193
if result.returncode != 0:
199194
detail = (result.stderr or result.stdout or "").strip()
200195
print(
201-
f"Unable to read Telegram token from {secret_env_name}: "
202-
f"{detail or 'gcloud failed'}",
196+
f"Unable to read Telegram token from Secret Manager: {detail or 'gcloud failed'}",
203197
file=sys.stderr,
204198
)
205199
return ""
206200
return result.stdout.strip()
207201

208202

209-
def _first_env_value(*names: str) -> str:
210-
for name in names:
211-
value = (os.environ.get(name) or "").strip()
212-
if value:
213-
return value
214-
return ""
215-
203+
def _telegram_token() -> str:
204+
direct_token = (os.environ.get("TELEGRAM_TOKEN") or os.environ.get("TG_TOKEN") or "").strip()
205+
if direct_token:
206+
return direct_token
207+
return _load_telegram_token_from_secret()
216208

217-
def _telegram_targets() -> list[tuple[str, str]]:
218-
strategy_chat_ids = _split_values(
219-
os.environ.get("STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS")
220-
)
221-
if strategy_chat_ids:
222-
strategy_token = _first_env_value("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN")
223-
if not strategy_token:
224-
strategy_token = _load_telegram_token_from_secret(
225-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME"
226-
)
227-
if strategy_token:
228-
return list(
229-
dict.fromkeys((strategy_token, chat_id) for chat_id in strategy_chat_ids)
230-
)
231-
return []
232-
233-
token = _first_env_value("TELEGRAM_TOKEN", "TG_TOKEN")
234-
if not token:
235-
token = _load_telegram_token_from_secret("TELEGRAM_TOKEN_SECRET_NAME")
236-
targets = [
237-
(token, chat_id)
238-
for chat_id in _split_values(os.environ.get("GLOBAL_TELEGRAM_CHAT_ID"))
239-
if token
240-
]
241-
return list(dict.fromkeys(targets))
209+
def _send_telegram(message: str) -> bool:
210+
targets: list[tuple[str, str]] = []
242211

212+
token = _telegram_token()
213+
for chat_id in _split_values(os.environ.get("GLOBAL_TELEGRAM_CHAT_ID")):
214+
if token:
215+
targets.append((token, chat_id))
243216

244-
def _send_telegram(message: str) -> bool:
245-
unique_targets = _telegram_targets()
217+
unique_targets = list(dict.fromkeys(targets))
246218
if not unique_targets:
247-
print(
248-
"No Telegram token/chat configured; unable to send runtime guard alert.",
249-
file=sys.stderr,
250-
)
219+
print("No Telegram token/chat configured; unable to send runtime guard alert.", file=sys.stderr)
251220
return False
252221

253222
ok = True

scripts/execution_report_heartbeat.py

Lines changed: 21 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,9 @@ def _is_accepted_report(payload: dict[str, Any]) -> tuple[bool, str]:
288288
return True, "report exists"
289289
return False, f"unaccepted status={status or '-'} stage={stage or '-'}"
290290

291+
292+
293+
291294
def _telegram_secret_project() -> str | None:
292295
return (
293296
os.environ.get("RUNTIME_HEARTBEAT_GCP_PROJECT_ID")
@@ -297,80 +300,43 @@ def _telegram_secret_project() -> str | None:
297300
)
298301

299302

300-
def _load_telegram_token_from_secret(secret_env_name: str) -> str:
301-
secret_name = (os.environ.get(secret_env_name) or "").strip()
303+
def _load_telegram_token_from_secret() -> str:
304+
secret_name = (os.environ.get("TELEGRAM_TOKEN_SECRET_NAME") or "").strip()
302305
if not secret_name:
303306
return ""
304-
command = [
305-
"gcloud",
306-
"secrets",
307-
"versions",
308-
"access",
309-
"latest",
310-
"--secret",
311-
secret_name,
312-
]
307+
command = ["gcloud", "secrets", "versions", "access", "latest", "--secret", secret_name]
313308
project = _telegram_secret_project()
314309
if project:
315310
command.extend(["--project", project])
316311
result = _run_gcloud(command)
317312
if result.returncode != 0:
318313
detail = (result.stderr or result.stdout or "").strip()
319314
print(
320-
f"Unable to read Telegram token from {secret_env_name}: "
321-
f"{detail or 'gcloud failed'}",
315+
f"Unable to read Telegram token from Secret Manager: {detail or 'gcloud failed'}",
322316
file=sys.stderr,
323317
)
324318
return ""
325319
return result.stdout.strip()
326320

327321

328-
def _first_env_value(*names: str) -> str:
329-
for name in names:
330-
value = (os.environ.get(name) or "").strip()
331-
if value:
332-
return value
333-
return ""
334-
335-
336-
def _telegram_targets() -> list[tuple[str, str]]:
337-
strategy_chat_ids = _split_values(
338-
os.environ.get("STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS")
339-
)
340-
if strategy_chat_ids:
341-
strategy_token = _first_env_value("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN")
342-
if not strategy_token:
343-
strategy_token = _load_telegram_token_from_secret(
344-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME"
345-
)
346-
if strategy_token:
347-
return list(
348-
dict.fromkeys((strategy_token, chat_id) for chat_id in strategy_chat_ids)
349-
)
350-
return []
351-
352-
token = _first_env_value("TELEGRAM_TOKEN", "TG_TOKEN")
353-
if not token:
354-
token = _load_telegram_token_from_secret("TELEGRAM_TOKEN_SECRET_NAME")
355-
targets = [
356-
(token, chat_id)
357-
for chat_id in _split_values(os.environ.get("GLOBAL_TELEGRAM_CHAT_ID"))
358-
if token
359-
]
360-
return list(dict.fromkeys(targets))
361-
322+
def _telegram_token() -> str:
323+
direct_token = (os.environ.get("TELEGRAM_TOKEN") or os.environ.get("TG_TOKEN") or "").strip()
324+
if direct_token:
325+
return direct_token
326+
return _load_telegram_token_from_secret()
362327

363328
def _send_telegram(message: str) -> bool:
364-
unique_targets = _telegram_targets()
329+
targets: list[tuple[str, str]] = []
330+
token = _telegram_token()
331+
for chat_id in _split_values(os.environ.get("GLOBAL_TELEGRAM_CHAT_ID")):
332+
if token:
333+
targets.append((token, chat_id))
334+
unique_targets = list(dict.fromkeys(targets))
365335
if not unique_targets:
366-
print(
367-
"No Telegram token/chat configured; unable to send heartbeat alert.",
368-
file=sys.stderr,
369-
)
336+
print("No Telegram token/chat configured; unable to send heartbeat alert.", file=sys.stderr)
370337
return False
371-
372-
ok = True
373338
base_url = "https://api.telegram.org"
339+
ok = True
374340
for token_value, chat_id in unique_targets:
375341
body = urllib.parse.urlencode({"chat_id": chat_id, "text": message}).encode()
376342
request = urllib.request.Request(
@@ -380,9 +346,7 @@ def _send_telegram(message: str) -> bool:
380346
)
381347
try:
382348
with urllib.request.urlopen(request, timeout=15) as response:
383-
if response.status >= 400:
384-
ok = False
385-
print(f"Telegram returned HTTP {response.status}", file=sys.stderr)
349+
ok = ok and response.status < 400
386350
except Exception as exc: # noqa: BLE001
387351
ok = False
388352
print(f"Telegram send failed: {type(exc).__name__}", file=sys.stderr)

tests/test_cloud_run_runtime_guard.py

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,28 @@
55
from scripts import cloud_run_runtime_guard as guard
66

77

8-
def test_telegram_targets_prefer_strategy_plugin_alert_secret(monkeypatch):
8+
def test_telegram_token_falls_back_to_secret_manager(monkeypatch):
99
monkeypatch.delenv("TELEGRAM_TOKEN", raising=False)
1010
monkeypatch.delenv("TG_TOKEN", raising=False)
11-
monkeypatch.delenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN", raising=False)
12-
monkeypatch.setenv(
13-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS",
14-
"strategy-chat; backup-chat",
15-
)
16-
monkeypatch.setenv(
17-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME",
18-
"strategy-plugin-telegram-token",
19-
)
20-
monkeypatch.setenv("GLOBAL_TELEGRAM_CHAT_ID", "platform-chat")
2111
monkeypatch.setenv("TELEGRAM_TOKEN_SECRET_NAME", "platform-telegram-token")
2212
monkeypatch.setenv("GCP_PROJECT_ID", "firstradequant")
23-
observed = []
13+
observed = {}
2414

2515
def fake_run_gcloud(command):
26-
observed.append(command)
27-
return subprocess.CompletedProcess(
28-
command,
29-
0,
30-
stdout="strategy-token\n",
31-
stderr="",
32-
)
16+
observed["command"] = command
17+
return subprocess.CompletedProcess(command, 0, stdout="secret-token\n", stderr="")
3318

3419
monkeypatch.setattr(guard, "_run_gcloud", fake_run_gcloud)
3520

36-
assert guard._telegram_targets() == [
37-
("strategy-token", "strategy-chat"),
38-
("strategy-token", "backup-chat"),
39-
]
40-
assert observed == [
41-
[
42-
"gcloud",
43-
"secrets",
44-
"versions",
45-
"access",
46-
"latest",
47-
"--secret",
48-
"strategy-plugin-telegram-token",
49-
"--project",
50-
"firstradequant",
51-
]
21+
assert guard._telegram_token() == "secret-token"
22+
assert observed["command"] == [
23+
"gcloud",
24+
"secrets",
25+
"versions",
26+
"access",
27+
"latest",
28+
"--secret",
29+
"platform-telegram-token",
30+
"--project",
31+
"firstradequant",
5232
]

tests/test_execution_report_heartbeat.py

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,48 +26,29 @@ def test_report_globs_include_sanitized_month_segments(monkeypatch):
2626
"gs://runtime-state/firstrade-platform/strategy-runs/**/2026_06/*.json",
2727
]
2828

29-
def test_telegram_targets_prefer_strategy_plugin_alert_secret(monkeypatch):
29+
def test_telegram_token_falls_back_to_secret_manager(monkeypatch):
3030
monkeypatch.delenv("TELEGRAM_TOKEN", raising=False)
3131
monkeypatch.delenv("TG_TOKEN", raising=False)
32-
monkeypatch.delenv("STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN", raising=False)
33-
monkeypatch.setenv(
34-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_CHAT_IDS",
35-
"strategy-chat; backup-chat",
36-
)
37-
monkeypatch.setenv(
38-
"STRATEGY_PLUGIN_ALERT_TELEGRAM_BOT_TOKEN_SECRET_NAME",
39-
"strategy-plugin-telegram-token",
40-
)
41-
monkeypatch.setenv("GLOBAL_TELEGRAM_CHAT_ID", "platform-chat")
4232
monkeypatch.setenv("TELEGRAM_TOKEN_SECRET_NAME", "platform-telegram-token")
4333
monkeypatch.setenv("GCP_PROJECT_ID", "firstradequant")
44-
observed = []
34+
observed = {}
4535

4636
def fake_run_gcloud(command):
47-
observed.append(command)
48-
return subprocess.CompletedProcess(
49-
command,
50-
0,
51-
stdout="strategy-token\n",
52-
stderr="",
53-
)
37+
observed["command"] = command
38+
return subprocess.CompletedProcess(command, 0, stdout="secret-token\n", stderr="")
5439

5540
monkeypatch.setattr(heartbeat, "_run_gcloud", fake_run_gcloud)
5641

57-
assert heartbeat._telegram_targets() == [
58-
("strategy-token", "strategy-chat"),
59-
("strategy-token", "backup-chat"),
60-
]
61-
assert observed == [
62-
[
63-
"gcloud",
64-
"secrets",
65-
"versions",
66-
"access",
67-
"latest",
68-
"--secret",
69-
"strategy-plugin-telegram-token",
70-
"--project",
71-
"firstradequant",
72-
]
42+
assert heartbeat._telegram_token() == "secret-token"
43+
assert observed["command"] == [
44+
"gcloud",
45+
"secrets",
46+
"versions",
47+
"access",
48+
"latest",
49+
"--secret",
50+
"platform-telegram-token",
51+
"--project",
52+
"firstradequant",
7353
]
54+

0 commit comments

Comments
 (0)