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 plugin
6
7import (
8 "encoding/json"
9 "fmt"
10 "io"
11 "os"
12 "reflect"
13 "slices"
14 "strings"
15
16 "github.com/goccy/go-yaml"
17 "github.com/samber/oops"
18 "gitroot.dev/libs/golang/glob"
19 pluginLib "gitroot.dev/libs/golang/plugin"
20 "gitroot.dev/server/user"
21)
22
23type Plugin struct {
24 Url string `yaml:",omitempty"`
25 Crc string `yaml:",omitempty"`
26 Name string
27 Active bool
28 content []byte
29 commiter *user.Commiter
30 Run []PluginRun
31}
32
33type PluginRun struct {
34 pluginLib.PluginRun `yaml:",inline"`
35 glob *glob.Glob
36 write PluginWrite
37}
38
39type PluginWrite struct { //TODO not implemented
40 git []PluginWriteRight
41 web []PluginWriteRight
42}
43
44type PluginWriteRight struct {
45 pluginLib.PluginWriteRight
46 glob *glob.Glob
47}
48
49func pluginLibToPlugin(run []pluginLib.PluginRun) ([]PluginRun, error) {
50 p := make([]PluginRun, len(run))
51 for i, pl := range run {
52 g, err := glob.NewGlob(pl.Path)
53 if err != nil {
54 return nil, oops.Wrapf(err, "can't glob %s", pl.Path)
55 }
56 pluginWrite := PluginWrite{
57 git: make([]PluginWriteRight, len(pl.Write.Git)),
58 web: make([]PluginWriteRight, len(pl.Write.Web)),
59 }
60 for j, pw := range pl.Write.Git {
61 g, err := glob.NewGlob(pl.Path)
62 if err != nil {
63 return nil, oops.Wrapf(err, "can't glob %s", pl.Path)
64 }
65 pluginWrite.git[j] = PluginWriteRight{PluginWriteRight: pw, glob: g}
66 }
67 for j, pw := range pl.Write.Web {
68 g, err := glob.NewGlob(pl.Path)
69 if err != nil {
70 return nil, oops.Wrapf(err, "can't glob %s", pl.Path)
71 }
72 pluginWrite.web[j] = PluginWriteRight{PluginWriteRight: pw, glob: g}
73 }
74 p[i] = PluginRun{PluginRun: pl, glob: g, write: pluginWrite}
75 }
76 return p, nil
77}
78
79func ParsePlugins(fileContent []byte, withRun bool) ([]Plugin, error) {
80 plugins := make([]Plugin, 0)
81 if err := yaml.Unmarshal(fileContent, &plugins); err != nil {
82 return nil, oops.Wrapf(err, "can't parse yaml config")
83 }
84 if withRun {
85 for i, plugin := range plugins {
86 for j, pluginRun := range plugin.Run {
87 g, err := glob.NewGlob(pluginRun.Path)
88 if err != nil {
89 return nil, oops.Wrapf(err, "can't glob %s", pluginRun.Path)
90 }
91 plugins[i].Run[j].glob = g
92 for h, pluginWrite := range pluginRun.Write.Git {
93 g, err = glob.NewGlob(pluginWrite.Path)
94 if err != nil {
95 return nil, oops.Wrapf(err, "can't glob git %s", pluginRun.Path)
96 }
97 if plugins[i].Run[j].write.git == nil {
98 plugins[i].Run[j].write.git = make([]PluginWriteRight, len(pluginRun.Write.Git))
99 }
100 plugins[i].Run[j].write.git[h] = PluginWriteRight{PluginWriteRight: pluginWrite, glob: g}
101 }
102 for h, pluginWrite := range pluginRun.Write.Web {
103 g, err = glob.NewGlob(pluginWrite.Path)
104 if err != nil {
105 return nil, oops.Wrapf(err, "can't glob git %s", pluginRun.Path)
106 }
107 if plugins[i].Run[j].write.web == nil {
108 plugins[i].Run[j].write.web = make([]PluginWriteRight, len(pluginRun.Write.Web))
109 }
110 plugins[i].Run[j].write.web[h] = PluginWriteRight{PluginWriteRight: pluginWrite, glob: g}
111 }
112 }
113 }
114 }
115 return plugins, nil
116}
117
118func (p Plugin) SetActive(active bool) Plugin {
119 return Plugin{
120 Url: p.Url,
121 Crc: p.Crc,
122 Name: p.Name,
123 Active: active,
124 content: p.content,
125 commiter: p.commiter,
126 Run: p.Run,
127 }
128}
129
130func (p Plugin) OverrideWith(other Plugin) Plugin {
131 copy := Plugin{
132 Url: p.Url,
133 Crc: p.Crc,
134 Name: p.Name,
135 Active: p.Active,
136 content: p.content,
137 commiter: p.commiter,
138 Run: p.Run,
139 }
140 if copy.Url == "" {
141 copy.Url = other.Url
142 }
143 if copy.Crc == "" {
144 copy.Crc = other.Crc
145 }
146 if copy.Name == "" {
147 copy.Name = other.Name
148 }
149 if !copy.Active {
150 copy.Active = other.Active
151 }
152 if len(copy.Run) == 0 {
153 copy.Run = other.Run
154 }
155 if len(copy.content) == 0 {
156 copy.content = other.content
157 }
158 if copy.commiter == nil {
159 copy.commiter = other.commiter
160 }
161 return copy
162}
163
164func (p Plugin) Commiter() *user.Commiter {
165 return p.commiter
166}
167
168func (p Plugin) DetermineNameFromUrl() Plugin {
169 versions := strings.Split(p.Url, "/") // todo find a better way to have version
170 name := versions[len(versions)-1]
171 name = strings.Split(name, "-")[0]
172 return p.OverrideWith(Plugin{Name: name})
173}
174
175func (pr PluginRun) Marshal() ([]byte, error) {
176 return json.Marshal(pr.Configuration)
177}
178
179func (m *Manager) PathWasm(p Plugin) string {
180 pathPlugin := m.conf.GetDirPathDataPlugin(p.Name)
181 versions := strings.Split(p.Url, "/") // TODO find a better way to have version
182 version := versions[len(versions)-1]
183 version = strings.Split(version, "-")[1]
184 return fmt.Sprintf("%s/%s", pathPlugin, fmt.Sprintf("%s-%s", p.Name, version))
185}
186
187func (m *Manager) Install(p Plugin) error {
188 r, err := os.Open(p.Url)
189 if err != nil {
190 return err
191 }
192 defer r.Close()
193 w, err := os.Create(m.PathWasm(p))
194 if err != nil {
195 return err
196 }
197 defer func() {
198 if c := w.Close(); err == nil {
199 err = c
200 }
201 }()
202 _, err = io.Copy(w, r)
203 return err
204}
205
206func (m *Manager) Equal(aPlugin, bPlugin Plugin) bool {
207 return aPlugin.Name == bPlugin.Name &&
208 aPlugin.Active == bPlugin.Active &&
209 m.equalPluginRun(aPlugin.Run, bPlugin.Run)
210}
211
212func (m *Manager) equalPluginRun(aPlugin, bPlugin []PluginRun) bool {
213 if len(aPlugin) == len(bPlugin) {
214 for i, w := range aPlugin {
215 isEqual := w.Path == bPlugin[i].Path &&
216 slices.Equal(w.Branch, bPlugin[i].Branch) &&
217 slices.Equal(w.When, bPlugin[i].When) &&
218 m.equalPluginWrite(w.Write, bPlugin[i].Write) &&
219 reflect.DeepEqual(w.Configuration, bPlugin[i].Configuration)
220 if !isEqual {
221 return false
222 }
223 }
224 return true
225 }
226 return false
227}
228
229func (m *Manager) equalPluginWrite(aPlugin, bPlugin pluginLib.PluginWrite) bool {
230 if len(aPlugin.Git) == len(bPlugin.Git) {
231 for i, w := range aPlugin.Git {
232 if w.Path != bPlugin.Git[i].Path || slices.Equal(w.Can, bPlugin.Git[i].Can) {
233 return false
234 }
235 }
236 return true
237 }
238 return false
239}