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 "sync"
12
13 "github.com/go-git/go-git/v6/plumbing/object"
14 "github.com/samber/oops"
15 "gitroot.dev/server/logger"
16)
17
18func (pm *Manager) reloadPlugins() {
19 pm.pluginsLock.Lock()
20 pm.plugins = nil
21 pm.pluginsLock.Unlock()
22 pm.Availables(context.Background()) //TODO pass good context
23}
24
25func (pm *Manager) Availables(ctx context.Context) ([]Plugin, error) {
26 pm.pluginsLock.Lock()
27 defer pm.pluginsLock.Unlock()
28 needLoad := pm.plugins == nil
29 if needLoad {
30 pm.logger.Info("availables needLoad")
31 errorHandler := oops.With("rootData", pm.conf.PathDataPlugin())
32 forgeRepo, err := pm.repoManager.OpenForgeRepo(logger.AddCaller(ctx, "plugin.Availables"))
33 defer func() {
34 if err := forgeRepo.Close(); err != nil {
35 pm.logger.Error("forgeRepo.Close", err)
36 }
37 }()
38 if err != nil {
39 return nil, errorHandler.Wrapf(err, "can't open forgeRepo")
40 }
41 filecontent, err := forgeRepo.ContentPluginsConf()
42 if errors.Is(err, object.ErrFileNotFound) {
43 return []Plugin{}, nil
44 } else if err != nil {
45 return nil, oops.Wrapf(err, "can't read plugins filecontent from repo")
46 }
47 ps, err := ParsePlugins(filecontent, false)
48 if err != nil {
49 return nil, oops.Wrapf(err, "can't parse plugins from repo")
50 }
51 pm.plugins = make([]Plugin, 0)
52 timeLoading := pm.logger.Time("loading wasm files", logger.NewLoggerPairNamedSlice("plugins", ps))
53 wg := sync.WaitGroup{}
54 appendLock := sync.Mutex{}
55 for i, cp := range ps {
56 wg.Add(1)
57 go func(i int, cp Plugin) {
58 defer wg.Done()
59 content, err := os.ReadFile(pm.PathWasm(cp))
60 if err != nil {
61 pm.logger.Error("can't read plugin", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("path", pm.PathWasm(cp)))
62 return
63 }
64 compiled, err := pm.runtimes.Compile(ctx, content)
65 if err != nil {
66 pm.logger.Error("can't compile plugin", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("path", pm.PathWasm(cp)))
67 return
68 }
69 if len(cp.Run) == 0 {
70 conf, err := pm.runtimes.Conf(ctx, Plugin{PURL: cp.PURL, compiledModule: compiled})
71 if err != nil {
72 pm.logger.Error("can't get default conf", err, logger.NewLoggerPair("plugin", cp.Log()))
73 return
74 }
75 run, err := pluginLibToPlugin(conf.DefaultRun)
76 if err != nil {
77 pm.logger.Error("can't pluginLibToPlugin", err, logger.NewLoggerPair("plugin", cp.Log()))
78 return
79 } else {
80 cp.Run = run
81 }
82 if conf.SdkVersion != "0.5.0" {
83 pm.logger.Error("bad sdk version need update", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("sdkVersion", conf.SdkVersion))
84 return
85 }
86 }
87 commiter, err := pm.userManager.NewCommiter(cp.NamespaceAndName())
88 if err != nil {
89 pm.logger.Error("NewCommiter", err, logger.NewLoggerPair("pluginName", cp.Log()))
90 return
91 }
92 appendLock.Lock()
93 pm.plugins = append(pm.plugins, Plugin{
94 PURL: cp.PURL,
95 Active: cp.Active,
96 Run: cp.Run,
97 compiledModule: compiled,
98 commiter: commiter,
99 })
100 appendLock.Unlock()
101 pm.logger.Info("available plugin", logger.NewLoggerPair("plugin", cp.Log()))
102 }(i, cp)
103 }
104 wg.Wait()
105 timeLoading()
106 }
107 if len(pm.plugins) == 0 {
108 pm.logger.Info("no available plugin", logger.NewLoggerPair("in dir", pm.conf.PathDataPlugin()))
109 }
110 return pm.plugins, nil
111}
112
113func (pm *Manager) usableFromDefaultBranch(ctx context.Context, repoName string) ([]Plugin, error) {
114 pm.logger.Debug("usableFromDefaultBranch before open", logger.NewLoggerPair("repoName", repoName))
115 repo, err := pm.repoManager.Open(logger.AddCaller(ctx, "plugin.usable"), repoName)
116 if err != nil {
117 return nil, oops.Wrapf(err, "can't open repo")
118 }
119 defer repo.Close()
120 pm.logger.Debug("usableFromDefaultBranch before ContentPluginsConf", logger.NewLoggerPair("repoName", repoName))
121 filecontent, err := repo.ContentPluginsConf()
122 if err != nil {
123 return nil, oops.Wrapf(err, "can't read plugins from repo")
124 }
125 confPlugins, err := ParsePlugins(filecontent, true)
126 if err != nil {
127 return nil, oops.Wrapf(err, "can't parse plugins from repo")
128 }
129 if len(confPlugins) == 0 {
130 pm.logger.Debug("no usable plugin in conf")
131 }
132 pm.logger.Debug("usableFromDefaultBranch before Availables", logger.NewLoggerPair("repoName", repoName))
133 plugins, err := pm.Availables(ctx)
134 if err != nil {
135 return nil, oops.Wrapf(err, "can't get availables plugin from forgerepo")
136 }
137 pm.logger.Debug("availables plugins", logger.NewLoggerPairNamedSlice("names", plugins))
138 pm.logger.Debug("project plugins", logger.NewLoggerPairNamedSlice("names", confPlugins))
139 usablePlugins := make([]Plugin, 0)
140 for _, cp := range confPlugins {
141 for _, p := range plugins {
142 if p.Equal(cp) {
143 if cp.Active {
144 goodP := p.OverrideWith(cp).SetActive(cp.Active).SetRun(cp.Run)
145 usablePlugins = append(usablePlugins, goodP)
146 pm.logger.Debug("usable plugin", logger.NewLoggerPair("name", cp.Log()))
147 }
148 break
149 }
150 }
151 }
152 return usablePlugins, nil
153}