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"
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", 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 return err
68 }
69 c.logger.Debug("init plugin", logger.NewLoggerPair("name", c.plugin.Name), logger.NewLoggerPair("arg", arg))
70 if err := writeMemoryAndCall(ctx, c.module, init, malloc, free, c.repo.Name(), "false", string(arg)); err != nil {
71 return err
72 }
73 }
74
75 for _, com := range cmd.commits {
76 if startCommit != nil {
77 if err := writeMemoryAndCall(ctx, c.module, startCommit, malloc, free, cmd.branch.Short(), com.hash.String(), com.message, com.date.Format(time.RFC3339)); err != nil {
78 return err
79 }
80 }
81
82 for _, f := range com.files {
83 if f.action == commitForDiffActionAdd && callOnAdd {
84 c.logger.Debug("add", logger.NewLoggerPair("confPath", pluginRun.Path), logger.NewLoggerPair("currentPath", f.path))
85 if pluginRun.glob.Match(f.path) {
86 // creation
87 writeMemoryAndCall(ctx, c.module, addFile, malloc, free, f.path)
88 }
89 } else {
90 if pluginRun.glob.Match(f.path) {
91 if f.action == commitForDiffActionDel && callOnDel {
92 // deletion
93 writeMemoryAndCall(ctx, c.module, delFile, malloc, free, f.path)
94 } else if f.action == commitForDiffActionMod && callOnMod {
95 //modification
96 writeMemoryAndCall(ctx, c.module, modFile, malloc, free, f.oldPath, f.path)
97 }
98 }
99 }
100 }
101
102 if err := writeMemoryAndCall(ctx, c.module, endCommit, malloc, free, cmd.branch.Short(), com.hash.String(), com.message); err != nil {
103 return err
104 }
105 }
106
107 if finish := c.module.ExportedFunction("finish"); finish != nil {
108 if _, err := finish.Call(ctx); err != nil {
109 return err
110 }
111 }
112 }
113 }
114
115 if err := c.repoWriter.Checkout(currentBranch); err != nil {
116 c.logger.Error("can't Checkout", err, logger.NewLoggerPair("branch", currentBranch.Short()))
117 return err
118 }
119 return nil
120}
121
122func writeMemoryAndCall(ctx context.Context, module api.Module, toCall api.Function, malloc api.Function, free api.Function, message ...string) error {
123 params := make([]uint64, 0)
124 for _, m := range message {
125 size := uint64(len(m))
126
127 results, err := malloc.Call(ctx, size)
128 if err != nil {
129 return oops.Wrapf(err, "can't malloc memory for %s with %s", toCall.Definition().Name(), m)
130 }
131 ptr := results[0]
132
133 // The pointer is a linear memory offset, which is where we write the name.
134 if !module.Memory().Write(uint32(ptr), []byte(m)) {
135 return oops.Wrapf(err, "can't write memory")
136 }
137
138 params = append(params, ptr, size)
139 }
140
141 defer func() {
142 for i, d := range params {
143 if i%2 == 0 {
144 free.Call(ctx, d)
145 }
146 }
147 }()
148
149 if _, err := toCall.Call(ctx, params...); err != nil {
150 return err
151 }
152
153 return nil
154}