refactor(plugins): consolidate PDK module path and update Go version to 1.25

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan 2025-12-30 14:56:19 -05:00
parent 034e8af90b
commit c19ce124d5
27 changed files with 320 additions and 171 deletions

1
go.mod
View File

@ -21,7 +21,6 @@ require (
github.com/djherbis/stream v1.4.0
github.com/djherbis/times v1.6.0
github.com/dustin/go-humanize v1.0.1
github.com/extism/go-pdk v1.1.3
github.com/extism/go-sdk v1.7.1
github.com/fatih/structs v1.1.0
github.com/go-chi/chi/v5 v5.2.3

2
go.sum
View File

@ -57,8 +57,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE=
github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q=
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
github.com/extism/go-sdk v1.7.1 h1:lWJos6uY+tRFdlIHR+SJjwFDApY7OypS/2nMhiVQ9Sw=
github.com/extism/go-sdk v1.7.1/go.mod h1:IT+Xdg5AZM9hVtpFUA+uZCJMge/hbvshl8bwzLtFyKA=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=

View File

@ -792,7 +792,7 @@ See [capabilities/README.md](capabilities/README.md) for available schemas and s
### Using Host Service SDKs
Generated SDKs for calling host services are in `plugins/pdk/go/host/`, `plugins/pdk/python/` and `plugins/pdk/rust`.
Generated SDKs for calling host services are in `plugins/pdk/go/`, `plugins/pdk/python/` and `plugins/pdk/rust`.
**For Go plugins:** Import the SDK as a Go module:
@ -803,11 +803,11 @@ import ndhost "github.com/navidrome/navidrome/plugins/pdk/go/host"
Add to your `go.mod`:
```
require github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go
```
See [pdk/go/host/README.md](pdk/go/host/README.md) for detailed documentation.
See [pdk/go/README.md](pdk/go/README.md) for detailed documentation.
**For Python plugins:** Copy functions from `nd_host_*.py` into your `__init__.py` (see comments in those files for extism-py limitations).

View File

@ -31,7 +31,7 @@ ndpgen -input <dir> -output <dir> [-package <name>] [-v] [-dry-run] [-host-only]
```bash
go run ./plugins/cmd/ndpgen \
-input ./plugins/host \
-output ./plugins/pdk/go/host
-output ./plugins/pdk
```
## Annotations
@ -92,7 +92,7 @@ type SubsonicAPIService interface {
### Go Client Library (Go/TinyGo WASM)
Generated files are named `nd_host_<servicename>.go` (lowercase) and placed directly in the output directory. The output directory becomes a complete Go module (`github.com/navidrome/navidrome/plugins/pdk/go/host`) with package name `ndpdk`, intended for import by Navidrome plugins built with TinyGo.
Generated files are named `nd_host_<servicename>.go` (lowercase) and placed in `$output/go/host/`. The `$output/go/` directory becomes a complete Go module (`github.com/navidrome/navidrome/plugins/pdk/go`) with package name `host`, intended for import by Navidrome plugins built with TinyGo.
The generator creates:
- `nd_host_<servicename>.go` - Client wrapper code (WASM build)

View File

@ -166,9 +166,11 @@ type ServiceB interface {
Expect(filepath.Join(goHostDir, "nd_host_test.go")).To(BeAnExistingFile())
// Stub file also generated
Expect(filepath.Join(goHostDir, "nd_host_test_stub.go")).To(BeAnExistingFile())
// doc.go and go.mod also generated
// doc.go in host dir
Expect(filepath.Join(goHostDir, "doc.go")).To(BeAnExistingFile())
Expect(filepath.Join(goHostDir, "go.mod")).To(BeAnExistingFile())
// go.mod at parent $output/go/ for consolidated module
goDir := filepath.Join(outputDir, "go")
Expect(filepath.Join(goDir, "go.mod")).To(BeAnExistingFile())
})
})
@ -332,15 +334,18 @@ type ServiceB interface {
pluginDir := filepath.Join(outputDir, "plugin")
Expect(os.MkdirAll(pluginDir, 0750)).To(Succeed())
// go.mod is at parent $output/go/ for consolidated module
goDir := filepath.Join(outputDir, "go")
// Create go.mod for the plugin that imports the generated library
goMod := fmt.Sprintf(`module testplugin
go 1.24
go 1.25
require github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go/host => %s
`, goHostDir)
replace github.com/navidrome/navidrome/plugins/pdk/go => %s
`, goDir)
Expect(os.WriteFile(filepath.Join(pluginDir, "go.mod"), []byte(goMod), 0600)).To(Succeed())
// Add a simple main function that imports and uses the ndpdk package
@ -357,7 +362,7 @@ var _ = ndpdk.ComprehensiveNoParams
// Tidy dependencies for the generated go library
goTidyLibCmd := exec.Command("go", "mod", "tidy")
goTidyLibCmd.Dir = goHostDir
goTidyLibCmd.Dir = goDir
goTidyLibOutput, err := goTidyLibCmd.CombinedOutput()
Expect(err).ToNot(HaveOccurred(), "go mod tidy (library) failed: %s", goTidyLibOutput)

View File

@ -573,11 +573,13 @@ var _ = Describe("Generator", func() {
codeStr := string(code)
// Check for module declaration (using new PDK path)
Expect(codeStr).To(ContainSubstring("module github.com/navidrome/navidrome/plugins/pdk/go/host"))
// Check for module declaration (consolidated PDK path at pdk/go level)
Expect(codeStr).To(ContainSubstring("module github.com/navidrome/navidrome/plugins/pdk/go"))
// Ensure it's not the old host-specific path
Expect(codeStr).NotTo(ContainSubstring("module github.com/navidrome/navidrome/plugins/pdk/go/host"))
// Check for Go version
Expect(codeStr).To(ContainSubstring("go 1.24"))
Expect(codeStr).To(ContainSubstring("go 1.25"))
// Check for extism-go-pdk dependency
Expect(codeStr).To(ContainSubstring("github.com/extism/go-pdk"))

View File

@ -1,5 +1,5 @@
module github.com/navidrome/navidrome/plugins/pdk/go/host
module github.com/navidrome/navidrome/plugins/pdk/go
go 1.24
go 1.25
require github.com/extism/go-pdk v1.1.3

View File

@ -601,21 +601,25 @@ func generateGoDocFile(services []internal.Service, outputDir, pkgName string, d
}
// generateGoModFile generates the go.mod file for the Go library.
// The go.mod is placed at the parent directory ($output/go/) to create a unified
// module that includes both host wrappers and capabilities.
func generateGoModFile(outputDir string, dryRun, verbose bool) error {
code, err := internal.GenerateGoMod()
if err != nil {
return fmt.Errorf("generating go.mod: %w", err)
}
modFile := filepath.Join(outputDir, "go.mod")
// Output to parent directory ($output/go/) instead of host directory
parentDir := filepath.Dir(outputDir)
modFile := filepath.Join(parentDir, "go.mod")
if dryRun {
fmt.Printf("=== %s ===\n%s\n", modFile, code)
return nil
}
// Create output directory if needed
if err := os.MkdirAll(outputDir, 0755); err != nil {
// Create parent directory if needed
if err := os.MkdirAll(parentDir, 0755); err != nil {
return fmt.Errorf("creating output directory: %w", err)
}

View File

@ -4,10 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0-00010101000000-000000000000
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0-00010101000000-000000000000
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,10 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0-00010101000000-000000000000
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0-00010101000000-000000000000
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -2,8 +2,8 @@ module minimal-plugin
go 1.25
require github.com/navidrome/navidrome v0.0.0-00010101000000-000000000000
require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
require github.com/extism/go-pdk v1.1.3 // indirect
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,7 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0-00010101000000-000000000000
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -38,4 +38,5 @@
// indicating they should not be edited manually.
//
//go:generate go run ../cmd/ndpgen -input=. -output=../pdk -go -python -rust
//go:generate go mod tidy -C ../pdk/go
package host

251
plugins/pdk/go/README.md Normal file
View File

@ -0,0 +1,251 @@
# Navidrome Plugin Development Kit for Go
This directory contains the auto-generated Go PDK (Plugin Development Kit) for building Navidrome plugins.
The PDK provides both **host function wrappers** for interacting with Navidrome and
**capability interfaces** for implementing plugin functionality.
## ⚠️ Auto-Generated Code
**Do not edit files in this directory manually.** They are generated by the `ndpgen` tool.
To regenerate:
```bash
make gen
```
## Module Structure
This is a consolidated Go module that includes:
- `host/` - Host function wrappers for calling Navidrome services from plugins
- `lifecycle/` - Plugin lifecycle hooks (initialization)
- `metadata/` - Metadata agent capability for artist/album info
- `scheduler/` - Scheduler callback capability for scheduled tasks
- `scrobbler/` - Scrobbler capability for play tracking
- `websocket/` - WebSocket callback capability for real-time messages
## Usage
Add this module as a dependency in your plugin's `go.mod`:
```go
require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go
```
Then import the packages you need:
```go
package main
import (
"github.com/navidrome/navidrome/plugins/pdk/go/host"
"github.com/navidrome/navidrome/plugins/pdk/go/lifecycle"
"github.com/navidrome/navidrome/plugins/pdk/go/scheduler"
)
func init() {
lifecycle.Register(&myPlugin{})
scheduler.Register(&myPlugin{})
}
type myPlugin struct{}
func (p *myPlugin) OnInit() error {
// Initialize your plugin
return nil
}
func (p *myPlugin) OnSchedulerCallback(req scheduler.SchedulerCallbackRequest) error {
// Handle scheduled task
return host.WebSocketBroadcast("task-complete", req.ScheduleID)
}
func main() {}
```
## Host Services
The `host` package provides wrappers for calling Navidrome's host services:
| Service | Description |
|---------------|----------------------------------------------------|
| `Artwork` | Access album and artist artwork |
| `Cache` | Temporary key-value storage with TTL |
| `KVStore` | Persistent key-value storage |
| `Library` | Access the music library (albums, artists, tracks) |
| `Scheduler` | Schedule one-time and recurring tasks |
| `SubsonicAPI` | Make Subsonic API calls |
| `WebSocket` | Send real-time messages to clients |
### Example: Using Host Services
```go
package main
import (
"github.com/navidrome/navidrome/plugins/pdk/go/host"
)
func myPluginFunction() error {
// Use the cache service
_, err := host.CacheSetString("my_key", "my_value", 3600)
if err != nil {
return err
}
// Schedule a recurring task
_, err = host.SchedulerScheduleRecurring("@every 5m", "payload", "task_id")
if err != nil {
return err
}
// Access library data with typed structs
resp, err := host.LibraryGetAllLibraries()
if err != nil {
return err
}
for _, lib := range resp.Result {
// Library: %s with %d songs", lib.Name, lib.TotalSongs
}
return nil
}
```
## Capabilities
Capabilities define what functionality your plugin implements. Register your implementations
in the `init()` function.
### Lifecycle
Provides plugin initialization hooks.
```go
import "github.com/navidrome/navidrome/plugins/pdk/go/lifecycle"
func init() {
lifecycle.Register(&myPlugin{})
}
type myPlugin struct{}
func (p *myPlugin) OnInit() error {
// Called once when plugin is loaded
return nil
}
```
### MetadataAgent
Provides artist and album metadata from external sources.
```go
import "github.com/navidrome/navidrome/plugins/pdk/go/metadata"
func init() {
metadata.Register(&myAgent{})
}
type myAgent struct{}
func (a *myAgent) GetArtistBiography(req metadata.ArtistRequest) (*metadata.ArtistBiographyResponse, error) {
return &metadata.ArtistBiographyResponse{
Biography: "Artist biography text...",
}, nil
}
func (a *myAgent) GetArtistImages(req metadata.ArtistRequest) (*metadata.ArtistImagesResponse, error) {
return &metadata.ArtistImagesResponse{
Images: []metadata.ImageInfo{
{URL: "https://example.com/image.jpg", Size: 1000},
},
}, nil
}
```
### Scheduler
Handles callbacks from scheduled tasks.
```go
import (
"github.com/navidrome/navidrome/plugins/pdk/go/host"
"github.com/navidrome/navidrome/plugins/pdk/go/scheduler"
)
func init() {
scheduler.Register(&myScheduler{})
}
type myScheduler struct{}
func (s *myScheduler) OnSchedulerCallback(req scheduler.SchedulerCallbackRequest) error {
// Handle the scheduled task
if req.Payload == "update-data" {
// Do work...
return host.WebSocketBroadcast("data-updated", "")
}
return nil
}
```
### Scrobbler
Tracks play activity.
```go
import "github.com/navidrome/navidrome/plugins/pdk/go/scrobbler"
func init() {
scrobbler.Register(&myScrobbler{})
}
type myScrobbler struct{}
func (s *myScrobbler) Scrobble(req scrobbler.ScrobbleRequest) error {
// Track the play
return nil
}
func (s *myScrobbler) NowPlaying(req scrobbler.NowPlayingRequest) error {
// Update now playing status
return nil
}
```
### WebSocket
Handles incoming WebSocket messages.
```go
import "github.com/navidrome/navidrome/plugins/pdk/go/websocket"
func init() {
websocket.Register(&myHandler{})
}
type myHandler struct{}
func (h *myHandler) OnWebSocketMessage(req websocket.WebSocketMessageRequest) error {
// Handle incoming message
return nil
}
```
## Building Plugins
Go plugins must be compiled to WebAssembly using TinyGo:
```bash
tinygo build -o plugin.wasm -target=wasip1 -buildmode=c-shared .
```
Or use the provided Makefile targets in plugin examples:
```bash
make plugin.wasm
```

5
plugins/pdk/go/go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/navidrome/navidrome/plugins/pdk/go
go 1.25
require github.com/extism/go-pdk v1.1.3

View File

@ -1 +1,2 @@
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=

View File

@ -1,98 +0,0 @@
# Navidrome Host Function Wrappers for Go
This directory contains auto-generated Go wrappers for Navidrome's host services.
These wrappers provide idiomatic Go APIs for interacting with Navidrome from WASM plugins.
## ⚠️ Auto-Generated Code
**Do not edit these files manually.** They are generated by the `ndpgen` tool.
To regenerate:
```bash
make gen
```
## Usage
Add this module as a dependency in your plugin's `go.mod`:
```go
require github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
```
Then import the package in your plugin code:
```go
package main
import (
host "github.com/navidrome/navidrome/plugins/pdk/go/host"
"github.com/extism/go-pdk"
)
func myPluginFunction() error {
// Use the cache service
_, err := host.CacheSetString("my_key", "my_value", 3600)
if err != nil {
return err
}
// Schedule a recurring task
_, err = host.SchedulerScheduleRecurring("@every 5m", "payload", "task_id")
if err != nil {
return err
}
// Access library data with typed structs
resp, err := host.LibraryGetAllLibraries()
if err != nil {
return err
}
for _, lib := range resp.Result {
pdk.Log(pdk.LogInfo, fmt.Sprintf("Library: %s with %d songs", lib.Name, lib.TotalSongs))
}
return nil
}
```
## Typed Structs
Services that work with domain objects provide typed Go structs instead of
`map[string]interface{}`. This enables compile-time type checking and IDE
autocompletion.
For example, the library service provides a `Library` struct:
```go
resp, err := host.LibraryGetAllLibraries()
if err != nil {
return err
}
for _, lib := range resp.Result {
fmt.Printf("Library: %s (%d songs)\n", lib.Name, lib.TotalSongs)
}
```
## Available Services
| Module | Description |
|---------------|----------------------------------------------------|
| `Artwork` | Access album and artist artwork |
| `Cache` | Temporary key-value storage with TTL |
| `KVStore` | Persistent key-value storage |
| `Library` | Access the music library (albums, artists, tracks) |
| `Scheduler` | Schedule one-time and recurring tasks |
| `SubsonicAPI` | Make Subsonic API calls |
| `WebSocket` | Send real-time messages to clients |
## Building Plugins
Go plugins must be compiled to WebAssembly using TinyGo:
```bash
tinygo build -o plugin.wasm -target=wasip1 -buildmode=c-shared .
```

View File

@ -1,5 +0,0 @@
module github.com/navidrome/navidrome/plugins/pdk/go/host
go 1.24
require github.com/extism/go-pdk v1.1.3

View File

@ -1,10 +1,10 @@
module test-artwork
go 1.24
go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,7 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -1,10 +1,10 @@
module test-kvstore
go 1.24
go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -1,10 +1,10 @@
module test-library
go 1.24
go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,7 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -2,13 +2,8 @@ module test-scheduler
go 1.25
require (
github.com/navidrome/navidrome v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
)
require github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
require github.com/extism/go-pdk v1.1.3 // indirect
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,7 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -1,10 +1,10 @@
module test-subsonicapi-plugin
go 1.24
go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go

View File

@ -4,10 +4,7 @@ go 1.25
require (
github.com/extism/go-pdk v1.1.3
github.com/navidrome/navidrome v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go/host v0.0.0
github.com/navidrome/navidrome/plugins/pdk/go v0.0.0
)
replace github.com/navidrome/navidrome => ../../..
replace github.com/navidrome/navidrome/plugins/pdk/go/host => ../../pdk/go/host
replace github.com/navidrome/navidrome/plugins/pdk/go => ../../pdk/go