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	"gitroot.dev/libs/golang/plugin/model"
 17	pluginLib "gitroot.dev/libs/golang/plugin/model"
 18	grfs "gitroot.dev/server/fs"
 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)
 60	if err != nil {
 61		return oops.Wrapf(err, "can't load group")
 62	}
 63
 64	fs := grfs.NewMultiple(ctx, map[string]fs.FS{
 65		"worktree":   r.repoWriter.ToFs(ctx),
 66		"webcontent": r.manager.conf.DataWeb(r.repo.Name()),
 67	})
 68
 69	for _, plugin := range plugins {
 70		fs.UpdateSubFs("cache", r.manager.conf.Cache(r.repo.Name(), plugin.NamespaceAndName()))
 71		r.plugin = plugin
 72		timerStop := r.logger.Time(fmt.Sprintf("Timer %s", plugin.Log()))
 73
 74		r.logger.Debug("start plugin worktree", logger.NewLoggerPair("name", plugin.Log()))
 75		m, err := r.loadModule(ctx, plugin, fs)
 76		if err != nil {
 77			r.logger.Error("loadModule for worktree error", err, logger.NewLoggerPair("name", plugin.Log()))
 78			continue
 79		}
 80		l := logger.NewLogger(logger.WASM)
 81		l.Debug("memory before worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Log()))
 82		startCommit := m.ExportedFunction("startCommit")
 83		endCommit := m.ExportedFunction("endCommit")
 84		addFile := m.ExportedFunction("addFile")
 85		malloc := m.ExportedFunction("gitrootAlloc")
 86		if malloc == nil {
 87			malloc = m.ExportedFunction("malloc")
 88		}
 89
 90		for _, pluginRun := range plugin.Run {
 91			isAuthorized := checkBranch(pluginRun, r.command.branch)
 92			if !isAuthorized {
 93				continue
 94			}
 95			r.pluginRun = pluginRun
 96			if init := m.ExportedFunction("init"); init != nil {
 97				arg, err := pluginRun.Marshal()
 98				if err != nil {
 99					r.logger.Error("can't marshall pluginRun", err, logger.NewLoggerPair("name", plugin.Log()))
100					continue
101				}
102				r.logger.Debug("init plugin worktree", logger.NewLoggerPair("name", plugin.Log()), logger.NewLoggerPair("arg", arg))
103				if err := r.writeMemoryAndCall(m, init, malloc, repo.Name(), string(model.InitKindConfHasChange), string(arg)); err != nil {
104					r.logger.Error("can't writeMemoryAndCall init", err, logger.NewLoggerPair("name", plugin.Log()))
105					continue
106				}
107			}
108
109			if slices.Contains(pluginRun.When, pluginLib.PluginRunWhenAdd) {
110				for _, lastCommit := range commands {
111					firstCall := true
112					hasBeenCalled := false
113					com := commitToCommitForDiff(lastCommit.Commit, []pluginLib.File{}, groups)
114					r.commit = &com
115					comMarshalled, err := MarshallOne(r.command.branch.Short(), com)
116					if err != nil {
117						r.logger.Error("can't MarshallOne commit", err, logger.NewLoggerPair("name", plugin.Log()))
118						continue
119					}
120					for _, f := range lastCommit.Filepath {
121						if pluginRun.glob.Match(f.Path) {
122							if startCommit != nil && firstCall {
123								firstCall = false
124								if err := r.writeMemoryAndCall(m, startCommit, malloc, comMarshalled); err != nil {
125									r.logger.Error("can't writeMemoryAndCall startCommit", err, logger.NewLoggerPair("name", plugin.Log()))
126									continue
127								}
128							}
129							if addFile != nil {
130								jsonFile, err := json.Marshal(f)
131								if err != nil {
132									r.logger.Error("can marshal file", err, logger.NewLoggerPair("branch", r.command.branch.Short()), logger.NewLoggerPair("plugin", r.plugin.Log()))
133									continue
134								}
135								if err := r.writeMemoryAndCall(m, addFile, malloc, string(jsonFile)); err != nil {
136									r.logger.Error("can't writeMemoryAndCall addFile", err, logger.NewLoggerPair("name", plugin.Log()))
137									continue
138								}
139								hasBeenCalled = true
140							}
141						}
142					}
143					if hasBeenCalled && endCommit != nil {
144						if err := r.writeMemoryAndCall(m, endCommit, malloc, comMarshalled); err != nil {
145							r.logger.Error("can't writeMemoryAndCall endCommit", err, logger.NewLoggerPair("name", plugin.Log()))
146							continue
147						}
148					}
149				}
150			}
151
152			if finish := m.ExportedFunction("finish"); finish != nil {
153				if _, err := finish.Call(ctx); err != nil {
154					r.logger.Error("can't call finish", err, logger.NewLoggerPair("name", plugin.Log()))
155					continue
156				}
157			}
158		}
159
160		l.Debug("memory after worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Log()))
161		r.logger.Debug("finish plugin conf", logger.NewLoggerPair("name", plugin.Log()))
162		timerStop()
163	}
164
165	return nil
166}