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				pm.logger.Debug("availables doing start", logger.NewLoggerPair("plugin", cp.Log()))
 60				content, err := os.ReadFile(pm.PathWasm(cp))
 61				if err != nil {
 62					pm.logger.Error("can't read plugin", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("path", pm.PathWasm(cp)))
 63					return
 64				}
 65				compiled, err := pm.runtimes.Compile(ctx, content)
 66				if err != nil {
 67					pm.logger.Error("can't compile plugin", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("path", pm.PathWasm(cp)))
 68					return
 69				}
 70				pm.logger.Debug("availables doing compiled", logger.NewLoggerPair("plugin", cp.Log()))
 71				if len(cp.Run) == 0 {
 72					conf, err := pm.runtimes.Conf(ctx, Plugin{PURL: cp.PURL, compiledModule: compiled})
 73					if err != nil {
 74						pm.logger.Error("can't get default conf", err, logger.NewLoggerPair("plugin", cp.Log()))
 75						return
 76					}
 77					pm.logger.Debug("availables doing conf", logger.NewLoggerPair("plugin", cp.Log()))
 78					run, err := pluginLibToPlugin(conf.DefaultRun)
 79					if err != nil {
 80						pm.logger.Error("can't pluginLibToPlugin", err, logger.NewLoggerPair("plugin", cp.Log()))
 81						return
 82					} else {
 83						cp.Run = run
 84					}
 85					if conf.SdkVersion != "0.5.0" {
 86						pm.logger.Error("bad sdk version need update", err, logger.NewLoggerPair("plugin", cp.Log()), logger.NewLoggerPair("sdkVersion", conf.SdkVersion))
 87						return
 88					}
 89					pm.logger.Debug("availables doing sdkVersion", logger.NewLoggerPair("plugin", cp.Log()))
 90				}
 91				commiter, err := pm.userManager.NewCommiter(cp.NamespaceAndName())
 92				if err != nil {
 93					pm.logger.Error("NewCommiter", err, logger.NewLoggerPair("pluginName", cp.Log()))
 94					return
 95				}
 96				pm.logger.Debug("availables doing commiter", logger.NewLoggerPair("plugin", cp.Log()))
 97				appendLock.Lock()
 98				pm.plugins = append(pm.plugins, Plugin{
 99					PURL:           cp.PURL,
100					Active:         cp.Active,
101					Run:            cp.Run,
102					compiledModule: compiled,
103					commiter:       commiter,
104				})
105				appendLock.Unlock()
106				pm.logger.Info("available plugin", logger.NewLoggerPair("plugin", cp.Log()))
107			}(i, cp)
108		}
109		wg.Wait()
110		timeLoading()
111	}
112	if len(pm.plugins) == 0 {
113		pm.logger.Info("no available plugin", logger.NewLoggerPair("in dir", pm.conf.PathDataPlugin()))
114	}
115	return pm.plugins, nil
116}
117
118func (pm *Manager) usableFromDefaultBranch(ctx context.Context, repoName string) ([]Plugin, error) {
119	pm.logger.Debug("usableFromDefaultBranch before open", logger.NewLoggerPair("repoName", repoName))
120	repo, err := pm.repoManager.Open(logger.AddCaller(ctx, "plugin.usable"), repoName)
121	if err != nil {
122		return nil, oops.Wrapf(err, "can't open repo")
123	}
124	defer repo.Close()
125	pm.logger.Debug("usableFromDefaultBranch before ContentPluginsConf", logger.NewLoggerPair("repoName", repoName))
126	filecontent, err := repo.ContentPluginsConf()
127	if err != nil {
128		return nil, oops.Wrapf(err, "can't read plugins from repo")
129	}
130	confPlugins, err := ParsePlugins(filecontent, true)
131	if err != nil {
132		return nil, oops.Wrapf(err, "can't parse plugins from repo")
133	}
134	if len(confPlugins) == 0 {
135		pm.logger.Debug("no usable plugin in conf")
136	}
137	pm.logger.Debug("usableFromDefaultBranch before Availables", logger.NewLoggerPair("repoName", repoName))
138	plugins, err := pm.Availables(ctx)
139	if err != nil {
140		return nil, oops.Wrapf(err, "can't get availables plugin from forgerepo")
141	}
142	pm.logger.Debug("availables plugins", logger.NewLoggerPairNamedSlice("names", plugins))
143	pm.logger.Debug("project plugins", logger.NewLoggerPairNamedSlice("names", confPlugins))
144	usablePlugins := make([]Plugin, 0)
145	for _, cp := range confPlugins {
146		for _, p := range plugins {
147			if p.Equal(cp) {
148				if cp.Active {
149					goodP := p.OverrideWith(cp).SetActive(cp.Active).SetRun(cp.Run)
150					usablePlugins = append(usablePlugins, goodP)
151					pm.logger.Debug("usable plugin", logger.NewLoggerPair("name", cp.Log()))
152				}
153				break
154			}
155		}
156	}
157	return usablePlugins, nil
158}