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 "context"
9 "errors"
10 "os"
11
12 "github.com/go-git/go-git/v5/plumbing/object"
13 "github.com/samber/oops"
14 "gitroot.dev/server/logger"
15)
16
17func (pm *Manager) reloadPlugins() {
18 pm.pluginsLock.Lock()
19 pm.plugins = nil
20 pm.pluginsLock.Unlock()
21}
22
23func (pm *Manager) Availables(ctx context.Context) ([]Plugin, error) {
24 pm.pluginsLock.RLock()
25 needLoad := pm.plugins == nil
26 pm.pluginsLock.RUnlock()
27 if needLoad {
28 errorHandler := oops.With("rootData", pm.conf.PathDataPlugin())
29 forgeRepo, err := pm.repoManager.OpenForgeRepo(logger.AddCaller(ctx, "plugin.Availables"))
30 defer forgeRepo.Close()
31 if err != nil {
32 return nil, errorHandler.Wrapf(err, "can't open forgeRepo")
33 }
34 filecontent, err := forgeRepo.ContentPluginsConf()
35 if errors.Is(err, object.ErrFileNotFound) {
36 return []Plugin{}, nil
37 } else if err != nil {
38 return nil, oops.Wrapf(err, "can't read plugins filecontent from repo")
39 }
40 ps, err := ParsePlugins(filecontent, false)
41 if err != nil {
42 return nil, oops.Wrapf(err, "can't parse plugins from repo")
43 }
44 pm.pluginsLock.Lock()
45 pm.plugins = make([]Plugin, len(ps))
46 for i, cp := range ps {
47 content, err := os.ReadFile(pm.PathWasm(cp))
48 if err != nil {
49 return nil, errorHandler.With("filename", cp.Name).Wrapf(err, "can't read plugin")
50 }
51 if cp.Name == "" {
52 cp = cp.DetermineNameFromUrl()
53 }
54 if len(cp.Run) == 0 {
55 conf, err := pm.runtimes.Conf(ctx, Plugin{Name: cp.Name, content: content})
56 if err != nil {
57 pm.logger.Error("can't get default conf", err, logger.NewLoggerPair("filename", cp.Name))
58 }
59 run, err := pluginLibToPlugin(conf)
60 if err != nil {
61 pm.logger.Error("can't pluginLibToPlugin", err, logger.NewLoggerPair("filename", cp.Name))
62 } else {
63 cp.Run = run
64 }
65 }
66 commiter, err := pm.userManager.NewCommiter(cp.Name)
67 if err != nil {
68 pm.logger.Error("NewCommiter", err, logger.NewLoggerPair("pluginName", cp.Name))
69 }
70
71 pm.plugins[i] = Plugin{
72 Url: cp.Url,
73 Crc: cp.Crc,
74 Active: cp.Active,
75 Name: cp.Name,
76 Run: cp.Run,
77 content: content,
78 commiter: commiter,
79 }
80 pm.logger.Info("available plugin", logger.NewLoggerPair("name", cp.Name))
81 }
82 pm.pluginsLock.Unlock()
83 }
84 pm.pluginsLock.RLock()
85 defer pm.pluginsLock.RUnlock()
86 if len(pm.plugins) == 0 {
87 pm.logger.Info("no available plugin", logger.NewLoggerPair("in dir", pm.conf.PathDataPlugin()))
88 }
89 return pm.plugins, nil
90}
91
92func (pm *Manager) usable(ctx context.Context, repoName string) ([]Plugin, error) {
93 repo, err := pm.repoManager.Open(logger.AddCaller(ctx, "plugin.usable"), repoName)
94 if err != nil {
95 return nil, oops.Wrapf(err, "can't open repo")
96 }
97 defer repo.Close()
98 filecontent, err := repo.ContentPluginsConf()
99 if err != nil {
100 return nil, oops.Wrapf(err, "can't read plugins from repo")
101 }
102 confPlugins, err := ParsePlugins(filecontent, true)
103 if err != nil {
104 return nil, oops.Wrapf(err, "can't parse plugins from repo")
105 }
106 if len(confPlugins) == 0 {
107 pm.logger.Debug("no usable plugin in conf")
108 }
109 plugins, err := pm.Availables(ctx)
110 if err != nil {
111 return nil, oops.Wrapf(err, "can't get availables plugin from forgerepo")
112 }
113 usablePlugins := make([]Plugin, 0)
114 for _, p := range plugins {
115 for _, cp := range confPlugins {
116 if p.Name == cp.Name {
117 if cp.Active {
118 goodP := Plugin{ //TODO factorise plugin creation/copy...
119 Url: p.Url,
120 Crc: p.Crc,
121 Name: p.Name,
122 Active: cp.Active,
123 Run: cp.Run,
124 content: p.content,
125 commiter: p.commiter,
126 }
127 usablePlugins = append(usablePlugins, goodP)
128 pm.logger.Info("usable plugin", logger.NewLoggerPair("name", cp.Name))
129 }
130 break
131 }
132 }
133 }
134 if len(usablePlugins) == 0 {
135 pluginsLog := make([]Plugin, len(plugins))
136 for i, p := range plugins {
137 pluginsLog[i] = Plugin{Name: p.Name, Active: p.Active}
138 }
139 confPluginsLog := make([]Plugin, len(confPlugins))
140 for i, p := range confPlugins {
141 confPluginsLog[i] = Plugin{Name: p.Name, Active: p.Active}
142 }
143 pm.logger.Info("no usable plugin", logger.NewLoggerPair("availables", pluginsLog), logger.NewLoggerPair("conf", confPluginsLog))
144 }
145 return usablePlugins, nil
146}