feat(03-01): add shared postgres pool and plugin migrations
Open one pgx stdlib *sql.DB, hand it to GORM, and run per-plugin gormigrate sets with isolated history tables after an ICU pl-PL check. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -32,6 +32,9 @@ func toolCommands() []bonfire.Command {
|
||||
parityProxyCommand(),
|
||||
parityRecordCommand(),
|
||||
parityReplayCommand(),
|
||||
delegateCommand("migrate", "Run plugin migrations in the app binary"),
|
||||
delegateRollbackCommand(),
|
||||
delegateCommand("migrate:status", "Show per-plugin migration history in the app binary"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestToolCommandNames(t *testing.T) {
|
||||
for _, c := range toolCommands() {
|
||||
names = append(names, c.Name)
|
||||
}
|
||||
for _, want := range []string{"build", "make:plugin", "plugin:add", "dev"} {
|
||||
for _, want := range []string{"build", "make:plugin", "plugin:add", "dev", "migrate", "migrate:rollback", "migrate:status"} {
|
||||
if !slices.Contains(names, want) {
|
||||
t.Fatalf("missing %s in %v", want, names)
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func TestToolDoesNotImportExamplePlugins(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(path, "examples/hello") {
|
||||
if strings.Contains(path, "examples/hello") || strings.Contains(path, "golem15/fonoteka") {
|
||||
t.Fatalf("%s imports %s", name, path)
|
||||
}
|
||||
}
|
||||
|
||||
96
cmd/summer/runtime.go
Normal file
96
cmd/summer/runtime.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/summercms/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 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
|
||||
}
|
||||
Reference in New Issue
Block a user