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}