feat(triggers): add schedule trigger kind + internal scheduler
Build / build (push) Successful in 10m42s
Build / build (push) Successful in 10m42s
Fourth trigger kind alongside registry/git/manual. Recurring time-interval fires driven by a new internal/scheduler tick loop (default 30s, clamped to 5m). Goes through the same webhook.Handler.FanOutForTrigger seam as inbound HTTP webhooks, so per-binding concurrency, outcome accounting, and config-merge semantics are identical. Schema: triggers.last_fired_at TEXT column (additive ALTER for existing DBs). Scheduler persists last_fired_at BEFORE dispatch so a panicking Match cannot wedge a tight loop; failed deploys wait one full interval before retry — correct trade-off for a periodic refresh trigger. Frontend: TriggerKindForm + /triggers/new + /triggers/[id] gain the schedule kind (4-col card grid, preset chips Hourly/Daily/Weekly, custom interval input matched to Go time.ParseDuration syntax, optional pinned reference). /triggers/[id] surfaces "last fired" on schedule rows. EN+RU i18n in parity. Review fixes from go-reviewer / security-reviewer / typescript-reviewer: - Scheduler Start/Stop wrapped in sync.Once (no goroutine leak / double- cancel panic on shutdown re-entry). - shouldFire rejects sub-MinInterval as defense-in-depth against hand-inserted rows that bypassed Validate. - fire() asserts trigger Kind=="schedule" before dispatching. - Aligned isValidInterval regex across all three frontend sites; reject the unsupported "d" unit (Go time.ParseDuration doesn't accept it). - formatLastFired falls back to lastFiredNever on malformed timestamps rather than leaking raw bytes into the UI. - main.go scheduler closure logs per-fire deployed/errored counts.
This commit is contained in:
@@ -25,10 +25,21 @@ order.
|
||||
> already had ≥87% coverage from the trigger-split work.
|
||||
>
|
||||
> **What's next** is open — the remaining items in the doc are nice-to-
|
||||
> haves (richer kind-aware UI forms for new trigger kinds; a /triggers
|
||||
> deep-link from the proxies page; more compose-source coverage that
|
||||
> needs a `compose` exec seam). Pick from the task list or close the
|
||||
> arc.
|
||||
> haves (a /triggers deep-link from the proxies page; more compose-source
|
||||
> coverage that needs a `compose` exec seam). Pick from the task list or
|
||||
> close the arc.
|
||||
>
|
||||
> **Trigger kind expansion (2026-05-16):** added the fourth trigger
|
||||
> kind, **schedule** — interval-based recurring trigger driven by the
|
||||
> new `internal/scheduler` tick loop (default 30s, ≤5m). v1 takes a
|
||||
> Go-duration interval ("24h", "1h", "168h") with a 1-minute floor;
|
||||
> dispatches through the same `webhook.Handler.FanOutForTrigger` seam
|
||||
> the inbound HTTP webhook uses, so per-binding concurrency / outcome
|
||||
> accounting / config-merge semantics are identical. `triggers` gained
|
||||
> a `last_fired_at` column; the scheduler persists it BEFORE dispatch
|
||||
> so a panicking Match cannot wedge a tight loop. The frontend
|
||||
> picker grid grew to four columns and `/triggers/[id]` surfaces
|
||||
> "last fired" on schedule rows.
|
||||
|
||||
## Status at a glance
|
||||
|
||||
|
||||
Reference in New Issue
Block a user