Files
summercms/modules/bonfire/call.go
Jakub Zych d9f939a1ea feat(11-02): run plugin schedules as River periodic jobs through bonfire.Call
- pact.HasSchedule with ScheduledCommand and Daily/DailyAt/Every cadences (no River import)
- bonfire.Call, Catalog and ErrUnknownCommand for in-process command runs
- conga Daily/Every wall-clock schedules in app.timezone, periodic jobs on every worker,
  scheduled queue (MaxAttempts 1, unique by args within the cadence period)
- scheduled worker runs only entries matching the compiled table; unregistered
  commands are skipped with a Warn log
- generated app main publishes bonfire.NewCatalog(commands); hello main regenerated
2026-09-29 19:47:51 +02:00

73 lines
1.9 KiB
Go

package bonfire
import (
"context"
"errors"
"fmt"
"io"
"strings"
)
// ErrUnknownCommand is returned by Call when no command has the given name.
var ErrUnknownCommand = errors.New("bonfire: unknown command")
// Call runs the command named name in-process with args, writing its output
// to out. Stdin is empty, so prompts take their defaults. A name that no
// command has is ErrUnknownCommand.
func Call(ctx context.Context, commands []Command, name string, args []string, out io.Writer) error {
cmd, ok := findCommand(commands, name)
if !ok {
return fmt.Errorf("%w: %q", ErrUnknownCommand, name)
}
if out == nil {
out = io.Discard
}
root, err := NewRootIO(name, []Command{cmd}, strings.NewReader(""), out, out)
if err != nil {
return err
}
root.SetArgs(append([]string{name}, args...))
if ctx == nil {
ctx = context.Background()
}
return root.ExecuteContext(ctx)
}
// Catalog is the final command list of an application binary. The generated
// main publishes it on the app so code outside the cobra root, such as the
// scheduler, can run any registered command with Call.
type Catalog struct {
commands []Command
}
// NewCatalog returns a Catalog holding a copy of commands.
func NewCatalog(commands []Command) *Catalog {
return &Catalog{commands: append([]Command(nil), commands...)}
}
// Has reports whether the catalog holds a command named name.
func (c *Catalog) Has(name string) bool {
if c == nil {
return false
}
_, ok := findCommand(c.commands, name)
return ok
}
// Call runs the named catalog command in-process; see the package Call.
func (c *Catalog) Call(ctx context.Context, name string, args []string, out io.Writer) error {
if c == nil {
return fmt.Errorf("%w: %q", ErrUnknownCommand, name)
}
return Call(ctx, c.commands, name, args, out)
}
func findCommand(commands []Command, name string) (Command, bool) {
for _, c := range commands {
if c.Name == name {
return c, true
}
}
return Command{}, false
}