Files
summercms/cmd/summer/runtime.go
Jakub Zych b319e7cc61 feat(11-01): run job workers in serve and queue:work, add queue:clear
- Manager gains the apparatus JobManager surface: StartJob, UpdateJobState,
  UpdateMetadata, FailJob, CancelJob (is_canceled + STOPPED + River JobCancel),
  StopJob (STOPPED only), CheckIfCanceled and GetMetadata, all raw column
  writes so updated_at is untouched
- serve starts the in-process worker unless queue.work_in_serve is false and
  stops it on shutdown; an app without jobs gets an idle worker
- queue:work runs a foreground worker with repeatable --queue filters;
  queue:clear deletes available, scheduled and retryable jobs of one queue
- the generated main appends conga.RuntimeCommands; summer delegates
  queue:work and queue:clear; make:job scaffolds a conga.Job
2026-09-29 15:20:14 +02:00

117 lines
2.9 KiB
Go

package main
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"git.golem15.com/golem15/summercms/modules/bonfire"
"git.golem15.com/golem15/summercms/internal/build"
)
func delegateCommand(name, description string) bonfire.Command {
return bonfire.Command{
Name: name,
Description: description,
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
argv := []string{name}
argv = append(argv, in.Args()...)
return runAppBinary(ctx, argv, out)
},
}
}
func delegateRollbackCommand() bonfire.Command {
return bonfire.Command{
Name: "migrate:rollback",
Description: "Roll back the last migration of a plugin in the app binary",
Flags: []bonfire.Flag{{
Name: "plugin",
Description: "Plugin ID whose last migration to roll back",
}},
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
argv := []string{"migrate:rollback"}
if plugin, ok := in.Flag("plugin"); ok && plugin != "" {
argv = append(argv, "--plugin", plugin)
}
argv = append(argv, in.Args()...)
return runAppBinary(ctx, argv, out)
},
}
}
func delegateQueueWorkCommand() bonfire.Command {
return bonfire.Command{
Name: "queue:work",
Description: "Run background job workers in the app binary",
Flags: []bonfire.Flag{{
Name: "queue",
Description: "Queue to work (repeatable; default all known queues)",
Repeatable: true,
}},
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
argv := []string{"queue:work"}
for _, q := range in.Flags("queue") {
argv = append(argv, "--queue", q)
}
argv = append(argv, in.Args()...)
return runAppBinary(ctx, argv, out)
},
}
}
func runAppBinary(ctx context.Context, argv []string, out bonfire.Output) error {
if len(argv) == 0 {
return fmt.Errorf("summer: missing app command")
}
dir, err := os.Getwd()
if err != nil {
return err
}
appDir, err := build.FindAppDir(dir)
if err != nil {
return err
}
m, err := build.LoadManifest(filepath.Join(appDir, "summer.yaml"))
if err != nil {
return err
}
bin := filepath.Join(appDir, "bin", m.Binary)
if _, err := os.Stat(bin); err != nil {
if err := build.App(ctx, appDir, out); err != nil {
return err
}
}
self, err := os.Executable()
if err == nil {
if same, _ := sameFile(self, bin); same {
return fmt.Errorf("summer: refusing to recurse into %s", bin)
}
}
cmd := exec.CommandContext(ctx, bin, argv...)
cmd.Dir = appDir
cmd.Stdout = out
cmd.Stderr = out
cmd.Stdin = os.Stdin
cmd.Env = os.Environ()
if err := cmd.Run(); err != nil {
return fmt.Errorf("summer: %s: %w", strings.Join(argv, " "), err)
}
return nil
}
func sameFile(a, b string) (bool, error) {
ai, err := os.Stat(a)
if err != nil {
return false, err
}
bi, err := os.Stat(b)
if err != nil {
return false, err
}
return os.SameFile(ai, bi), nil
}