feat(build): bake plugin metadata and the framework version into plugins.gen.go

- party.Info, party.Installed, party.Describe (additive; Plugin and Activate unchanged)
- summer build resolves plugin dirs with one go list, reads plugin.yaml, versions per D-07 (module version, git describe without --dirty, none for in-app plugins)
- build.EffectiveFramework: newest tag of a locally replaced or used framework checkout, else the go.mod require
- generated main calls party.Describe(app, PluginInfo, FrameworkVersion) before Activate; metadata failures warn and fall back to IDs
- summer new goldens and examples/hello regenerated
This commit is contained in:
Jakub Zych
2026-10-29 17:12:13 +01:00
parent 4916ee3d43
commit e8d3d724f5
15 changed files with 770 additions and 15 deletions

View File

@@ -26,6 +26,7 @@ Use cases:
- A process-wide, concurrency-safe registry filled by `party.Register` (nil plugins are ignored).
- `party.Activate` selects plugins by manifest ID and fails on an empty ID, a duplicate ID, an unregistered plugin, a missing requirement or a dependency cycle.
- Stable topological ordering: plugins without a dependency relation keep their manifest order.
- Build-time metadata: `party.Describe` publishes `party.Installed`, the plugin metadata (`party.Info`: ID, module, package, name, description, version and source) and the framework version that `summer build` baked into the application's `plugins.gen.go`. The generated `main` calls it before `party.Activate`.
- Activation sequence: records the ordered IDs on the `backpack.App`, publishes `party.Activated` (the ordered plugin list for commands that need plugin scopes at Run time), merges each `pact.HasConfig` tree into the app config under the plugin ID, runs every Register, publishes the translator ([phrasebook](../phrasebook/README.md)), the mailer ([postcard](../postcard/README.md)) and the menu item type registry ([signpost](../signpost/README.md), collected from every `pact.HasMenuItemTypes` plugin; a duplicate type key fails the activation), then registers each plugin's mail templates and runs its Boot.
## Usage
@@ -52,7 +53,7 @@ func init() {
}
```
The application activates the plugins it lists, in dependency order:
The application publishes its build-time metadata and activates the plugins it lists, in dependency order (`summer build` generates both lists):
```go
cfg, err := compass.Load("config")
@@ -60,6 +61,13 @@ if err != nil {
return err
}
app := backpack.New(cfg)
info := []party.Info{
{ID: "acme.user", Source: party.SourceLocal},
{ID: "acme.blog", Version: "v0.2.5", Source: party.SourceModule},
}
if err := party.Describe(app, info, "v0.6.0"); err != nil {
return err
}
plugins, err := party.Activate(app, []string{"acme.user", "acme.blog"})
if err != nil {
return err
@@ -73,6 +81,12 @@ if err != nil {
| `party.Plugin` | Interface every compiled plugin implements: ID, required plugin IDs, Register and Boot. |
| `party.Register` | Adds a plugin to the process-wide registry; called from the plugin's `init`. |
| `party.Activate` | Selects registered plugins by ID, orders them by `party.Plugin.Requires` and runs the config, Register, translation, mail, menu item type and Boot steps; returns the ordered plugins. |
| `party.Info` | One plugin's build-time metadata: `ID`, `Module`, `Package`, `Name`, `Description` (from its `plugin.yaml`), `Version` and `Source`. |
| `party.SourceModule` | Source of a plugin built from the Go module cache; its version is the module version. |
| `party.SourceSubmodule` | Source of a plugin directory that is its own git repository; its version is `git describe --tags` output. |
| `party.SourceLocal` | Source of a plugin inside the application repository; it has no version. |
| `party.Installed` | What the application was built with: `Framework` and `Plugins` in manifest order; look it up with `app.Lookup[party.Installed]()`. |
| `party.Describe` | Publishes `party.Installed` on the app; a nil app is an error. |
| `party.Activated` | The ordered `[]party.Plugin` published by `party.Activate`; commands look it up at Run time with `app.Lookup[party.Activated]()`. |
## Dependencies