# kiosk Plugin manifests and the plugin registry contract: generates and checks each plugin's plugin.yaml from its own sources. `import "git.golem15.com/golem15/summercms/modules/kiosk"` **Name:** A beach kiosk where every plugin on the shelf carries its label; the label is printed from what is in the box, never written by hand. ## Overview A plugin describes itself in `plugin.yaml`, committed next to its `plugin.go`. The file is generated: `summer plugin:manifest` runs `kiosk.Write`, which reads the plugin's sources and never runs its code. The ID and the required plugins come from what `ID()` and `Requires()` in `plugin.go` return, the module path and the version minimums from `go.mod`, the package import path from the directory, and the name and description from the README's H1 and the paragraph after it (the standard README structure). The registry on summercms.io reads the same file from every tagged release, and `summer build` bakes its name and description into the application. WinterCMS keeps this data in a plugin's `Plugin.php` `pluginDetails()` and in `composer.json`; here it is derived once, so the copy in the repository cannot drift from the code. ## Features - One `plugin.yaml` per plugin package directory, so a repository with a nested plugin carries two files. - `kiosk.ReadIdentity` reads `ID()` and `Requires()` statically with `go/parser`: a string literal, a constant of the same package, of another package of the module, or of a package in a module `go.mod` requires. It never builds or runs plugin code, and `summer plugin:add` reads plugin IDs with it too. - A schema version (`kiosk.SchemaVersion`) and a fixed header line (`kiosk.Header`); a manifest with a newer schema is refused with a hint to update summer. - Go-style minimums: `framework` is the `go.mod` require of `kiosk.FrameworkModule`, and each required plugin gets the version its module is required at. A requirement at `v0.0.0` is an error (not a released version); a pseudo-version is accepted with a warning. - Name and description overrides (`--name`, `--description`) stored in an `overrides:` block that regeneration keeps. - The registry API v1 contract in Go: `kiosk.RegistryAPIPath` and the wire types (`kiosk.ListResponse`, `kiosk.DetailResponse`, `kiosk.VersionsResponse`, `kiosk.ErrorResponse`, `kiosk.IngestRequest`, `kiosk.IngestResponse`). The registry server encodes with them and the summer tool and the admin decode with them, so the JSON shape exists once. - Strict version rules: `kiosk.ValidVersion` (only `vX.Y.Z`), `kiosk.CompareVersions`, `kiosk.BaseVersion` for `git describe` output, `kiosk.Satisfies` for Go-style minimums and `kiosk.NewestCompatible` to pick the release an application can install. - `kiosk.Check` regenerates in memory and returns a `kiosk.StaleError` when the committed file is missing or differs, which `summer plugin:manifest --check` turns into exit status 1, and `kiosk.CheckT` turns into a failing test in the plugin's own `go test`. ## Usage ```go path, warnings, err := kiosk.Write(ctx, "plugins/acme/blog", kiosk.DeriveOptions{}) if err != nil { return err } for _, w := range warnings { log.Println(w) } fmt.Println("wrote", path) if err := kiosk.Check(ctx, "plugins/acme/blog"); err != nil { var stale *kiosk.StaleError if errors.As(err, &stale) { fmt.Printf("expected:\n%s", stale.Want) } return err } ``` Picking the newest release of a plugin that an application on framework v0.6.2 can install: ```go var versions kiosk.VersionsResponse if err := json.Unmarshal(body, &versions); err != nil { return err } v, ok := kiosk.NewestCompatible(versions.Data, "v0.6.2") if !ok { return errors.New("every release needs a newer framework") } fmt.Println("go get", v.Module+"@"+v.Version) ``` A plugin repository keeps its manifest current with one test: ```go func TestPluginManifest(t *testing.T) { kiosk.CheckT(t, ".") } ``` ## API reference | Identifier | Description | |------------|-------------| | `kiosk.FileName` | The manifest file name, `plugin.yaml`. | | `kiosk.SchemaVersion` | The newest schema this package reads, and the one it writes. | | `kiosk.FrameworkModule` | The framework module path; its `go.mod` require is the plugin's framework minimum. | | `kiosk.Header` | The comment line every generated `plugin.yaml` starts with. | | `kiosk.Manifest` | One `plugin.yaml`: `Schema`, `ID`, `Module`, `Package`, `Name`, `Description`, `Framework`, `Requires`, `Overrides`, written in that order. | | `kiosk.Requires` | Required plugin ID to minimum version; marshals sorted by ID. An empty minimum means the plugin ships in the same module or its module is not a `go.mod` require. | | `kiosk.Requires.MarshalYAML` | Emits the map as a mapping sorted by ID. | | `kiosk.Overrides` | The persisted `--name` and `--description` values. | | `kiosk.Marshal` | The `plugin.yaml` bytes: the header line, then the fields. | | `kiosk.Parse` | Reads `plugin.yaml` bytes; unknown fields are ignored, a newer schema is an error. | | `kiosk.DeriveOptions` | Override changes for `kiosk.Derive`: nil keeps the persisted value, a pointer to `""` clears it. | | `kiosk.Derive` | Builds the manifest of a plugin directory from its sources and returns it with warnings. | | `kiosk.Generate` | `kiosk.Derive` plus `kiosk.Marshal`: the bytes `kiosk.Write` would write. | | `kiosk.Write` | Generates and writes `