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

92 lines
2.5 KiB
Go

package kiosk
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
)
// Generate derives the manifest of the plugin in dir and returns the
// plugin.yaml bytes and any warnings, without writing anything.
func Generate(ctx context.Context, dir string, opts DeriveOptions) ([]byte, []string, error) {
m, warnings, err := Derive(ctx, dir, opts)
if err != nil {
return nil, warnings, err
}
out, err := Marshal(m)
if err != nil {
return nil, warnings, err
}
return out, warnings, nil
}
// Write generates dir's plugin.yaml and writes it, returning its path.
func Write(ctx context.Context, dir string, opts DeriveOptions) (path string, warnings []string, err error) {
out, warnings, err := Generate(ctx, dir, opts)
if err != nil {
return "", warnings, err
}
path = filepath.Join(dir, FileName)
if err := os.WriteFile(path, out, 0o644); err != nil {
return "", warnings, fmt.Errorf("kiosk: write %s: %w", path, err)
}
return path, warnings, nil
}
// StaleError reports a plugin.yaml that is missing or differs from what
// summer plugin:manifest would write now.
type StaleError struct {
// Path is the plugin.yaml path.
Path string
// Missing is true when the file does not exist.
Missing bool
// Want is the freshly generated content; Got is the committed one.
Want, Got []byte
}
// Error names the path and, for a stale file, the first differing line.
func (e *StaleError) Error() string {
if e.Missing {
return fmt.Sprintf("kiosk: %s is missing; generate it with summer plugin:manifest", e.Path)
}
want := bytes.Split(e.Want, []byte("\n"))
got := bytes.Split(e.Got, []byte("\n"))
for i := 0; i < len(want) || i < len(got); i++ {
var w, g []byte
if i < len(want) {
w = want[i]
}
if i < len(got) {
g = got[i]
}
if !bytes.Equal(w, g) {
return fmt.Sprintf("kiosk: %s is stale: line %d is %q, want %q", e.Path, i+1, g, w)
}
}
return fmt.Sprintf("kiosk: %s is stale", e.Path)
}
// Check regenerates dir's plugin.yaml in memory, keeping its overrides,
// and returns a *StaleError when the file on disk is missing or differs.
func Check(ctx context.Context, dir string) error {
want, _, err := Generate(ctx, dir, DeriveOptions{})
if err != nil {
return err
}
path := filepath.Join(dir, FileName)
got, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
return &StaleError{Path: path, Missing: true, Want: want}
}
if err != nil {
return fmt.Errorf("kiosk: read %s: %w", path, err)
}
if !bytes.Equal(want, got) {
return &StaleError{Path: path, Want: want, Got: got}
}
return nil
}