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}