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: MIT
4
5package gitroot
6
7// #include <stdlib.h>
8import "C"
9
10import (
11 "encoding/json"
12 "errors"
13 "io/fs"
14 "os"
15 "runtime"
16
17 "github.com/tidwall/gjson"
18 "gitroot.dev/libs/golang/plugin/model"
19)
20
21type PluginFactory = func(server model.Server) model.Plugin
22
23type pServer struct {
24 plugin model.Plugin
25 pluginOpts model.PluginOption
26 run []model.PluginRun
27 atLeastOneChange bool
28 deps []string
29 exportedFuncs map[string]func(args map[string]string) (map[string]string, error)
30}
31
32var server = &pServer{
33 atLeastOneChange: false,
34 pluginOpts: model.PluginOption{},
35 deps: []string{},
36 exportedFuncs: map[string]func(args map[string]string) (map[string]string, error){},
37}
38
39func Register(run []model.PluginRun, p PluginFactory, deps []string) {
40 server.run = run
41 server.plugin = p(server)
42 server.deps = deps
43}
44
45func (s *pServer) PluginOption(opts ...model.PluginOptionWith) {
46 for _, opt := range opts {
47 server.pluginOpts = opt(server.pluginOpts)
48 }
49}
50
51func (s *pServer) ForgeConf() (model.ForgeConf, error) {
52 forgeConf := model.ForgeConf{}
53 ptrSize := _forgeConf()
54 if ptrSize == 0 {
55 return forgeConf, errors.New("can't make diff")
56 }
57 ptrDiff := uint32(ptrSize >> 32)
58 sizeDiff := uint32(ptrSize)
59 forgeConfJson := gjson.Parse(ptrToString(ptrDiff, sizeDiff))
60 return model.ForgeConf{
61 Domain: forgeConfJson.Get("domain").String(),
62 ExternalSshAddr: forgeConfJson.Get("externalSshAddr").String(),
63 ExternalHttpAddr: forgeConfJson.Get("externalHttpAddr").String(),
64 RootRepositoryName: forgeConfJson.Get("rootRepositoryName").String(),
65 }, nil
66}
67
68func (s *pServer) Worktree() fs.FS {
69 return os.DirFS("/worktree")
70}
71
72func (s *pServer) Webcontent() fs.FS {
73 return os.DirFS("/webcontent")
74}
75
76func (s *pServer) Cache() fs.FS {
77 return os.DirFS("/cache")
78}
79
80func (s *pServer) ModifyContent(filepath, content string) {
81 s.atLeastOneChange = true
82 ptr, size := stringToPtr(filepath)
83 ptr2, size2 := stringToPtr(content)
84 _modifyContent(ptr, size, ptr2, size2)
85 runtime.KeepAlive(filepath)
86 runtime.KeepAlive(content)
87}
88
89func (s *pServer) ModifyWebContent(filepath, content string) {
90 ptr, size := stringToPtr(filepath)
91 ptr2, size2 := stringToPtr(content)
92 _modifyWebContent(ptr, size, ptr2, size2)
93 runtime.KeepAlive(filepath)
94 runtime.KeepAlive(content)
95}
96
97func (s *pServer) ReplaceWebContent(filepath, oldContent, content string) {
98 ptr, size := stringToPtr(filepath)
99 ptr2, size2 := stringToPtr(oldContent)
100 ptr3, size3 := stringToPtr(content)
101 _replaceWebContent(ptr, size, ptr2, size2, ptr3, size3)
102 runtime.KeepAlive(filepath)
103 runtime.KeepAlive(oldContent)
104 runtime.KeepAlive(content)
105}
106
107func (s *pServer) ModifyCacheContent(filepath, content string) {
108 ptr, size := stringToPtr(filepath)
109 ptr2, size2 := stringToPtr(content)
110 _modifyCacheContent(ptr, size, ptr2, size2)
111 runtime.KeepAlive(filepath)
112 runtime.KeepAlive(content)
113}
114
115func (s *pServer) CommitAllIfNeeded(message string) {
116 if s.atLeastOneChange {
117 ptr, size := stringToPtr(message)
118 _commitAll(ptr, size)
119 runtime.KeepAlive(message)
120 }
121}
122
123func (s *pServer) DiffWithParent(hash string, oldFilepath string, newFilePath string) (string, error) {
124 ptrHash, sizeHash := stringToPtr(hash)
125 ptrOldFile, sizeOldFile := stringToPtr(oldFilepath)
126 ptrNewFile, sizeNewFile := stringToPtr(newFilePath)
127 ptrSizeDiff := _diffWithParent(ptrHash, sizeHash, ptrOldFile, sizeOldFile, ptrNewFile, sizeNewFile)
128 if ptrSizeDiff == 0 {
129 return "", errors.New("can't make diff")
130 }
131 ptrDiff := uint32(ptrSizeDiff >> 32)
132 sizeDiff := uint32(ptrSizeDiff)
133 return ptrToString(ptrDiff, sizeDiff), nil
134}
135
136func (s *pServer) Log(message string) {
137 ptr, size := stringToPtr(message)
138 _log(ptr, size)
139 runtime.KeepAlive(message)
140}
141
142func (s *pServer) LogError(message string, err error) {
143 ptr, size := stringToPtr(message)
144 errPtr, errSize := stringToPtr(err.Error())
145 _logError(ptr, size, errPtr, errSize)
146 runtime.KeepAlive(message)
147 runtime.KeepAlive(err)
148}
149
150func (s *pServer) Merge(from string, to string) {
151 fromPtr, fromSize := stringToPtr(from)
152 toPtr, toSize := stringToPtr(to)
153 _merge(fromPtr, fromSize, toPtr, toSize)
154 runtime.KeepAlive(from)
155 runtime.KeepAlive(to)
156}
157
158func (s *pServer) Commits(from string, to string) ([]model.Commit, error) {
159 fromPtr, fromSize := stringToPtr(from)
160 toPtr, toSize := stringToPtr(to)
161 ptrSizeDiff := _commits(fromPtr, fromSize, toPtr, toSize)
162 runtime.KeepAlive(from)
163 runtime.KeepAlive(to)
164 if ptrSizeDiff == 0 {
165 return nil, errors.New("can't get commits")
166 }
167 ptrDiff := uint32(ptrSizeDiff >> 32)
168 sizeDiff := uint32(ptrSizeDiff)
169 return model.CommitsFromString(ptrToString(ptrDiff, sizeDiff)), nil
170}
171
172func (s *pServer) Exec(exec model.Exec) (model.ExecStatus, error) {
173 execMarshalled, err := json.Marshal(exec)
174 if err != nil {
175 return model.ExecStatus{}, err
176 }
177 ptr, size := stringToPtr(string(execMarshalled))
178 ptrSize := _exec(ptr, size)
179 if ptrSize == 0 {
180 return model.ExecStatus{}, errors.New("can't exec")
181 }
182 ptrLog := uint32(ptrSize >> 32)
183 sizeLog := uint32(ptrSize)
184 return model.ExecStatusFromString(ptrToString(ptrLog, sizeLog)), nil
185}
186
187func (s *pServer) ReportToPlugin(report model.Report) {
188 if s.pluginOpts.Reporter != nil {
189 if err := s.pluginOpts.Reporter(report); err != nil {
190 s.LogError("plugin reporter err", err)
191 }
192 }
193}
194
195func (s *pServer) Report(level model.ReportLevel, content []string) error {
196 reportMarshalled, err := json.Marshal(model.ReportToGitroot{Level: level, Content: content})
197 if err != nil {
198 return err
199 }
200 ptr, size := stringToPtr(string(reportMarshalled))
201 _report(ptr, size)
202 return nil
203}
204
205func (s *pServer) ExportFunc(name string, callback func(args map[string]string) (map[string]string, error)) {
206 s.exportedFuncs[name] = callback
207}
208
209func (s *pServer) CallFunc(plugin string, name string, args map[string]string) (map[string]string, error) {
210 call := model.Call{
211 Plugin: plugin,
212 Name: name,
213 Args: args,
214 }
215 callJson, err := json.Marshal(call)
216 if err != nil {
217 return nil, err
218 }
219 ptr, size := stringToPtr(string(callJson))
220 ptrSize := _call(ptr, size)
221 if ptrSize == 0 {
222 return nil, errors.New("can't call")
223 }
224 ptrRes := uint32(ptrSize >> 32)
225 sizeRes := uint32(ptrSize)
226 resCall := model.CallResFromString(ptrToString(ptrRes, sizeRes))
227 if resCall.Err != "" {
228 return nil, errors.New(resCall.Err)
229 }
230 return resCall.Res, nil
231}