diff --git a/frontend/src/lib/SettingsPlugins.svelte b/frontend/src/lib/SettingsPlugins.svelte
index 35ef162..424177e 100644
--- a/frontend/src/lib/SettingsPlugins.svelte
+++ b/frontend/src/lib/SettingsPlugins.svelte
@@ -107,11 +107,11 @@
- {#if p.hasInstall && !p.installed}
+ {#if !p.installed}
- {:else if p.hasInstall && p.installed}
+ {:else}
- {:else}
-
{/if}
diff --git a/internal/core/plugins/manager.go b/internal/core/plugins/manager.go
index 28ff4e6..b5067e7 100644
--- a/internal/core/plugins/manager.go
+++ b/internal/core/plugins/manager.go
@@ -130,14 +130,18 @@ func (m *Manager) Discover() {
os.MkdirAll(dataDir, 0o750)
hasInstall := meta.Hooks["on_install"] != ""
+ if !hasInstall {
+ log.Printf("[plugins] %s: skipping — no on_install hook (not a managed plugin)", e.Name())
+ continue
+ }
m.plugins = append(m.plugins, Plugin{
- Meta: meta,
- Dir: filepath.Join(pluginsDir, e.Name()),
- DataDir: dataDir,
- Active: false,
- Installed: false,
- HasInstall: hasInstall,
+ Meta: meta,
+ Dir: filepath.Join(pluginsDir, e.Name()),
+ DataDir: dataDir,
+ Active: false,
+ Installed: false,
+ HasInstall: true,
})
}
}
@@ -158,10 +162,6 @@ func (m *Manager) SyncConfig(cfg *config.AppConfig) {
}
for i := range m.plugins {
installed := installedSet[m.plugins[i].Meta.Name]
- // Plugins without on_install hook are always "installed"
- if !m.plugins[i].HasInstall {
- installed = true
- }
m.plugins[i].Installed = installed
m.plugins[i].Active = installed && enabledSet[m.plugins[i].Meta.Name]
}
@@ -352,16 +352,14 @@ type NodeMeta struct {
Type string `json:"type"` // text, url, etc.
}
-// Enable activates a plugin by name.
-// If the plugin has on_install hook, it must be installed first.
+// Enable activates a plugin by name. Plugin must be installed first.
func (m *Manager) Enable(name string) error {
for i := range m.plugins {
if m.plugins[i].Meta.Name == name {
- p := &m.plugins[i]
- if p.HasInstall && !p.Installed {
+ if !m.plugins[i].Installed {
return fmt.Errorf("plugin %q must be installed first", name)
}
- p.Active = true
+ m.plugins[i].Active = true
return nil
}
}
diff --git a/internal/core/plugins/manager_test.go b/internal/core/plugins/manager_test.go
index 997b687..ae16b80 100644
--- a/internal/core/plugins/manager_test.go
+++ b/internal/core/plugins/manager_test.go
@@ -41,7 +41,8 @@ func TestDiscover(t *testing.T) {
"plugin.json": []byte(`{
"name": "client",
"version": "1.0.0",
- "description": "Client template",
+ "description": "Client plugin",
+ "hooks": { "on_install": "on_install" },
"templates": ["client"]
}`),
},
@@ -78,7 +79,8 @@ func TestDiscover(t *testing.T) {
t.Errorf("plugin name = %q", plugins[0].Meta.Name)
}
- // Enable plugin to load templates
+ // Install & enable plugin to load templates
+ mgr.Install("client")
mgr.Enable("client")
// Templates.
@@ -104,12 +106,12 @@ func TestEnableDisable(t *testing.T) {
root := setupPluginDir(t, map[string]*fsDir{
"plugin-a": {
files: map[string][]byte{
- "plugin.json": []byte(`{"name": "a", "version": "1.0"}`),
+ "plugin.json": []byte(`{"name": "a", "version": "1.0", "hooks": {"on_install": "on_install"}}`),
},
},
"plugin-b": {
files: map[string][]byte{
- "plugin.json": []byte(`{"name": "b", "version": "1.0"}`),
+ "plugin.json": []byte(`{"name": "b", "version": "1.0", "hooks": {"on_install": "on_install"}}`),
},
},
})
@@ -126,7 +128,9 @@ func TestEnableDisable(t *testing.T) {
t.Errorf("active after discover = %d, want 0", len(mgr.Active()))
}
- // Enable both.
+ // Install & enable both.
+ mgr.Install("a")
+ mgr.Install("b")
mgr.Enable("a")
mgr.Enable("b")
if len(mgr.Active()) != 2 {
@@ -148,12 +152,14 @@ func TestEnableDisable(t *testing.T) {
func TestActiveNames(t *testing.T) {
root := setupPluginDir(t, map[string]*fsDir{
- "p1": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p1"}`)}},
- "p2": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p2"}`)}},
+ "p1": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p1","hooks":{"on_install":"on_install"}}`)}},
+ "p2": {files: map[string][]byte{"plugin.json": []byte(`{"name":"p2","hooks":{"on_install":"on_install"}}`)}},
})
mgr := NewManager(root)
mgr.Discover()
+ mgr.Install("p1")
+ mgr.Install("p2")
mgr.Enable("p1")
mgr.Enable("p2")
mgr.Disable("p1")
diff --git a/internal/core/plugins/runtime_test.go b/internal/core/plugins/runtime_test.go
index 6dd7e4b..876f725 100644
--- a/internal/core/plugins/runtime_test.go
+++ b/internal/core/plugins/runtime_test.go
@@ -215,7 +215,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
pj := `{
"name": "testp",
"version": "1.0.0",
- "hooks": { "on_init": "on_init" }
+ "hooks": { "on_install": "on_install", "on_init": "on_init" }
}`
if err := os.WriteFile(filepath.Join(pluginsDir, "plugin.json"), []byte(pj), 0644); err != nil {
t.Fatal(err)
@@ -228,6 +228,7 @@ func TestPluginManager_InitRuntimes(t *testing.T) {
mgr := NewManager(dir)
mgr.Discover()
+ mgr.Install("testp")
mgr.Enable("testp")
mgr.InitRuntimes()
defer mgr.CloseRuntimes()