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	"fmt"
 10	"path/filepath"
 11	"sync"
 12
 13	"github.com/samber/oops"
 14	"github.com/tetratelabs/wazero"
 15	"github.com/tetratelabs/wazero/api"
 16	"github.com/tetratelabs/wazero/experimental"
 17	pluginLib "gitroot.dev/libs/golang/plugin/model"
 18	"gitroot.dev/server/logger"
 19)
 20
 21type runtimes struct {
 22	lockRuntime sync.Mutex
 23	manager     *Manager
 24	runtime     *runtime
 25	log         *logger.Logger
 26	start       chan runtimeInputs
 27}
 28
 29func newRuntimes(manager *Manager, log *logger.Logger) *runtimes {
 30	chanStart := make(chan runtimeInputs)
 31	compileCache, _ := wazero.NewCompilationCacheWithDir(filepath.Join(manager.conf.PathCache(), "wazero"))
 32	runtimeConfig := wazero.NewRuntimeConfig().WithCompilationCache(compileCache).WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesThreads)
 33	r, err := newRuntime(context.Background(), manager, log, runtimeConfig)
 34	if err != nil {
 35		log.Error("can't create runtime", err)
 36	}
 37	go func() {
 38		r.listen(chanStart)
 39	}()
 40	return &runtimes{
 41		manager: manager,
 42		runtime: r,
 43		log:     log,
 44		start:   chanStart,
 45	}
 46}
 47
 48func (r *runtimes) Compile(ctx context.Context, binary []byte) (wazero.CompiledModule, error) {
 49	r.lockRuntime.Lock()
 50	defer r.lockRuntime.Unlock()
 51	return r.runtime.wazRun.CompileModule(ctx, binary)
 52}
 53
 54func (r *runtimes) Start(ctx context.Context, repoName string, plugins []Plugin, kind runtimeInputsKind, commands []CommandForDiff) {
 55	r.lockRuntime.Lock()
 56	wg := sync.WaitGroup{}
 57	for _, command := range commands {
 58		wg.Add(1)
 59		r.start <- runtimeInputs{
 60			ctx:      ctx,
 61			repoName: repoName,
 62			kind:     kind,
 63			plugins:  plugins,
 64			command:  command,
 65			close: func() {
 66				wg.Done()
 67			},
 68		}
 69	}
 70	wg.Wait()
 71	r.lockRuntime.Unlock()
 72}
 73
 74func (r *runtimes) Conf(ctx context.Context, plugin Plugin) (*pluginLib.ConfPlugin, error) {
 75	timerStop := r.log.Time(fmt.Sprintf("Timer conf plugin %s", plugin.Log()))
 76	defer timerStop()
 77	r.lockRuntime.Lock()
 78	defer r.lockRuntime.Unlock()
 79	return r.runtime.conf(ctx, plugin)
 80}
 81
 82func (r *runtimes) Schedule(ctx context.Context, repoName string, pluginPurl string, pluginRun *PluginRun) error {
 83	r.log.Debug("start schedule", logger.NewLoggerPair("repo", repoName), logger.NewLoggerPair("plugin", pluginPurl))
 84	plugins, err := r.manager.usableFromDefaultBranch(ctx, repoName)
 85	if err != nil {
 86		return err
 87	}
 88	var plugin Plugin
 89	found := false
 90	for _, p := range plugins {
 91		if p.PURL.String() == pluginPurl {
 92			plugin = p
 93			found = true
 94			break
 95		}
 96	}
 97	if !found {
 98		return oops.With("repo", repoName, "pluginPurl", pluginPurl).Errorf("plugin not found")
 99	}
100	timerStop := r.log.Time(fmt.Sprintf("Timer schedule plugin %s", plugin.Log()))
101	defer timerStop()
102	r.lockRuntime.Lock()
103	defer r.lockRuntime.Unlock()
104	return r.runtime.schedule(ctx, repoName, plugin, *pluginRun)
105}