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 "slices"
10 "time"
11
12 "github.com/samber/oops"
13 "github.com/tetratelabs/wazero/api"
14 pluginLib "gitroot.dev/libs/golang/plugin/model"
15 "gitroot.dev/server/logger"
16 "gitroot.dev/server/repository"
17)
18
19type callPlugin struct {
20 manager *Manager
21 plugin Plugin
22 repo *repository.GitRootRepository
23 repoWriter *repository.GitRootRepositoryWrite
24 module api.Module
25 logger *logger.Logger
26}
27
28func (c callPlugin) callPluginForDiff(ctx context.Context, r *runtime, commands []CommandForDiff) error {
29 startCommit := c.module.ExportedFunction("startCommit")
30 addFile := c.module.ExportedFunction("addFile")
31 modFile := c.module.ExportedFunction("modFile")
32 delFile := c.module.ExportedFunction("delFile")
33 endCommit := c.module.ExportedFunction("endCommit")
34 malloc := c.module.ExportedFunction("malloc")
35 free := c.module.ExportedFunction("free")
36
37 currentBranch, err := c.repo.CurrentBranch()
38 if err != nil {
39 return oops.Wrapf(err, "can't get CurrentBranch")
40 }
41
42 for _, cmd := range commands {
43 r.command = &cmd
44 for _, pluginRun := range r.plugin.Run {
45 r.pluginRun = pluginRun
46 callOnAdd := addFile != nil && slices.Contains(pluginRun.When, pluginLib.PluginRunWhenAdd)
47 callOnMod := modFile != nil && slices.Contains(pluginRun.When, pluginLib.PluginRunWhenMod)
48 callOnDel := delFile != nil && slices.Contains(pluginRun.When, pluginLib.PluginRunWhenDel)
49 isAuthorized := checkBranch(pluginRun, cmd.branch)
50 if !isAuthorized {
51 continue
52 }
53
54 if cmd.branchAction == commitForDiffActionDel {
55 c.logger.Info("delete branch", logger.NewLoggerPair("branch", cmd.branch))
56 continue
57 }
58
59 if err := c.repoWriter.Checkout(cmd.branch); err != nil {
60 c.logger.Error("can't Checkout from cmd", err, logger.NewLoggerPair("branch", cmd.branch))
61 continue
62 }
63
64 if init := c.module.ExportedFunction("init"); init != nil {
65 arg, err := pluginRun.Marshal()
66 if err != nil {
67 c.logger.Error("can't Marshal pluginRun", err, logger.NewLoggerPair("branch", cmd.branch))
68 continue
69 }
70 c.logger.Debug("init plugin", logger.NewLoggerPair("name", c.plugin.Name), logger.NewLoggerPair("arg", arg))
71 if err := writeMemoryAndCall(ctx, c.module, init, malloc, free, c.repo.Name(), "false", string(arg)); err != nil {
72 c.logger.Error("can't init plugin", err, logger.NewLoggerPair("branch", cmd.branch), logger.NewLoggerPair("plugin", c.plugin.Name))
73 continue
74 }
75 }
76
77 for _, com := range cmd.commits {
78 comMarshalled, err := MarshallOne(cmd.branch.Short(), com)
79 if err != nil {
80 c.logger.Error("can't marshall commit", err, logger.NewLoggerPair("branch", cmd.branch))
81 continue
82 }
83 if startCommit != nil {
84 c.logger.Debug("startCommit", logger.NewLoggerPair("branch", cmd.branch.Short()), logger.NewLoggerPair("hash", com.hash.String()), logger.NewLoggerPair("message", com.message), logger.NewLoggerPair("date", com.date.Format(time.RFC3339)))
85 if err := writeMemoryAndCall(ctx, c.module, startCommit, malloc, free, comMarshalled); err != nil {
86 c.logger.Error("startCommit error", err, logger.NewLoggerPair("branch", cmd.branch), logger.NewLoggerPair("plugin", c.plugin.Name))
87 continue
88 }
89 }
90
91 for _, f := range com.files {
92 if f.action == commitForDiffActionAdd && callOnAdd {
93 c.logger.Debug("add", logger.NewLoggerPair("confPath", pluginRun.Path), logger.NewLoggerPair("currentPath", f.path))
94 if pluginRun.glob.Match(f.path) {
95 // creation
96 writeMemoryAndCall(ctx, c.module, addFile, malloc, free, f.path)
97 }
98 } else {
99 if pluginRun.glob.Match(f.path) {
100 if f.action == commitForDiffActionDel && callOnDel {
101 // deletion
102 writeMemoryAndCall(ctx, c.module, delFile, malloc, free, f.path)
103 } else if f.action == commitForDiffActionMod && callOnMod {
104 //modification
105 writeMemoryAndCall(ctx, c.module, modFile, malloc, free, f.oldPath, f.path)
106 }
107 }
108 }
109 }
110
111 if endCommit != nil {
112 if err := writeMemoryAndCall(ctx, c.module, endCommit, malloc, free, comMarshalled); err != nil {
113 c.logger.Error("endCommit error", err, logger.NewLoggerPair("branch", cmd.branch), logger.NewLoggerPair("plugin", c.plugin.Name))
114 continue
115 }
116 }
117 }
118
119 if finish := c.module.ExportedFunction("finish"); finish != nil {
120 if _, err := finish.Call(ctx); err != nil {
121 c.logger.Error("finish error", err, logger.NewLoggerPair("branch", cmd.branch), logger.NewLoggerPair("plugin", c.plugin.Name))
122 continue
123 }
124 }
125 }
126 }
127
128 if err := c.repoWriter.Checkout(currentBranch); err != nil {
129 c.logger.Error("can't Checkout currentBranch", err, logger.NewLoggerPair("branch", currentBranch.Short()))
130 return err
131 }
132 return nil
133}
134
135func writeMemoryAndCall(ctx context.Context, module api.Module, toCall api.Function, malloc api.Function, free api.Function, message ...string) error {
136 params := make([]uint64, 0)
137 for _, m := range message {
138 size := uint64(len(m))
139
140 results, err := malloc.Call(ctx, size)
141 if err != nil {
142 return oops.Wrapf(err, "can't malloc memory for %s with %s", toCall.Definition().Name(), m)
143 }
144 ptr := results[0]
145
146 // The pointer is a linear memory offset, which is where we write the name.
147 if !module.Memory().Write(uint32(ptr), []byte(m)) {
148 return oops.Wrapf(err, "can't write memory")
149 }
150
151 params = append(params, ptr, size)
152 }
153
154 defer func() {
155 for i, d := range params {
156 if i%2 == 0 {
157 free.Call(ctx, d)
158 }
159 }
160 }()
161
162 if _, err := toCall.Call(ctx, params...); err != nil {
163 return oops.With("method", toCall.Definition().ExportNames()).Wrapf(err, "can't call")
164 }
165
166 return nil
167}