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}