package static import ( "fmt" "strings" "github.com/alexei/tinyforge/internal/workload/plugin" ) // containerNameFor is the deterministic container name. Includes // w.Name for visual continuity in `docker ps` plus the ID short for // uniqueness. func containerNameFor(w plugin.Workload) string { return fmt.Sprintf("dw-site-%s-%s", w.Name, plugin.IDShort(w)) } // imageTagFor is the deterministic image tag — same shape as the // container name so the linkage between an image and the workload // that owns it stays obvious from `docker images`. func imageTagFor(w plugin.Workload) string { return fmt.Sprintf("dw-site-%s-%s:latest", w.Name, plugin.IDShort(w)) } // siteVolumeKey is the input to docker.SiteVolumeName / EnsureSiteVolume // / RemoveSiteVolume. Composing it here (instead of building the full // name ourselves) keeps the naming concern in one place — those docker // helpers wrap the value with their own `tinyforge-site-...-data` // envelope. Including idShort prevents two workloads sharing a name // from sharing one persistent volume. func siteVolumeKey(w plugin.Workload) string { return fmt.Sprintf("%s-%s", w.Name, plugin.IDShort(w)) } // sanitizeError clamps an error string so persisting it (in // containers.extra_json's last_error) or echoing it (via the // outbound notification webhook) cannot leak a multi-line response // body, an HTTP header echoing the access token, or a stack trace. // // Strategy: // - Reduce to a single line (replace any newline / tab with space). // - Cap to a short maxLen so a very long Gitea/GitHub error body // never round-trips into operator-visible state. // - Redact the access token verbatim if it appears in the message // (defense in depth — providers shouldn't echo tokens but a // misbehaving one could). func sanitizeError(msg, accessToken string) string { if msg == "" { return "" } if accessToken != "" { msg = strings.ReplaceAll(msg, accessToken, "[REDACTED]") } // Collapse whitespace runs onto one line. msg = strings.Map(func(r rune) rune { switch r { case '\n', '\r', '\t': return ' ' } return r }, msg) const maxLen = 240 if len(msg) > maxLen { msg = msg[:maxLen] + "…" } return msg }