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