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/v5/plumbing"
 15	"github.com/samber/oops"
 16	pluginLib "gitroot.dev/libs/golang/plugin/model"
 17	grfs "gitroot.dev/server/fs"
 18	"gitroot.dev/server/logger"
 19	"gitroot.dev/server/repository"
 20	"gitroot.dev/server/user"
 21)
 22
 23func (r *runtime) worktree(ctx context.Context, repo *repository.GitRootRepository, repoWriter *repository.GitRootRepositoryWrite, plugins []Plugin, cmds []CommandForDiff) error {
 24	r.repo = repo
 25	r.repoWriter = repoWriter
 26	r.command = &cmds[len(cmds)-1] // TODO should not be last cmd see ./manager.go line 65 todo
 27	r.commit = nil
 28
 29	filepaths := []string{}
 30	fs.WalkDir(repoWriter.ToFs(ctx), ".", func(path string, d fs.DirEntry, err error) error {
 31		if !d.IsDir() {
 32			filepaths = append(filepaths, path)
 33		}
 34		return nil
 35	})
 36	commands, err := repo.GetLastCommits(filepaths)
 37	if err != nil {
 38		return err
 39	}
 40	slices.Reverse(commands)
 41
 42	r.commitHook = func(hash plumbing.Hash) {
 43		command, err := repoWriter.GetLastCommit(hash)
 44		if err != nil {
 45			r.logger.Error("can't GetLastCommit", err)
 46			return
 47		}
 48		commands = append(commands, command)
 49	}
 50	defer func() {
 51		r.commitHook = nil
 52	}()
 53
 54	groups, err := user.LoadGroup(r.repo)
 55	if err != nil {
 56		return oops.Wrapf(err, "can't load group")
 57	}
 58
 59	fs := grfs.NewMultiple(ctx, map[string]fs.FS{
 60		"worktree":   r.repoWriter.ToFs(ctx),
 61		"webcontent": r.manager.conf.DataWeb(r.repo.Name()),
 62	})
 63
 64	for _, plugin := range plugins {
 65		fs.UpdateSubFs("cache", r.manager.conf.Cache(r.repo.Name(), plugin.Name))
 66		r.plugin = plugin
 67		timerStop := r.logger.Time(fmt.Sprintf("Timer %s", plugin.Name))
 68
 69		r.logger.Debug("start plugin worktree", logger.NewLoggerPair("name", plugin.Name))
 70		m, err := r.loadModule(ctx, plugin, fs)
 71		if err != nil {
 72			r.logger.Error("loadModule for worktree error", err, logger.NewLoggerPair("name", plugin.Name))
 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.Name))
 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.Name))
 95					continue
 96				}
 97				r.logger.Debug("init plugin worktree", logger.NewLoggerPair("name", plugin.Name), logger.NewLoggerPair("arg", arg))
 98				if err := r.writeMemoryAndCall(m, init, malloc, repo.Name(), "true", string(arg)); err != nil {
 99					r.logger.Error("can't writeMemoryAndCall init", err, logger.NewLoggerPair("name", plugin.Name))
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.Name))
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.Name))
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.Name))
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.Name))
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.Name))
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.Name))
150					continue
151				}
152			}
153		}
154
155		l.Debug("memory after worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Name))
156		r.logger.Debug("finish plugin conf", logger.NewLoggerPair("name", plugin.Name))
157		timerStop()
158	}
159
160	return nil
161}