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}
 29
 30var server = &pServer{
 31	atLeastOneChange: false,
 32	pluginOpts:       model.PluginOption{},
 33}
 34
 35func Register(run []model.PluginRun, p PluginFactory) {
 36	server.run = run
 37	server.plugin = p(server)
 38}
 39
 40func (s *pServer) PluginOption(opts ...model.PluginOptionWith) {
 41	for _, opt := range opts {
 42		server.pluginOpts = opt(server.pluginOpts)
 43	}
 44}
 45
 46func (s *pServer) ForgeConf() (model.ForgeConf, error) {
 47	forgeConf := model.ForgeConf{}
 48	ptrSize := _forgeConf()
 49	if ptrSize == 0 {
 50		return forgeConf, errors.New("can't make diff")
 51	}
 52	ptrDiff := uint32(ptrSize >> 32)
 53	sizeDiff := uint32(ptrSize)
 54	forgeConfJson := gjson.Parse(ptrToString(ptrDiff, sizeDiff))
 55	return model.ForgeConf{
 56		Domain:           forgeConfJson.Get("domain").String(),
 57		ExternalSshAddr:  forgeConfJson.Get("externalSshAddr").String(),
 58		ExternalHttpAddr: forgeConfJson.Get("externalHttpAddr").String(),
 59	}, nil
 60}
 61
 62func (s *pServer) Worktree() fs.FS {
 63	return os.DirFS("/worktree")
 64}
 65
 66func (s *pServer) Webcontent() fs.FS {
 67	return os.DirFS("/webcontent")
 68}
 69
 70func (s *pServer) Cache() fs.FS {
 71	return os.DirFS("/cache")
 72}
 73
 74func (s *pServer) ModifyContent(filepath, content string) {
 75	s.atLeastOneChange = true
 76	ptr, size := stringToPtr(filepath)
 77	ptr2, size2 := stringToPtr(content)
 78	_modifyContent(ptr, size, ptr2, size2)
 79	runtime.KeepAlive(filepath)
 80	runtime.KeepAlive(content)
 81}
 82
 83func (s *pServer) ModifyWebContent(filepath, content string) {
 84	ptr, size := stringToPtr(filepath)
 85	ptr2, size2 := stringToPtr(content)
 86	_modifyWebContent(ptr, size, ptr2, size2)
 87	runtime.KeepAlive(filepath)
 88	runtime.KeepAlive(content)
 89}
 90
 91func (s *pServer) ReplaceWebContent(filepath, oldContent, content string) {
 92	ptr, size := stringToPtr(filepath)
 93	ptr2, size2 := stringToPtr(oldContent)
 94	ptr3, size3 := stringToPtr(content)
 95	_replaceWebContent(ptr, size, ptr2, size2, ptr3, size3)
 96	runtime.KeepAlive(filepath)
 97	runtime.KeepAlive(oldContent)
 98	runtime.KeepAlive(content)
 99}
100
101func (s *pServer) ModifyCacheContent(filepath, content string) {
102	ptr, size := stringToPtr(filepath)
103	ptr2, size2 := stringToPtr(content)
104	_modifyCacheContent(ptr, size, ptr2, size2)
105	runtime.KeepAlive(filepath)
106	runtime.KeepAlive(content)
107}
108
109func (s *pServer) CommitAllIfNeeded(message string) {
110	if s.atLeastOneChange {
111		ptr, size := stringToPtr(message)
112		_commitAll(ptr, size)
113		runtime.KeepAlive(message)
114	}
115}
116
117func (s *pServer) DiffWithParent(hash string, oldFilepath string, newFilePath string) (string, error) {
118	ptrHash, sizeHash := stringToPtr(hash)
119	ptrOldFile, sizeOldFile := stringToPtr(oldFilepath)
120	ptrNewFile, sizeNewFile := stringToPtr(newFilePath)
121	ptrSizeDiff := _diffWithParent(ptrHash, sizeHash, ptrOldFile, sizeOldFile, ptrNewFile, sizeNewFile)
122	if ptrSizeDiff == 0 {
123		return "", errors.New("can't make diff")
124	}
125	ptrDiff := uint32(ptrSizeDiff >> 32)
126	sizeDiff := uint32(ptrSizeDiff)
127	return ptrToString(ptrDiff, sizeDiff), nil
128}
129
130func (s *pServer) Log(message string) {
131	ptr, size := stringToPtr(message)
132	_log(ptr, size)
133	runtime.KeepAlive(message)
134}
135
136func (s *pServer) LogError(message string, err error) {
137	ptr, size := stringToPtr(message)
138	errPtr, errSize := stringToPtr(err.Error())
139	_logError(ptr, size, errPtr, errSize)
140	runtime.KeepAlive(message)
141	runtime.KeepAlive(err)
142}
143
144func (s *pServer) Merge(from string, to string) {
145	fromPtr, fromSize := stringToPtr(from)
146	toPtr, toSize := stringToPtr(to)
147	_merge(fromPtr, fromSize, toPtr, toSize)
148	runtime.KeepAlive(from)
149	runtime.KeepAlive(to)
150}
151
152func (s *pServer) Commits(from string, to string) ([]model.Commit, error) {
153	fromPtr, fromSize := stringToPtr(from)
154	toPtr, toSize := stringToPtr(to)
155	ptrSizeDiff := _commits(fromPtr, fromSize, toPtr, toSize)
156	runtime.KeepAlive(from)
157	runtime.KeepAlive(to)
158	if ptrSizeDiff == 0 {
159		return nil, errors.New("can't get commits")
160	}
161	ptrDiff := uint32(ptrSizeDiff >> 32)
162	sizeDiff := uint32(ptrSizeDiff)
163	return model.CommitsFromString(ptrToString(ptrDiff, sizeDiff)), nil
164}
165
166func (s *pServer) Exec(exec model.Exec) (model.ExecStatus, error) {
167	execMarshalled, err := json.Marshal(exec)
168	if err != nil {
169		return model.ExecStatus{}, err
170	}
171	ptr, size := stringToPtr(string(execMarshalled))
172	ptrSize := _exec(ptr, size)
173	if ptrSize == 0 {
174		return model.ExecStatus{}, errors.New("can't exec")
175	}
176	ptrLog := uint32(ptrSize >> 32)
177	sizeLog := uint32(ptrSize)
178	return model.ExecStatusFromString(ptrToString(ptrLog, sizeLog)), nil
179}
180
181func (s *pServer) ReportToPlugin(report model.Report) {
182	if s.pluginOpts.Reporter != nil {
183		if err := s.pluginOpts.Reporter(report); err != nil {
184			s.LogError("plugin reporter err", err)
185		}
186	}
187}
188
189func (s *pServer) Report(level model.ReportLevel, content []string) error {
190	reportMarshalled, err := json.Marshal(model.ReportToGitroot{Level: level, Content: content})
191	if err != nil {
192		return err
193	}
194	ptr, size := stringToPtr(string(reportMarshalled))
195	_report(ptr, size)
196	return nil
197}