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