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	"encoding/json"
 10	"fmt"
 11	"io/fs"
 12	"slices"
 13
 14	"github.com/go-git/go-git/v6/plumbing"
 15	"github.com/samber/oops"
 16	"github.com/tetratelabs/wazero/api"
 17	"gitroot.dev/libs/golang/plugin/model"
 18	pluginLib "gitroot.dev/libs/golang/plugin/model"
 19	"gitroot.dev/server/logger"
 20	"gitroot.dev/server/repository"
 21	"gitroot.dev/server/user"
 22)
 23
 24func (r *runtime) worktree(ctx context.Context, repo *repository.GitRootRepository, repoWriter *repository.GitRootRepositoryWrite, plugins []Plugin, cmd CommandForDiff) error {
 25	r.repo = repo
 26	r.repoWriter = repoWriter
 27	r.command = &cmd
 28	r.commit = nil
 29
 30	filepaths := []string{}
 31	fs.WalkDir(repoWriter.ToFs(ctx), ".", func(path string, d fs.DirEntry, err error) error {
 32		if !d.IsDir() {
 33			filepaths = append(filepaths, path)
 34		}
 35		return nil
 36	})
 37	commands, err := repo.GetLastCommits(filepaths)
 38	if err != nil {
 39		return err
 40	}
 41	slices.Reverse(commands)
 42
 43	r.commitHook = func(hash plumbing.Hash) {
 44		command, err := repoWriter.GetLastCommit(hash)
 45		if err != nil {
 46			r.logger.Error("can't GetLastCommit", err)
 47			return
 48		}
 49		commands = append(commands, command)
 50	}
 51	defer func() {
 52		r.repo = nil
 53		r.repoWriter = nil
 54		r.command = nil
 55		r.commitHook = nil
 56		r.commit = nil
 57	}()
 58
 59	groups, err := user.LoadGroup(r.repo, r.command.branch)
 60	if err != nil {
 61		return oops.Wrapf(err, "can't load group")
 62	}
 63
 64	var m api.Module
 65	for _, plugin := range plugins {
 66		r.plugin = plugin
 67		timerStop := r.logger.Time(fmt.Sprintf("Timer %s", plugin.Log()))
 68
 69		r.logger.Debug("start plugin worktree", logger.NewLoggerPair("name", plugin.Log()))
 70		m, _, err = r.loadModule(ctx, plugin)
 71		if err != nil {
 72			r.logger.Error("loadModule for worktree error", err, logger.NewLoggerPair("name", plugin.Log()))
 73			continue
 74		}
 75		l := logger.NewLogger(logger.WASM)
 76		l.Debug("memory before worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Log()))
 77		startCommit := m.ExportedFunction("startCommit")
 78		endCommit := m.ExportedFunction("endCommit")
 79		addFile := m.ExportedFunction("addFile")
 80		malloc := m.ExportedFunction("gitrootAlloc")
 81		if malloc == nil {
 82			malloc = m.ExportedFunction("malloc")
 83		}
 84
 85		for _, pluginRun := range plugin.Run {
 86			isAuthorized := checkBranch(pluginRun, r.command.branch)
 87			if !isAuthorized {
 88				continue
 89			}
 90			r.pluginRun = pluginRun
 91			if init := m.ExportedFunction("init"); init != nil {
 92				arg, err := pluginRun.Marshal()
 93				if err != nil {
 94					r.logger.Error("can't marshall pluginRun", err, logger.NewLoggerPair("name", plugin.Log()))
 95					continue
 96				}
 97				r.logger.Debug("init plugin worktree", logger.NewLoggerPair("name", plugin.Log()), logger.NewLoggerPair("arg", arg))
 98				if err := r.writeMemoryAndCall(m, init, malloc, repo.Name(), string(model.InitKindConfHasChange), string(arg)); err != nil {
 99					r.logger.Error("can't writeMemoryAndCall init", err, logger.NewLoggerPair("name", plugin.Log()))
100					continue
101				}
102			}
103
104			if slices.Contains(pluginRun.When, pluginLib.PluginRunWhenAdd) {
105				for _, lastCommit := range commands {
106					firstCall := true
107					hasBeenCalled := false
108					com := commitToCommitForDiff(lastCommit.Commit, []pluginLib.File{}, groups)
109					r.commit = &com
110					comMarshalled, err := MarshallOne(r.command.branch.Short(), com)
111					if err != nil {
112						r.logger.Error("can't MarshallOne commit", err, logger.NewLoggerPair("name", plugin.Log()))
113						continue
114					}
115					for _, f := range lastCommit.Filepath {
116						if pluginRun.glob.Match(f.Path) {
117							if startCommit != nil && firstCall {
118								firstCall = false
119								if err := r.writeMemoryAndCall(m, startCommit, malloc, comMarshalled); err != nil {
120									r.logger.Error("can't writeMemoryAndCall startCommit", err, logger.NewLoggerPair("name", plugin.Log()))
121									continue
122								}
123							}
124							if addFile != nil {
125								jsonFile, err := json.Marshal(f)
126								if err != nil {
127									r.logger.Error("can marshal file", err, logger.NewLoggerPair("branch", r.command.branch.Short()), logger.NewLoggerPair("plugin", r.plugin.Log()))
128									continue
129								}
130								if err := r.writeMemoryAndCall(m, addFile, malloc, string(jsonFile)); err != nil {
131									r.logger.Error("can't writeMemoryAndCall addFile", err, logger.NewLoggerPair("name", plugin.Log()))
132									continue
133								}
134								hasBeenCalled = true
135							}
136						}
137					}
138					if hasBeenCalled && endCommit != nil {
139						if err := r.writeMemoryAndCall(m, endCommit, malloc, comMarshalled); err != nil {
140							r.logger.Error("can't writeMemoryAndCall endCommit", err, logger.NewLoggerPair("name", plugin.Log()))
141							continue
142						}
143					}
144				}
145			}
146
147			if finish := m.ExportedFunction("finish"); finish != nil {
148				if _, err := finish.Call(ctx); err != nil {
149					r.logger.Error("can't call finish", err, logger.NewLoggerPair("name", plugin.Log()))
150					continue
151				}
152			}
153		}
154
155		l.Debug("memory after worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Log()))
156		r.logger.Debug("finish plugin conf", logger.NewLoggerPair("name", plugin.Log()))
157		timerStop()
158	}
159
160	return nil
161}