fix(build): scaffold plugins under plugins/<vendor>/<name> (quick-261028-pq8)

- make:plugin writes plugins/<vendor>/<name> with module
  <app>/plugins/<vendor>/<name>; the package name stays <name>
- make:* and the models-leaf check look in summer.yaml, then
  plugins/<vendor>/<name>, then the older plugins/<name>
- summer new puts its starter plugin at plugins/golem15/<name>
- refuse a vendor directory that is a legacy plugin module and a second
  copy of a plugin that already sits in plugins/<name>
- help text and docs, with an upgrading note: moving is optional
This commit is contained in:
Jakub Zych
2026-10-28 18:55:46 +01:00
parent a941ab2394
commit a95a014b52
26 changed files with 247 additions and 86 deletions

View File

@@ -96,14 +96,14 @@ func makePluginCommand() bonfire.Command {
return bonfire.Command{
Name: "make:plugin",
Description: "Scaffold a compiling plugin module from a vendor.plugin id",
Long: `Creates plugins/<plugin> in the current application: a Go module with plugin.go
Long: `Creates plugins/<vendor>/<plugin> in the current application: a Go module with plugin.go
(the plugin ID, Register and Boot) and go.mod, with a replace for a local
framework checkout when the application uses one. Register it with plugin:add
so the next summer build compiles it in. Run it inside an application; the
directory must not exist yet.`,
Examples: []bonfire.Example{
{Command: "make:plugin acme.blog", Description: "Create plugins/blog with the ID acme.blog"},
{Command: "plugin:add plugins/blog", Description: "Then register it in summer.yaml, go.mod and go.work"},
{Command: "make:plugin acme.blog", Description: "Create plugins/acme/blog with the ID acme.blog"},
{Command: "plugin:add plugins/acme/blog", Description: "Then register it in summer.yaml, go.mod and go.work"},
},
Args: []bonfire.Arg{{
Name: "id",
@@ -142,7 +142,7 @@ plugin picks them up on the next build. Existing files and names are never
overwritten.`,
Examples: []bonfire.Example{
{Command: "make:model acme.blog Post", Description: "A Post model and its create-table migration"},
{Command: "make:model Post --no-migration", Description: "Inside plugins/blog: only the model"},
{Command: "make:model Post --no-migration", Description: "Inside plugins/acme/blog: only the model"},
},
Args: makeArtifactArgs(),
Flags: []bonfire.Flag{{
@@ -179,7 +179,7 @@ created them. registry.gen.go adds it to the plugin's migrations; run migrate
to apply it.`,
Examples: []bonfire.Example{
{Command: "make:migration acme.blog AddSlugToPosts", Description: "An empty migration in acme.blog"},
{Command: "make:migration AddSlugToPosts", Description: "The same from inside plugins/blog"},
{Command: "make:migration AddSlugToPosts", Description: "The same from inside plugins/acme/blog"},
},
Args: makeArtifactArgs(),
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
@@ -212,7 +212,7 @@ takes a parameter instead and is returned from the plugin's Commands by hand
(see the porting guide's blog:publish).`,
Examples: []bonfire.Example{
{Command: "make:command acme.blog Publish", Description: "A blog:publish command in acme.blog"},
{Command: "make:command Publish", Description: "The same from inside plugins/blog"},
{Command: "make:command Publish", Description: "The same from inside plugins/acme/blog"},
},
Args: makeArtifactArgs(),
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
@@ -243,7 +243,7 @@ generated registry adds it to the jobs the plugin returns. Dispatch it with
conga.Manager.Dispatch; queue:work or serve runs it.`,
Examples: []bonfire.Example{
{Command: "make:job acme.blog SendDigest", Description: "A SendDigest job in acme.blog"},
{Command: "make:job SendDigest", Description: "The same from inside plugins/blog"},
{Command: "make:job SendDigest", Description: "The same from inside plugins/acme/blog"},
},
Args: makeArtifactArgs(),
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
@@ -276,7 +276,7 @@ Permissions(), and its NewRecord returns nil until you return your model, so
the screens stay closed until you finish it.`,
Examples: []bonfire.Example{
{Command: "make:admin-controller acme.blog Posts", Description: "A Posts controller in acme.blog"},
{Command: "make:admin-controller Posts", Description: "The same from inside plugins/blog"},
{Command: "make:admin-controller Posts", Description: "The same from inside plugins/acme/blog"},
},
Args: makeArtifactArgs(),
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {

View File

@@ -118,10 +118,10 @@ func TestMakeCommandsViaCLI(t *testing.T) {
}
out := runSummer("make:plugin", "golem15.demo")
if !strings.Contains(out, filepath.Join("plugins", "demo")) {
if !strings.Contains(out, filepath.Join("plugins", "golem15", "demo")) {
t.Fatalf("make:plugin output missing plugin dir:\n%s", out)
}
pluginDir := filepath.Join(dir, "plugins", "demo")
pluginDir := filepath.Join(dir, "plugins", "golem15", "demo")
if _, err := os.Stat(filepath.Join(pluginDir, "plugin.go")); err != nil {
t.Fatal(err)
}
@@ -193,7 +193,7 @@ func TestNewCommandViaCLI(t *testing.T) {
t.Fatalf("new demo --no-git: %v\n%s", err, out)
}
app := filepath.Join(parent, "sm-demo-app")
for _, rel := range []string{"summer.yaml", "main.go", "plugins/demo/routes.go"} {
for _, rel := range []string{"summer.yaml", "main.go", "plugins/golem15/demo/routes.go"} {
if _, err := os.Stat(filepath.Join(app, rel)); err != nil {
t.Fatalf("%s missing: %v", rel, err)
}