feat(kiosk): resolve constant plugin IDs, overrides and the CheckT helper

- 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
This commit is contained in:
Jakub Zych
2026-10-29 17:02:38 +01:00
parent ec6a1955fc
commit 51ec2f9daa
22 changed files with 1156 additions and 115 deletions

View File

@@ -5,19 +5,16 @@ import (
"context"
"embed"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"go/version"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"text/template"
"git.golem15.com/golem15/summercms/modules/bonfire"
"git.golem15.com/golem15/summercms/modules/kiosk"
)
//go:embed stubs/*.tmpl
@@ -357,38 +354,19 @@ func frameworkReplaceFor(pluginDir, appDir string) (string, error) {
return filepath.ToSlash(rel), nil
}
// pluginIDFromGo reads the plugin ID from the package of the plugin.go at
// path with kiosk.ReadIdentity: a string literal or a constant, resolved
// without running any code.
func pluginIDFromGo(path string) (string, error) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, 0)
if err != nil {
if _, err := os.Stat(path); err != nil {
return "", fmt.Errorf("build: parse %s: %w", path, err)
}
var id string
ast.Inspect(file, func(n ast.Node) bool {
fn, ok := n.(*ast.FuncDecl)
if !ok || fn.Name == nil || fn.Name.Name != "ID" || fn.Recv == nil || fn.Body == nil {
return true
}
for _, stmt := range fn.Body.List {
ret, ok := stmt.(*ast.ReturnStmt)
if !ok || len(ret.Results) != 1 {
continue
}
lit, ok := ret.Results[0].(*ast.BasicLit)
if !ok || lit.Kind != token.STRING {
continue
}
s, err := strconv.Unquote(lit.Value)
if err == nil {
id = s
}
}
return true
})
if id == "" {
return "", fmt.Errorf("build: %s does not return a plugin id", path)
id, err := kiosk.ReadIdentity(context.Background(), filepath.Dir(path))
if id.ID != "" {
// A Requires() that kiosk cannot read does not hide the ID.
return id.ID, nil
}
return id, nil
return "", fmt.Errorf("build: %s does not return a plugin id: %w", path, err)
}
func editAppModule(ctx context.Context, appDir, pluginDir, modPath string) error {