feat(11-02): add schedule:run as a scheduler process and a cron --once mode

- schedule:run runs a worker on the scheduled queue with every plugin's periodic jobs
- schedule:run --once runs entries due in the current app.timezone minute without River,
  warns and skips unregistered commands, and returns the first command error
- summer schedule:run delegate forwards --once
- tests for --once minute matching, forged-entry skipping (T-11-09) and ByPeriod dedupe
This commit is contained in:
Jakub Zych
2026-09-29 22:34:21 +02:00
parent d9f939a1ea
commit 2237a640d2
7 changed files with 521 additions and 18 deletions

View File

@@ -145,16 +145,8 @@ func (m *Manager) runScheduled(ctx context.Context, a ScheduledCommandArgs) erro
// at Warn and skipped (skipped true, nil error); a command error is logged
// with the duration and returned.
func callScheduled(ctx context.Context, app *backpack.App, log *slog.Logger, command string, args []string, out io.Writer) (bool, error) {
var cat *bonfire.Catalog
if app != nil {
cat, _ = app.Lookup[*bonfire.Catalog]()
}
if cat == nil {
log.Warn("schedule: no command catalog published; skipping", "command", command)
return true, nil
}
if !cat.Has(command) {
log.Warn("schedule: command not registered; skipping", "command", command)
cat, ok := scheduledCatalog(app, log, command)
if !ok {
return true, nil
}
start := time.Now()
@@ -168,6 +160,24 @@ func callScheduled(ctx context.Context, app *backpack.App, log *slog.Logger, com
return false, nil
}
// scheduledCatalog returns the app's catalog when it holds command. A missing
// catalog or command is logged at Warn and reported as false.
func scheduledCatalog(app *backpack.App, log *slog.Logger, command string) (*bonfire.Catalog, bool) {
var cat *bonfire.Catalog
if app != nil {
cat, _ = app.Lookup[*bonfire.Catalog]()
}
if cat == nil {
log.Warn("schedule: no command catalog published; skipping", "command", command)
return nil, false
}
if !cat.Has(command) {
log.Warn("schedule: command not registered; skipping", "command", command)
return nil, false
}
return cat, true
}
// logWriter logs each output line of a scheduled command at Info.
type logWriter struct {
log *slog.Logger
@@ -214,3 +224,6 @@ func (w *logWriter) emit(line string) {
}
w.log.Info(line, "command", w.command)
}
// scheduleNow is the clock of schedule:run --once (tests replace it).
var scheduleNow = time.Now