Files
summercms/modules/kiosk/manifest.go
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

91 lines
2.9 KiB
Go

package kiosk
import (
"bytes"
"fmt"
"sort"
"github.com/goccy/go-yaml"
)
// Manifest is one plugin.yaml. Fields are written in declaration order.
type Manifest struct {
// Schema is the plugin.yaml format version (SchemaVersion when written).
Schema int `yaml:"schema"`
// ID is the plugin ID that plugin.go's ID() returns.
ID string `yaml:"id"`
// Module is the go.mod module path: what go get takes.
Module string `yaml:"module"`
// Package is the plugin's import path: what summer.yaml lists. It
// differs from Module for a plugin in a subdirectory of its module.
Package string `yaml:"package"`
// Name is the display name: the README's H1 or the name override.
Name string `yaml:"name"`
// Description is the README's one-sentence summary or the override.
Description string `yaml:"description"`
// Framework is the minimum framework version: the go.mod require of
// FrameworkModule.
Framework string `yaml:"framework"`
// Requires maps each plugin ID that Requires() returns to its minimum
// version; empty when the plugin ships in the same module or its
// module is not a go.mod require.
Requires Requires `yaml:"requires,omitempty"`
// Overrides holds the --name and --description values; nil when
// neither is set.
Overrides *Overrides `yaml:"overrides,omitempty"`
}
// Overrides are the name and description set with summer plugin:manifest
// --name and --description. They survive regeneration until cleared.
type Overrides struct {
Name string `yaml:"name,omitempty"`
Description string `yaml:"description,omitempty"`
}
// Requires maps a required plugin ID to its minimum version. It marshals
// with the IDs sorted, so the generated bytes are stable.
type Requires map[string]string
// MarshalYAML emits the map as a sorted mapping.
func (r Requires) MarshalYAML() (any, error) {
ids := make([]string, 0, len(r))
for id := range r {
ids = append(ids, id)
}
sort.Strings(ids)
out := make(yaml.MapSlice, 0, len(ids))
for _, id := range ids {
out = append(out, yaml.MapItem{Key: id, Value: r[id]})
}
return out, nil
}
// Marshal returns the plugin.yaml bytes: the Header line, then the fields.
func Marshal(m Manifest) ([]byte, error) {
if m.Overrides != nil && *m.Overrides == (Overrides{}) {
m.Overrides = nil
}
body, err := yaml.Marshal(m)
if err != nil {
return nil, fmt.Errorf("kiosk: marshal %s: %w", FileName, err)
}
var b bytes.Buffer
b.WriteString(Header)
b.WriteByte('\n')
b.Write(body)
return b.Bytes(), nil
}
// Parse reads plugin.yaml bytes. A schema newer than SchemaVersion is an
// error that says to update summer; unknown fields are ignored.
func Parse(data []byte) (Manifest, error) {
var m Manifest
if err := yaml.Unmarshal(data, &m); err != nil {
return Manifest{}, fmt.Errorf("kiosk: parse %s: %w", FileName, err)
}
if m.Schema > SchemaVersion {
return Manifest{}, fmt.Errorf("kiosk: %s has schema %d, this summer reads up to %d; update summer", FileName, m.Schema, SchemaVersion)
}
return m, nil
}