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