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}