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}