// Package manual implements the "manual" trigger: any ManualEvent fires a // deploy. No per-workload config — the trigger always matches its kind. package manual import ( "context" "encoding/json" "fmt" "time" "github.com/alexei/tinyforge/internal/workload/plugin" ) type trigger struct{} func init() { plugin.RegisterTrigger(&trigger{}) } func (*trigger) Kind() string { return "manual" } func (*trigger) SchemaSample() any { return struct{}{} } func (*trigger) Validate(cfg json.RawMessage) error { // Manual triggers have no config; accept empty or a small valid JSON // blob. The cap prevents an admin from pinning a 1 MiB blob to a // trigger row that gets serialized on every read. if len(cfg) == 0 { return nil } if len(cfg) > 1024 { return fmt.Errorf("manual trigger: config must be empty or a small JSON value (got %d bytes)", len(cfg)) } if !json.Valid(cfg) { return fmt.Errorf("manual trigger: invalid json") } return nil } func (*trigger) Match(ctx context.Context, deps plugin.Deps, w plugin.Workload, evt plugin.InboundEvent) (*plugin.DeploymentIntent, error) { if evt.Kind != "manual" || evt.Manual == nil { return nil, nil } actor := evt.Manual.Actor if actor == "" { actor = "manual" } meta := map[string]string{} if evt.Manual.Note != "" { meta["note"] = evt.Manual.Note } return &plugin.DeploymentIntent{ Reason: "manual", Reference: evt.Manual.Reference, Metadata: meta, TriggeredAt: time.Now().UTC(), TriggeredBy: actor, }, nil }