Skip to content

Commit 7710c30

Browse files
authored
Drop legacy runtime routes (#175)
1 parent 38d881e commit 7710c30

7 files changed

Lines changed: 50 additions & 73 deletions

File tree

.github/workflows/invoke-cloud-run.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ on:
1515
path:
1616
description: "HTTP path to call"
1717
required: false
18-
default: "/precheck"
18+
default: "/dry-run"
1919
type: string
2020
allow_live_execution:
21-
description: "Allow calling / live execution entrypoint"
21+
description: "Allow calling /run live execution entrypoint"
2222
required: false
2323
default: false
2424
type: boolean
@@ -82,12 +82,12 @@ jobs:
8282
8383
raw_path="${{ inputs.path }}"
8484
if [ -z "${raw_path}" ]; then
85-
raw_path="/"
85+
raw_path="/dry-run"
8686
fi
8787
if [[ "${raw_path}" != /* ]]; then
8888
raw_path="/${raw_path}"
8989
fi
90-
if { [ "${raw_path}" = "/" ] || [ "${raw_path}" = "/run" ]; } && [ "${{ inputs.allow_live_execution }}" != "true" ]; then
90+
if [ "${raw_path}" = "/run" ] && [ "${{ inputs.allow_live_execution }}" != "true" ]; then
9191
echo "Calling ${raw_path} can trigger the live execution entrypoint. Re-run with allow_live_execution=true if this is intentional." >&2
9292
exit 1
9393
fi
@@ -144,21 +144,21 @@ jobs:
144144
if [ "${service_ingress}" = "internal" ]; then
145145
scheduler_location="${CLOUD_SCHEDULER_LOCATION:-${CLOUD_RUN_REGION}}"
146146
case "${raw_path}" in
147-
/|/run)
147+
/run)
148148
scheduler_job="${CLOUD_RUN_SERVICE}-scheduler"
149-
scheduler_expected_path="/"
149+
scheduler_expected_path="/run"
150150
;;
151151
/probe)
152152
scheduler_job="${CLOUD_RUN_SERVICE}-probe-scheduler"
153153
scheduler_expected_path="/probe"
154154
;;
155-
/precheck|/dry-run)
155+
/dry-run)
156156
scheduler_job="${CLOUD_RUN_SERVICE}-precheck-scheduler"
157-
scheduler_expected_path="/precheck"
157+
scheduler_expected_path="/dry-run"
158158
;;
159159
*)
160160
echo "Cloud Run service ${CLOUD_RUN_SERVICE} has internal ingress, so GitHub-hosted runners cannot curl ${raw_path} directly." >&2
161-
echo "Use one of the scheduler-backed paths: /, /run, /probe, /precheck, /dry-run." >&2
161+
echo "Use one of the scheduler-backed paths: /run, /probe, /dry-run." >&2
162162
exit 1
163163
;;
164164
esac

.github/workflows/sync-cloud-run-env.yml

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -991,17 +991,29 @@ jobs:
991991
probe_time="${scheduler_market_config[2]}"
992992
precheck_time="${scheduler_market_config[3]}"
993993
994+
service_url="$(gcloud run services describe "${CLOUD_RUN_SERVICE}" \
995+
--project="${GCP_PROJECT_ID}" \
996+
--region="${CLOUD_RUN_REGION}" \
997+
--format='value(status.url)' 2>/dev/null || true)"
998+
if [ -z "${service_url}" ]; then
999+
echo "Unable to resolve Cloud Run service URL for ${CLOUD_RUN_SERVICE}; cannot sync scheduler URI." >&2
1000+
exit 1
1001+
fi
1002+
9941003
for suffix in scheduler probe-scheduler precheck-scheduler; do
9951004
job_name="${CLOUD_RUN_SERVICE}-${suffix}"
9961005
case "${suffix}" in
9971006
scheduler)
9981007
schedule_time="${main_time}"
1008+
scheduler_path="/run"
9991009
;;
10001010
probe-scheduler)
10011011
schedule_time="${probe_time}"
1012+
scheduler_path="/probe"
10021013
;;
10031014
precheck-scheduler)
10041015
schedule_time="${precheck_time}"
1016+
scheduler_path="/dry-run"
10051017
;;
10061018
esac
10071019
@@ -1027,10 +1039,12 @@ jobs:
10271039
PY
10281040
)"
10291041
1030-
echo "Updating Cloud Scheduler job ${job_name} schedule to ${desired_schedule} and timezone to ${market_timezone}."
1042+
scheduler_uri="${service_url}${scheduler_path}"
1043+
echo "Updating Cloud Scheduler job ${job_name} schedule to ${desired_schedule}, timezone to ${market_timezone}, and URI to ${scheduler_uri}."
10311044
gcloud scheduler jobs update http "${job_name}" \
10321045
--project="${GCP_PROJECT_ID}" \
10331046
--location="${scheduler_location}" \
1047+
--uri="${scheduler_uri}" \
10341048
--schedule="${desired_schedule}" \
10351049
--time-zone="${market_timezone}" \
10361050
--quiet

main.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,8 @@ def run_strategy(*, force_run: bool = False, validation_only: bool = False, vali
518518
strategy_plugin_signals=strategy_plugin_signals,
519519
strategy_plugin_error=strategy_plugin_error,
520520
notification_title_key=(
521-
"precheck_title"
522-
if validation_only and validation_label == "precheck"
521+
"dry_run_title"
522+
if validation_only and validation_label == "dry_run"
523523
else ""
524524
),
525525
),
@@ -672,7 +672,6 @@ def run_probe(*, response_body: str = "Probe OK"):
672672
print(f"failed to persist execution report: {persist_exc}", flush=True)
673673

674674

675-
@app.route("/", methods=["POST", "GET"])
676675
@app.route("/run", methods=["POST", "GET"])
677676
def handle_trigger():
678677
"""Entrypoint for Cloud Run / scheduler: run strategy and return 200."""
@@ -695,27 +694,14 @@ def handle_backfill():
695694
)
696695

697696

698-
@app.route("/precheck", methods=["POST", "GET"])
699-
def handle_precheck():
700-
"""Pre-market / post-open verification entrypoint for dry-run only execution."""
701-
return _route_with_runtime_error_fallback(
702-
run_strategy,
703-
force_run=True,
704-
validation_only=True,
705-
validation_label="precheck",
706-
success_body="Precheck OK",
707-
route_label="POST /precheck",
708-
)
709-
710-
711697
@app.route("/dry-run", methods=["POST", "GET"])
712698
def handle_dry_run():
713-
"""Strategy dry-run entrypoint; alias of precheck with clearer operator wording."""
699+
"""Strategy dry-run entrypoint."""
714700
return _route_with_runtime_error_fallback(
715701
run_strategy,
716702
force_run=True,
717703
validation_only=True,
718-
validation_label="precheck",
704+
validation_label="dry_run",
719705
success_body="Dry Run OK",
720706
route_label="POST /dry-run",
721707
)

notifications/telegram.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
"income_locked": "🏦 收入层锁定占比: {ratio}",
4040
"signal": "🎯 触发信号: {msg}",
4141
"heartbeat_title": "💓 【心跳检测】",
42-
"precheck_title": "🧪 【策略预检】",
42+
"precheck_title": "🧪 【策略演练】",
43+
"dry_run_title": "🧪 【策略演练】",
4344
"health_probe_title": "🔎 【连接探针】",
4445
"health_probe_error_prefix": "健康探针异常:\n",
4546
"equity": "💰 净值: ${value}",
@@ -197,7 +198,8 @@
197198
"income_locked": "🏦 Income Locked: {ratio}",
198199
"signal": "🎯 Signal: {msg}",
199200
"heartbeat_title": "💓 【Heartbeat】",
200-
"precheck_title": "🧪 【Strategy Precheck】",
201+
"precheck_title": "🧪 【Strategy Dry Run】",
202+
"dry_run_title": "🧪 【Strategy Dry Run】",
201203
"health_probe_title": "🔎 【Health Probe】",
202204
"health_probe_error_prefix": "Health probe error:\n",
203205
"equity": "💰 Equity: ${value}",

tests/test_invoke_cloud_run_workflow.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ workflow_file="$repo_dir/.github/workflows/invoke-cloud-run.yml"
77
grep -Fq "name: Invoke Cloud Run" "$workflow_file"
88
grep -Fq "workflow_dispatch:" "$workflow_file"
99
grep -Fq "environment: \${{ inputs.environment }}" "$workflow_file"
10-
grep -Fq 'default: "/precheck"' "$workflow_file"
10+
grep -Fq 'default: "/dry-run"' "$workflow_file"
1111
grep -Fq "allow_live_execution:" "$workflow_file"
12-
grep -Fq "Calling / can trigger the live execution entrypoint." "$workflow_file"
12+
grep -Fq "Calling \${raw_path} can trigger the live execution entrypoint." "$workflow_file"
1313
grep -Fq "id-token: write" "$workflow_file"
1414
grep -Fq "google-github-actions/auth@v3" "$workflow_file"
1515
grep -Fq "google-github-actions/setup-gcloud@v3" "$workflow_file"
@@ -19,7 +19,7 @@ grep -Fq "CLOUD_SCHEDULER_LOCATION: \${{ vars.CLOUD_SCHEDULER_LOCATION }}" "$wor
1919
grep -Fq "longbridge-hk|longbridge-paper|longbridge-sg" "$workflow_file"
2020
grep -Fq "gcloud run services describe \"\${CLOUD_RUN_SERVICE}\"" "$workflow_file"
2121
grep -Fq "Cloud Run service \${CLOUD_RUN_SERVICE} has internal ingress" "$workflow_file"
22-
grep -Fq "Use one of the scheduler-backed paths: /, /probe, /precheck." "$workflow_file"
22+
grep -Fq "Use one of the scheduler-backed paths: /run, /probe, /dry-run." "$workflow_file"
2323
grep -Fq "scheduler_job=\"\${CLOUD_RUN_SERVICE}-precheck-scheduler\"" "$workflow_file"
2424
grep -Fq "Invoke internal service through Cloud Scheduler" "$workflow_file"
2525
grep -Fq "gcloud scheduler jobs run \"\${scheduler_job}\"" "$workflow_file"

tests/test_notifications.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def test_heartbeat_signal_snapshot_localizes_price_source(self):
177177
self.assertIn("📊 市场状态: 🚀 风险开启(SOXX+SOXL)", rendered.compact_text)
178178
self.assertNotIn("longbridge_candlesticks", rendered.compact_text)
179179

180-
def test_precheck_heartbeat_uses_precheck_title(self):
180+
def test_dry_run_heartbeat_uses_dry_run_title(self):
181181
rendered = render_heartbeat_notification(
182182
execution={
183183
"signal_display": "🚀 入场信号 | 原因:QQQ 高于 MA200",
@@ -188,7 +188,7 @@ def test_precheck_heartbeat_uses_precheck_title(self):
188188
separator="━━━━━━━━━━━━━━━━━━",
189189
strategy_display_name="TQQQ 增长收益",
190190
dry_run_only=True,
191-
title_key="precheck_title",
191+
title_key="dry_run_title",
192192
)
193193
en_rendered = render_heartbeat_notification(
194194
execution={
@@ -200,12 +200,12 @@ def test_precheck_heartbeat_uses_precheck_title(self):
200200
separator="━━━━━━━━━━━━━━━━━━",
201201
strategy_display_name="TQQQ Growth Income",
202202
dry_run_only=True,
203-
title_key="precheck_title",
203+
title_key="dry_run_title",
204204
)
205205

206-
self.assertIn("🧪 【策略预检】", rendered.compact_text)
206+
self.assertIn("🧪 【策略演练】", rendered.compact_text)
207207
self.assertNotIn("💓 【心跳检测】", rendered.compact_text)
208-
self.assertIn("🧪 【Strategy Precheck】", en_rendered.compact_text)
208+
self.assertIn("🧪 【Strategy Dry Run】", en_rendered.compact_text)
209209
self.assertNotIn("💓 【Heartbeat】", en_rendered.compact_text)
210210

211211
def test_heartbeat_renders_tqqq_volatility_delever_risk_control(self):

tests/test_request_handling.py

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,11 @@ class RequestHandlingTests(unittest.TestCase):
203203
def test_cloud_run_route_contracts_are_registered(self):
204204
module = load_module()
205205

206-
self.assertIs(module.app._routes[("/", ("POST", "GET"))], module.handle_trigger)
207206
self.assertIs(module.app._routes[("/run", ("POST", "GET"))], module.handle_trigger)
208207
self.assertIs(
209208
module.app._routes[("/backfill", ("POST", "GET"))],
210209
module.handle_backfill,
211210
)
212-
self.assertIs(
213-
module.app._routes[("/precheck", ("POST", "GET"))],
214-
module.handle_precheck,
215-
)
216211
self.assertIs(
217212
module.app._routes[("/dry-run", ("POST", "GET"))],
218213
module.handle_dry_run,
@@ -249,7 +244,7 @@ def fake_run_strategy():
249244
},
250245
clear=False,
251246
):
252-
with module.app.test_request_context("/", method="POST"):
247+
with module.app.test_request_context("/run", method="POST"):
253248
body, status = module.handle_trigger()
254249

255250
self.assertEqual(status, 200)
@@ -260,7 +255,7 @@ def test_handle_trigger_returns_500_when_strategy_reports_failure(self):
260255
module = load_module()
261256
module.run_strategy = lambda: False
262257

263-
with module.app.test_request_context("/", method="POST"):
258+
with module.app.test_request_context("/run", method="POST"):
264259
body, status = module.handle_trigger()
265260

266261
self.assertEqual(status, 500)
@@ -282,7 +277,7 @@ def fake_post(_url, *, json, timeout):
282277
module.requests.post = fake_post
283278
module.run_strategy = lambda: (_ for _ in ()).throw(RuntimeError("boom"))
284279

285-
with module.app.test_request_context("/", method="POST"):
280+
with module.app.test_request_context("/run", method="POST"):
286281
body, status = module.handle_trigger()
287282

288283
self.assertEqual(status, 500)
@@ -308,7 +303,7 @@ def fake_post(_url, *, json, timeout):
308303
module.requests.post = fake_post
309304
module.run_strategy = lambda: (_ for _ in ()).throw(RuntimeError("boom"))
310305

311-
with module.app.test_request_context("/", method="POST"):
306+
with module.app.test_request_context("/run", method="POST"):
312307
body, status = module.handle_trigger()
313308

314309
self.assertEqual(status, 500)
@@ -327,7 +322,7 @@ def fake_run_strategy():
327322

328323
module.run_strategy = fake_run_strategy
329324

330-
with module.app.test_request_context("/", method="GET"):
325+
with module.app.test_request_context("/run", method="GET"):
331326
body, status = module.handle_trigger()
332327

333328
self.assertEqual(status, 200)
@@ -351,27 +346,7 @@ def fake_run_strategy(*, force_run=False, validation_only=False, validation_labe
351346
self.assertEqual(body, "OK")
352347
self.assertTrue(observed["force_run"])
353348
self.assertTrue(observed["validation_only"])
354-
def test_handle_precheck_forces_strategy_run(self):
355-
module = load_module()
356-
observed = {"force_run": None, "validation_only": None}
357-
358-
def fake_run_strategy(*, force_run=False, validation_only=False, validation_label="backfill"):
359-
observed["force_run"] = force_run
360-
observed["validation_only"] = validation_only
361-
observed["validation_label"] = validation_label
362-
363-
module.run_strategy = fake_run_strategy
364-
365-
with module.app.test_request_context("/precheck", method="POST"):
366-
body, status = module.handle_precheck()
367-
368-
self.assertEqual(status, 200)
369-
self.assertEqual(body, "Precheck OK")
370-
self.assertTrue(observed["force_run"])
371-
self.assertTrue(observed["validation_only"])
372-
self.assertEqual(observed["validation_label"], "precheck")
373-
374-
def test_handle_dry_run_alias_forces_strategy_dry_run(self):
349+
def test_handle_dry_run_forces_strategy_dry_run(self):
375350
module = load_module()
376351
observed = {"force_run": None, "validation_only": None}
377352

@@ -389,7 +364,7 @@ def fake_run_strategy(*, force_run=False, validation_only=False, validation_labe
389364
self.assertEqual(body, "Dry Run OK")
390365
self.assertTrue(observed["force_run"])
391366
self.assertTrue(observed["validation_only"])
392-
self.assertEqual(observed["validation_label"], "precheck")
367+
self.assertEqual(observed["validation_label"], "dry_run")
393368

394369
def test_handle_probe_checks_account_snapshot_without_success_notification(self):
395370
module = load_module()
@@ -644,7 +619,7 @@ def build_rebalance_config(
644619
self.assertTrue(observed["silent_cycle_notifications"])
645620
self.assertEqual(observed["notification_title_key"], "")
646621

647-
def test_run_strategy_precheck_sets_precheck_notification_title(self):
622+
def test_run_strategy_dry_run_sets_dry_run_notification_title(self):
648623
module = load_module()
649624
observed = {"notification_title_key": None}
650625

@@ -685,9 +660,9 @@ def build_rebalance_config(
685660
module.is_market_open_now = lambda **_kwargs: False
686661
module.run_rebalance_cycle = lambda **_kwargs: None
687662

688-
module.run_strategy(force_run=True, validation_only=True, validation_label="precheck")
663+
module.run_strategy(force_run=True, validation_only=True, validation_label="dry_run")
689664

690-
self.assertEqual(observed["notification_title_key"], "precheck_title")
665+
self.assertEqual(observed["notification_title_key"], "dry_run_title")
691666

692667
def test_run_strategy_persists_machine_readable_report(self):
693668
module = load_module()

0 commit comments

Comments
 (0)