- kiosk.ReadIdentity: ID()/Requires() as literals or constants across packages and modules, read with go/parser, never run - plugin minimums from go.mod requires; v0.0.0 is an error, a pseudo-version warns - kiosk.Validate, kiosk.CheckT (no testing import) - plugin:add reads IDs through kiosk.ReadIdentity, so const-ID plugins register
6.9 KiB
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.yamlper plugin package directory, so a repository with a nested plugin carries two files. kiosk.ReadIdentityreadsID()andRequires()statically withgo/parser: a string literal, a constant of the same package, of another package of the module, or of a package in a modulego.modrequires. It never builds or runs plugin code, andsummer plugin:addreads 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:
frameworkis thego.modrequire ofkiosk.FrameworkModule, and each required plugin gets the version its module is required at. A requirement atv0.0.0is an error (not a released version); a pseudo-version is accepted with a warning. - Name and description overrides (
--name,--description) stored in anoverrides:block that regeneration keeps. kiosk.Checkregenerates in memory and returns akiosk.StaleErrorwhen the committed file is missing or differs, whichsummer plugin:manifest --checkturns into exit status 1, andkiosk.CheckTturns into a failing test in the plugin's owngo test.
Usage
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
}
A plugin repository keeps its manifest current with one test:
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 <dir>/plugin.yaml and returns its path. |
kiosk.Check |
Returns a kiosk.StaleError when <dir>/plugin.yaml is missing or differs from a fresh generation. |
kiosk.StaleError.Error |
Names the path, and for a stale file the first differing line. |
kiosk.TB |
The part of testing.TB that kiosk.CheckT calls (Helper, Fatalf); kiosk does not import testing. |
kiosk.CheckT |
Fails the test with the kiosk.Check error and the command to rerun when the manifest is missing or stale. |
kiosk.Validate |
Checks a manifest: schema, ID and required IDs, module and package paths (no leading -), name, description, and vX.Y.Z or pseudo-version minimums. kiosk.Parse and kiosk.Generate call it. |
kiosk.Identity |
The plugin ID and the required plugin IDs. |
kiosk.ReadIdentity |
Reads what ID() and Requires() return from the sources of a directory, resolving constants across packages and modules without running code. Any other expression is an error naming the method. |
kiosk.StaleError |
Path, Missing, and the generated (Want) and committed (Got) bytes; its message names the first differing line. |
CLI commands
| Command | Purpose |
|---|---|
summer plugin:manifest [dir] |
Writes plugin.yaml for the plugin in dir (default: the current directory). --pretend prints it instead, --check fails when the committed file is missing or stale, --name and --description set or (with an empty value) clear the overrides. |
Dependencies
- SummerCMS modules: none.
- Third-party:
github.com/goccy/go-yaml(manifest YAML),github.com/yuin/goldmark(README headings and paragraphs). - Standard library:
bytes,context,encoding/json,errors,fmt,go/ast,go/parser,go/token,go/types(expression printing only),os,os/exec,path/filepath,regexp,slices,sort,strconv,strings. - Tools: the
gocommand (go mod edit -json,go list -m -json, andgo mod download -jsonfor a required module not in the module cache), run without a shell and with workspaces off.
Testing
go test ./modules/kiosk/...
TestManifestSmoke copies the fixture plugins in testdata/ to a temporary directory and generates, checks and edits their manifests: a literal-ID plugin, a plugin whose identity is spread over constants of three packages and two modules, a nested plugin, the error cases, overrides, kiosk.CheckT and kiosk.Validate. Every fixture module replaces the framework with the stub in testdata/framework, so the tests run the local go command but need no network and no database.