GitRoot

craft your forge, build your project, grow your community freely
  1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
  2//
  3// SPDX-License-Identifier: EUPL-1.2
  4
  5package main
  6
  7// compile cmd
  8// tinygo build -o ladybug-0.0.1.wasm -scheduler=none --no-debug -target=wasi ./
  9
 10import (
 11	"bytes"
 12	_ "embed"
 13	"fmt"
 14	"io/fs"
 15
 16	"github.com/sigurn/crc16"
 17	"github.com/tidwall/gjson"
 18	gitroot "gitroot.dev/libs/golang/plugin"
 19	"gitroot.dev/libs/golang/plugin/model"
 20)
 21
 22type MyConf struct {
 23	Name      string `json:"name"`
 24	Type      string `json:"type"`
 25	Mandatory bool   `json:"mandatory"`
 26	Default   any    `json:"default"`
 27}
 28
 29var defaultConf = map[string]any{"metadata": []MyConf{
 30	{Name: "id", Type: "crc16", Mandatory: true, Default: "autogenerated"},
 31	{Name: "priority", Type: "int", Mandatory: true, Default: 50},
 32	{Name: "assignee", Type: "user", Mandatory: false, Default: nil},
 33	{Name: "status", Type: "string", Mandatory: false, Default: "triage"},
 34	{Name: "kind", Type: "string", Mandatory: false, Default: "issue"},
 35}}
 36
 37var defaultRun = []model.PluginRun{{
 38	Path:   "issues/**/*.md",
 39	Branch: []string{"*"},
 40	When:   []model.PluginRunWhen{model.PluginRunWhenAdd, model.PluginRunWhenMod},
 41	Write: model.PluginWrite{
 42		Git: []model.PluginWriteRight{{
 43			Path: "issues/**/*.md",
 44			Can:  []model.PluginWriteRightCan{model.PluginWriteRightCanMod},
 45		}},
 46		Web: []model.PluginWriteRight{},
 47	},
 48	Configuration: defaultConf,
 49}}
 50
 51var table = crc16.MakeTable(crc16.CRC16_MAXIM)
 52
 53type Plugin struct {
 54	server gitroot.Server
 55	conf   []MyConf
 56}
 57
 58func (p *Plugin) Init(repoName string, confHasChanged bool, serializedConf string) error {
 59	p.conf = []MyConf{}
 60	for _, meta := range gjson.Parse(serializedConf).Get("metadata").Array() {
 61		myConf := MyConf{
 62			Name:      meta.Get("name").String(),
 63			Type:      meta.Get("type").String(),
 64			Mandatory: meta.Get("mandatory").Bool(),
 65			Default:   meta.Get("default").Value(),
 66		}
 67		p.conf = append(p.conf, myConf)
 68	}
 69	return nil
 70}
 71
 72func (p *Plugin) StartCommit(commit model.Commit) error {
 73	return nil
 74}
 75
 76func (p *Plugin) AddFile(filepath string) error {
 77	content, err := fs.ReadFile(p.server.Worktree(), filepath)
 78	if err != nil {
 79		p.server.LogError("AddFile ReadFile "+filepath, err)
 80		return nil
 81	}
 82	if !bytes.HasPrefix(content, []byte("---")) {
 83		header := ""
 84		for _, props := range p.conf {
 85			if props.Name != "" {
 86				if props.Mandatory {
 87					if props.Default != nil {
 88						def := props.Default
 89						if props.Default == "autogenerated" && props.Type == "crc16" {
 90							def = fmt.Sprintf("%X", crc16.Checksum([]byte(filepath), table))
 91						}
 92						header += fmt.Sprintf("%s: %v\n", props.Name, def)
 93					}
 94				} else {
 95					if props.Default != nil {
 96						header += fmt.Sprintf("%s: %v\n", props.Name, props.Default)
 97					} else {
 98						header += fmt.Sprintf("%s: null\n", props.Name)
 99					}
100				}
101			}
102		}
103		newContent := fmt.Sprintf("---\n%s---\n\n%s", header, string(content))
104		p.server.ModifyContent(filepath, newContent)
105	} else {
106		metas := bytes.Split(content, []byte("---"))
107		meta := bytes.Trim(metas[1], "\n")
108		header := ""
109		for _, props := range p.conf {
110			if props.Name != "" {
111				if !bytes.Contains(meta, []byte(props.Name)) {
112					if props.Mandatory {
113						if props.Default != "" {
114							def := props.Default
115							if props.Default == "autogenerated" && props.Type == "crc16" {
116								def = fmt.Sprintf("%X", crc16.Checksum([]byte(filepath), table))
117							}
118							header += fmt.Sprintf("%s: %s\n", props.Name, def)
119						}
120					} else {
121						if props.Default != nil && props.Default != "" {
122							header += fmt.Sprintf("%s: %s\n", props.Name, props.Default)
123						} else {
124							header += fmt.Sprintf("%s: null\n", props.Name)
125						}
126					}
127				}
128			}
129		}
130		//no change so don't override content
131		if header != "" {
132			newContent := fmt.Sprintf("---\n%s\n%s---%s", meta, header, bytes.Join(metas[2:], []byte("---")))
133			p.server.ModifyContent(filepath, newContent)
134		}
135	}
136	return nil
137}
138
139func (p *Plugin) ModFile(fromPath string, toPath string) error {
140	return p.AddFile(toPath)
141}
142
143func (p *Plugin) DelFile(path string) error {
144	return nil
145}
146
147func (p *Plugin) EndCommit(commit model.Commit) error {
148	return nil
149}
150
151func (p *Plugin) Finish() error {
152	p.server.CommitAllIfNeeded("issue metadata added")
153	return nil
154}
155
156func Build(server gitroot.Server) gitroot.Plugin {
157	return &Plugin{
158		server: server,
159	}
160}
161
162//go:wasmexport install
163func main() {
164	gitroot.Register(defaultRun, Build)
165}