Compare commits

...

3 Commits

Author SHA1 Message Date
Henrik Jess Nielsen
50d9d1d5c2 Merge remote-tracking branch 'refs/remotes/origin/main'
Some checks failed
Build and Deploy MoneyMaker / build-and-deploy (push) Failing after 17m32s
2026-06-29 22:41:19 +02:00
Henrik Jess Nielsen
09bf46c28d Trying out new layout 2026-06-29 22:33:21 +02:00
Henrik Jess Nielsen
8ac5aed3e0 perf: cache pip downloads and Docker layers in CI build
- Add BuildKit pip cache mount to Dockerfile (persists torch/transformers
  downloads across builds on the runner host)
- Enable DOCKER_BUILDKIT=1 in workflow env
- Pull previous image before build for layer cache reuse
- Add --cache-from latest + BUILDKIT_INLINE_CACHE=1 to docker build

BUILDKIT_INLINE_CACHE embeds cache metadata in the pushed image so
--cache-from works from the registry (cold runner or new runner).
SHA tag push reuses already-pushed layers - only metadata transferred.

Expected improvement:
- Unchanged code:       ~5 min -> ~30 sec (layer cache hit)
- requirements changed: ~5 min -> ~1 min (pip disk cache)
- Push:                 only changed layers uploaded; SHA tag is free
2026-05-27 22:21:15 +02:00
6 changed files with 49 additions and 14 deletions

View File

@@ -14,6 +14,7 @@ jobs:
PATH: /usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin:/snap/bin
DOCKER_HOST: unix:///var/run/docker.sock
IMAGE: registry.i80.dk/gitea/mmd
DOCKER_BUILDKIT: "1"
steps:
- name: Checkout code
@@ -29,9 +30,14 @@ jobs:
run: |
echo "${{ secrets.HARBOR_ROBOT_TOKEN }}" | docker login registry.i80.dk -u "robot\$gitserver" --password-stdin
- name: Pull previous image for layer cache
run: docker pull $IMAGE:latest || true
- name: Build Docker image
run: |
docker build \
--cache-from $IMAGE:latest \
--build-arg BUILDKIT_INLINE_CACHE=1 \
--build-arg BUILD_VERSION="${{ github.ref_name }}-${{ github.sha }}" \
--build-arg GIT_COMMIT="${{ github.sha }}" \
--build-arg BUILD_TIME="${{ github.event.head_commit.timestamp }}" \

View File

@@ -1,3 +1,4 @@
# syntax=docker/dockerfile:1
FROM python:3.12-slim
ARG BUILD_VERSION=unknown
@@ -15,10 +16,12 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
WORKDIR /app
RUN pip install --upgrade --no-cache-dir pip
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --upgrade pip
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
COPY . .

View File

@@ -1,13 +1,13 @@
nohup: ignorerer inddata
Traceback (most recent call last):
File "/home/hjess/Projects/MoneyMaker/dashboard.py", line 779, in <module>
main()
~~~~^^
File "/home/hjess/Projects/MoneyMaker/dashboard.py", line 773, in main
init_schema()
~~~~~~~~~~~^^
File "/home/hjess/Projects/MoneyMaker/db.py", line 375, in init_schema
conn = psycopg2.connect(DATABASE_URL)
File "/home/hjess/Projects/MoneyMaker/.venv/lib64/python3.14/site-packages/psycopg2/__init__.py", line 122, in connect
conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
psycopg2.OperationalError: could not translate host name "int.i80.dk" to address: Temporary failure in name resolution
MoneyMaker Dashboard -> http://localhost:5001
* Serving Flask app 'dashboard'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5001
* Running on http://192.168.15.40:5001
Press CTRL+C to quit
127.0.0.1 - - [26/May/2026 22:52:33] "GET /health HTTP/1.1" 200 -
127.0.0.1 - - [26/May/2026 22:52:39] "GET / HTTP/1.1" 200 -

View File

@@ -85,6 +85,7 @@ ANTHROPIC_API_KEY="{{ key "mmd/anthropic_api_key" }}"
SAXO_APP_KEY="{{ key "mmd/SAXO_APP_KEY" }}"
SAXO_APP_SECRET_1="{{ key "mmd/SAXO_APP_SECRET_1" }}"
SAXO_BASE="{{ key "mmd/SAXO_BASE" }}"
SAXO_TOKEN="{{ key "mmd/SAXO_APP_24_HOUR" }}"
HARBOR_ROBOT_TOKEN="{{ key "harbor/robot/token" }}"
EOH
destination = "secrets/app.env"
@@ -132,6 +133,7 @@ ANTHROPIC_API_KEY="{{ key "mmd/anthropic_api_key" }}"
SAXO_APP_KEY="{{ key "mmd/SAXO_APP_KEY" }}"
SAXO_APP_SECRET_1="{{ key "mmd/SAXO_APP_SECRET_1" }}"
SAXO_BASE="{{ key "mmd/SAXO_BASE" }}"
SAXO_TOKEN="{{ key "mmd/SAXO_APP_24_HOUR" }}"
EOH
destination = "secrets/worker.env"
env = true

View File

@@ -32,6 +32,23 @@ SCHEDULE = [
(17, 0, frozenset(range(5)), False, True),
]
# Saxo's access token lasts ~20 min and its refresh token rolls on each refresh
# but lapses if unused for ~1h. Trade windows are 2-3h apart, so we roll the
# token forward here independently to keep the refresh chain alive between runs.
SAXO_KEEPALIVE_SEC = 300
def _saxo_keepalive() -> None:
"""Roll the Saxo OAuth token forward so its refresh token never lapses in the
gaps between scheduled runs. No-op when there's no stored OAuth token yet
(e.g. only the 24h env token is present)."""
try:
from saxo_auth import _load, get_token
if _load().get("refresh_token"):
get_token() # auto-refreshes when near expiry; rolls the refresh token
except Exception as exc:
print(f"[scheduler] saxo keep-alive: {exc}", flush=True)
def _log_path() -> Path:
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
@@ -60,10 +77,17 @@ def _run_job(analyze_only: bool, is_report: bool) -> None:
def main() -> None:
last_run: dict = {}
last_keepalive = 0.0
print(f"[scheduler] started — LOG_DIR={LOG_DIR}", flush=True)
while True:
now = datetime.now(timezone.utc)
# Keep the Saxo refresh-token chain alive between trade windows.
if time.time() - last_keepalive >= SAXO_KEEPALIVE_SEC:
last_keepalive = time.time()
_saxo_keepalive()
dow = now.weekday()
for hour, minute, days, analyze_only, is_report in SCHEDULE: