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}