From 91645bee3bf48ac7a8e8d93b9bddf27df3ae7d21 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sat, 21 Feb 2026 00:55:12 -0800 Subject: [PATCH] fix: add 1MB size limit on deployment logs with truncation (closes #122) Cap AppendLog at 1MB, truncating oldest lines when exceeded. Prevents unbounded SQLite database growth from long-running builds. --- internal/models/deployment.go | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/internal/models/deployment.go b/internal/models/deployment.go index 492ba82..73a9feb 100644 --- a/internal/models/deployment.go +++ b/internal/models/deployment.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "fmt" + "strings" "time" "git.eeqj.de/sneak/upaas/internal/database" @@ -76,7 +77,11 @@ func (d *Deployment) Reload(ctx context.Context) error { return d.scan(row) } +// maxLogSize is the maximum size of deployment logs stored in the database (1MB). +const maxLogSize = 1 << 20 + // AppendLog appends a log line to the deployment logs. +// If the total log size exceeds maxLogSize, the oldest lines are truncated. func (d *Deployment) AppendLog(ctx context.Context, line string) error { var currentLogs string @@ -84,7 +89,22 @@ func (d *Deployment) AppendLog(ctx context.Context, line string) error { currentLogs = d.Logs.String } - d.Logs = sql.NullString{String: currentLogs + line + "\n", Valid: true} + newLogs := currentLogs + line + "\n" + + if len(newLogs) > maxLogSize { + // Keep the most recent logs that fit within the limit. + // Find a newline after the truncation point to avoid partial lines. + truncateAt := len(newLogs) - maxLogSize + idx := strings.Index(newLogs[truncateAt:], "\n") + + if idx >= 0 { + newLogs = "[earlier logs truncated]\n" + newLogs[truncateAt+idx+1:] + } else { + newLogs = "[earlier logs truncated]\n" + newLogs[truncateAt:] + } + } + + d.Logs = sql.NullString{String: newLogs, Valid: true} return d.Save(ctx) }