Add a deploy_strategy field to each source's config blob — "" (default),
"recreate", or "blue-green" — validated in each source's Validate and read on
the deploy path. No new DB column, no migration: the field rides inside the
existing SourceConfig JSON and every existing workload decodes "" to its
historical behavior (image -> blue-green, others -> recreate).
The real gap this closes: dockerfile and static stopped the old container
before creating the new one on every redeploy — a downtime window image never
had. Their blue-green branch now:
- names the new "green" container with a unique suffix so it coexists with the
still-serving blue (plumbed into both the container name AND the proxy
forwardHost);
- skips the collision teardown that destroyed blue early;
- gates green — an HTTP readiness probe (deps.Health.Check) when a healthcheck
is configured, else the existing liveness window;
- swaps the route via a pure upsert (no pre-DeleteRoute) so NPM repoints in
place with no gap;
- persists green into the single runtime-state row BEFORE reaping blue, so a
crash mid-swap can never orphan green or leave the row pointing at a removed
container (state.go/teardown.go/reconcile.go stay untouched).
image honors explicit "recreate" (reap existing containers after pull, before
cutover); its default blue-green path is unchanged. compose stays
stack-managed and rejects "blue-green" at Validate so the contract is honest.
static forces recreate for storage-backed deno sites — blue-green would mount
the same RW volume into both containers at once.
Shared helper internal/workload/plugin/strategy.go (ValidateStrategy +
BuildGreenName). Backend-only (phase 1); the field is usable today via the
app's advanced-JSON editor — a friendly toggle + i18n follow in phase 2.
Tests: ValidateStrategy matrix, per-source Validate (incl. the empty-key
backward-compat lock), and effectiveStrategy defaults + the deno gate. Design
+ adversarial review: docs/plans/DEPLOY_STRATEGY_PLAN.md.
Report deploy status back to the Git provider as a commit status
(pending/success/failure) for git-sourced workloads (static + dockerfile).
- GitProvider.SetCommitStatus on gitea/github/gitlab over the existing
SSRF-safe client; fixed "tinyforge" context so redeploys update one row.
postJSON returns status-code-only errors (never echoes the upstream body,
which a hostile provider could use to reflect the auth token into the
best-effort log line).
- Best-effort deploy hook: pending on deploy start, success/failure on
outcome, gated on a per-workload report_commit_status flag. Never fails or
blocks a deploy; emits nothing on the unchanged-SHA short-circuit.
- UI ToggleSwitch (create + edit) + reportCommitStatus in sourceForms.ts
+ en/ru i18n.
- Tests: per-provider state mapping + request shape; reporter gating
(enabled/disabled/empty-SHA/nil/error-swallow).
Reviewed via go-reviewer + security-reviewer (0 CRITICAL/HIGH; one MEDIUM
body-echo log-leak fixed).