feat: image version identity + DB-config parity for nightly scripts
Version: CI now tags images YYYY.MM.DD-<shortsha> alongside :latest/:sha and passes GIT_SHA/BUILD_TAG build-args; the running app reports them via dashboard /api/health and admin /api/status (TrueNAS custom apps only show 'custom/1.0.0', so the app self-reports what it actually runs). Config parity: every scheduler-run script (nightly_tune, eod_replay, premarket_eval, regime, market_context, experiments cli) now loads config_overrides at startup like the live services do — a bare env would replay under DEFAULT exits (the documented 4.5x-baseline landmine). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
537e0c7e12
commit
82273d9d4e
11 files changed
+57
-2
No files matched your search
@@ -64,12 +64,19 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- id: ver
|
||||
run: echo "tag=$(date -u +%Y.%m.%d)-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
|
||||
- uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
build-args: |
|
||||
GIT_SHA=${{ github.sha }}
|
||||
BUILD_TAG=${{ steps.ver.outputs.tag }}
|
||||
tags: |
|
||||
ghcr.io/fez5587/momentum-app:latest
|
||||
ghcr.io/fez5587/momentum-app:${{ github.sha }}
|
||||
ghcr.io/fez5587/momentum-app:${{ steps.ver.outputs.tag }}
|
||||
fez5587/momentum-app:latest
|
||||
fez5587/momentum-app:${{ github.sha }}
|
||||
fez5587/momentum-app:${{ steps.ver.outputs.tag }}
|
||||
@@ -14,6 +14,13 @@ RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends libpq5 ffmpeg curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Image identity: CI passes the git sha + a date tag so the RUNNING app can
|
||||
# report exactly what it is (TrueNAS custom apps show only 'custom/1.0.0').
|
||||
ARG GIT_SHA=dev
|
||||
ARG BUILD_TAG=dev
|
||||
ENV APP_GIT_SHA=$GIT_SHA \
|
||||
APP_BUILD_TAG=$BUILD_TAG
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONPATH=/app \
|
||||
|
||||
+3
-1
@@ -321,7 +321,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
return self._send(401, {"error": "bad or missing X-Admin-Token"})
|
||||
if path == "/api/status":
|
||||
return self._send(200, {"containers": list_containers(), "checks": checks(),
|
||||
"overrides": _safe_overrides()})
|
||||
"overrides": _safe_overrides(),
|
||||
"version": os.environ.get("APP_BUILD_TAG", "dev"),
|
||||
"git_sha": os.environ.get("APP_GIT_SHA", "dev")[:12]})
|
||||
if path == "/api/config":
|
||||
eff = {k: v for k, v in sorted(os.environ.items()) if _editable(k)}
|
||||
secrets = {k: bool(os.environ.get(k)) for k in
|
||||
|
||||
+3
-1
@@ -878,7 +878,9 @@ class DashboardHandler(BaseHTTPRequestHandler):
|
||||
days = int((self._query().get("days") or ["7"])[0])
|
||||
self._send_json(compute_attribution(days=days))
|
||||
elif route == "/api/health":
|
||||
self._send_json({"ok": True, "mode": self.state.execution_mode})
|
||||
self._send_json({"ok": True, "mode": self.state.execution_mode,
|
||||
"version": os.environ.get("APP_BUILD_TAG", "dev"),
|
||||
"git_sha": os.environ.get("APP_GIT_SHA", "dev")[:12]})
|
||||
else:
|
||||
self._send_json({"error": "not found"}, 404)
|
||||
except Exception as exc: # noqa: BLE001
|
||||
|
||||
@@ -23,6 +23,12 @@ from dotenv import load_dotenv
|
||||
|
||||
sys.path.insert(0, ".")
|
||||
load_dotenv()
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
os.environ.setdefault("STRATEGY_SETUPS", "opening_range_break")
|
||||
|
||||
from alpaca_paper.client import AlpacaPaperClient
|
||||
|
||||
@@ -26,6 +26,12 @@ from research.multi_schema import open_research_db
|
||||
|
||||
load_dotenv()
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
|
||||
|
||||
def _write_heartbeat(outcome: str) -> None:
|
||||
"""Record that the tuner RAN (regardless of whether it found a profitable
|
||||
|
||||
@@ -11,6 +11,12 @@ from __future__ import annotations
|
||||
import os
|
||||
import sys
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
|
||||
|
||||
def _store():
|
||||
from storage.event_store import EventStore
|
||||
|
||||
@@ -26,6 +26,12 @@ from research.ingestion.market_data import ( # noqa: E402
|
||||
upsert_daily_bars, upsert_minute_bars)
|
||||
from research.multi_schema import open_research_db # noqa: E402
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
|
||||
INDEX_SYMBOLS = ("SPY", "QQQ")
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,12 @@ from dotenv import load_dotenv
|
||||
from research import labeler
|
||||
from research.multi_schema import open_research_db
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
|
||||
load_dotenv(os.path.join(os.path.dirname(__file__), "..", ".env"))
|
||||
|
||||
HIST = os.path.join(os.path.dirname(__file__), "..", "data", "premarket_eval_history.tsv")
|
||||
|
||||
@@ -37,6 +37,12 @@ from datetime import date, datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from statistics import mean, median, pstdev
|
||||
|
||||
# config parity with the live loop: DB-backed knobs (config_overrides) beat
|
||||
# compose env — a bare env would replay under DEFAULT exits (the 4.5x-baseline
|
||||
# landmine, orderflow-shelf-verdicts 2026-07-17)
|
||||
from runtime.env_overrides import load as _load_cfg_overrides # noqa: E402
|
||||
_load_cfg_overrides()
|
||||
|
||||
_SCHEMA_SQL = Path(__file__).resolve().parent.parent / "storage" / "regime_schema.sql"
|
||||
|
||||
# Pre-open / prior-close-lagged features only. See module docstring + plan 011
|
||||
|
||||
@@ -29,6 +29,7 @@ def test_read_missing_table_is_empty(con):
|
||||
|
||||
|
||||
def test_load_db_applies_onto_environ(con, monkeypatch):
|
||||
monkeypatch.setenv("ENV_OVERRIDES_DB", "1") # force on (harness may disable)
|
||||
env_overrides.write_db_overrides({"TRADING_TEST_KNOB_XYZ": "42"}, con=con)
|
||||
monkeypatch.setattr(env_overrides, "_db_con", lambda: con)
|
||||
monkeypatch.setenv("TRADING_TEST_KNOB_XYZ", "1") # compose value loses
|
||||
|
||||
Reference in new issue
Block a user