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 "fmt"
10 "io/fs"
11 "slices"
12 "strings"
13
14 "github.com/go-git/go-git/v5/plumbing"
15 pluginLib "gitroot.dev/libs/golang/plugin/model"
16 grfs "gitroot.dev/server/fs"
17 "gitroot.dev/server/logger"
18 "gitroot.dev/server/repository"
19)
20
21func (r *runtime) worktree(ctx context.Context, repo *repository.GitRootRepository, repoWriter *repository.GitRootRepositoryWrite, plugins []Plugin, cmds []CommandForDiff) error {
22 r.repo = repo
23 r.repoWriter = repoWriter
24 r.command = &cmds[len(cmds)-1] // TODO should not be last cmd see ./manager.go line 65 todo
25 r.commit = nil
26
27 filepaths := []string{}
28 fs.WalkDir(repoWriter.ToFs(ctx), ".", func(path string, d fs.DirEntry, err error) error {
29 if !d.IsDir() {
30 filepaths = append(filepaths, path)
31 }
32 return nil
33 })
34 commands, err := repo.GetLastCommits(filepaths)
35 if err != nil {
36 return err
37 }
38 slices.Reverse(commands)
39
40 r.commitHook = func(hash plumbing.Hash) {
41 command, err := repoWriter.GetLastCommit(hash)
42 if err != nil {
43 r.logger.Error("can't GetLastCommit", err)
44 return
45 }
46 commands = append(commands, command)
47 }
48 defer func() {
49 r.commitHook = nil
50 }()
51
52 fs := grfs.NewMultiple(ctx, map[string]fs.FS{
53 "worktree": r.repoWriter.ToFs(ctx),
54 "webcontent": r.manager.conf.DataWeb(r.repo.Name()),
55 })
56
57 for _, plugin := range plugins {
58 fs.UpdateSubFs("cache", r.manager.conf.Cache(r.repo.Name(), plugin.Name))
59 r.plugin = plugin
60 timerStop := r.logger.Time(fmt.Sprintf("Timer %s", plugin.Name))
61
62 r.logger.Debug("start plugin worktree", logger.NewLoggerPair("name", plugin.Name))
63 m, err := r.loadModule(ctx, plugin, fs)
64 if err != nil {
65 r.logger.Error("loadModule for worktree error", err, logger.NewLoggerPair("name", plugin.Name))
66 continue
67 }
68 l := logger.NewLogger(logger.WASM)
69 l.Debug("memory before worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Name))
70 startCommit := m.ExportedFunction("startCommit")
71 endCommit := m.ExportedFunction("endCommit")
72 addFile := m.ExportedFunction("addFile")
73 malloc := m.ExportedFunction("gitrootAlloc")
74 if malloc == nil {
75 malloc = m.ExportedFunction("malloc")
76 }
77
78 for _, pluginRun := range plugin.Run {
79 isAuthorized := checkBranch(pluginRun, r.command.branch)
80 if !isAuthorized {
81 continue
82 }
83 r.pluginRun = pluginRun
84 if init := m.ExportedFunction("init"); init != nil {
85 arg, err := pluginRun.Marshal()
86 if err != nil {
87 r.logger.Error("can't marshall pluginRun", err, logger.NewLoggerPair("name", plugin.Name))
88 continue
89 }
90 r.logger.Debug("init plugin worktree", logger.NewLoggerPair("name", plugin.Name), logger.NewLoggerPair("arg", arg))
91 if err := r.writeMemoryAndCall(m, init, malloc, repo.Name(), "true", string(arg)); err != nil {
92 r.logger.Error("can't writeMemoryAndCall init", err, logger.NewLoggerPair("name", plugin.Name))
93 continue
94 }
95 }
96
97 if slices.Contains(pluginRun.When, pluginLib.PluginRunWhenAdd) {
98 for _, lastCommit := range commands {
99 firstCall := true
100 hasBeenCalled := false
101 com := commitToCommitForDiff(lastCommit.Commit, []commitForDiffFile{})
102 r.commit = &com
103 comMarshalled, err := MarshallOne(r.command.branch.Short(), com)
104 if err != nil {
105 r.logger.Error("can't MarshallOne commit", err, logger.NewLoggerPair("name", plugin.Name))
106 continue
107 }
108 for _, path := range lastCommit.Filepath {
109 if pluginRun.glob.Match(path) {
110 if startCommit != nil && firstCall {
111 firstCall = false
112 if err := r.writeMemoryAndCall(m, startCommit, malloc, comMarshalled); err != nil {
113 r.logger.Error("can't writeMemoryAndCall startCommit", err, logger.NewLoggerPair("name", plugin.Name))
114 continue
115 }
116 }
117 if addFile != nil {
118 if err := r.writeMemoryAndCall(m, addFile, malloc, strings.TrimPrefix(path, "/")); err != nil {
119 r.logger.Error("can't writeMemoryAndCall addFile", err, logger.NewLoggerPair("name", plugin.Name))
120 continue
121 }
122 hasBeenCalled = true
123 }
124 }
125 }
126 if hasBeenCalled && endCommit != nil {
127 if err := r.writeMemoryAndCall(m, endCommit, malloc, comMarshalled); err != nil {
128 r.logger.Error("can't writeMemoryAndCall endCommit", err, logger.NewLoggerPair("name", plugin.Name))
129 continue
130 }
131 }
132 }
133 }
134
135 if finish := m.ExportedFunction("finish"); finish != nil {
136 if _, err := finish.Call(ctx); err != nil {
137 r.logger.Error("can't call finish", err, logger.NewLoggerPair("name", plugin.Name))
138 continue
139 }
140 }
141 }
142
143 l.Debug("memory after worktree", logger.NewLoggerPair("size", m.Memory().Size()), logger.NewLoggerPair("plugin", plugin.Name))
144 r.logger.Debug("finish plugin conf", logger.NewLoggerPair("name", plugin.Name))
145 r.garbageMemory(m)
146 timerStop()
147 }
148
149 return nil
150}