Files
summercms/modules/kiosk
Jakub Zych ec6a1955fc feat(kiosk): plugin.yaml manifest and summer plugin:manifest
- new module kiosk: Manifest, Marshal, Parse, Derive, Generate, Write, Check, StaleError
- plugin:manifest [dir] with --check, --pretend, --name, --description
- docs: kiosk README, root table row, plugins/marketplace.md, scaffolding row
2026-10-29 16:55:17 +01:00
..

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.
  • 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.
  • Name and description overrides (--name, --description) stored in an overrides: block that regeneration keeps.
  • 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.

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
}

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 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, os, os/exec, path/filepath, regexp, sort, strconv, strings.
  • Tools: the go command (go mod edit -json), run without a shell.

Testing

go test ./modules/kiosk/...

TestManifestSmoke copies the fixture plugins in testdata/ to a temporary directory and generates, checks and edits their manifests. The tests run the local go command but need no network and no database.