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 background
  6
  7import (
  8	"errors"
  9	"os"
 10
 11	"github.com/go-git/go-git/v5/plumbing"
 12	"github.com/go-git/go-git/v5/plumbing/protocol/packp"
 13	"github.com/samber/oops"
 14	"gitroot.dev/server/logger"
 15	"gitroot.dev/server/plugin"
 16	"gitroot.dev/server/user"
 17)
 18
 19type checkPluginsTaskInput struct {
 20	repoName         string
 21	isRootRepo       bool
 22	availablePlugins []plugin.Plugin
 23}
 24
 25func (m *Manager) syncPluginsInExistingRepos() {
 26	plugins, err := m.pluginManager.Availables(m.ctx)
 27	if err != nil {
 28		m.logger.Error("can't get availables plugins", err)
 29		return
 30	}
 31
 32	entries, err := os.ReadDir(m.conf.PathRepositories())
 33	if err != nil {
 34		m.logger.Error("can't read dir of repositories", err)
 35		return
 36	}
 37
 38	for _, repoName := range entries {
 39		m.Start(Task{
 40			call: m.checkPlugins,
 41			t: checkPluginsTaskInput{
 42				repoName:         repoName.Name(),
 43				isRootRepo:       m.conf.IsForgeRepo(repoName.Name()),
 44				availablePlugins: plugins,
 45			},
 46		})
 47	}
 48}
 49
 50func (m *Manager) checkPlugins(input interface{}) error {
 51	checkInput := input.(checkPluginsTaskInput)
 52	repoName := checkInput.repoName
 53	isRootRepo := checkInput.isRootRepo
 54	availablePlugins := checkInput.availablePlugins
 55
 56	repo, err := m.repoManager.Open(logger.AddCaller(m.ctx, "checkPlugins"), repoName)
 57	if err != nil {
 58		return oops.Wrapf(err, "can't open repo")
 59	}
 60	defer repo.Close()
 61
 62	status, err := repo.Status()
 63	if errors.Is(err, plumbing.ErrReferenceNotFound) { //repo has no head == it is empty == nothing todo
 64		return nil
 65	}
 66	if err != nil {
 67		return oops.Wrapf(err, "can't status")
 68	}
 69
 70	conf, err := repo.Configuration()
 71	if err != nil {
 72		return oops.Wrapf(err, "can't get conf")
 73	}
 74
 75	repoWriter, err := repo.WillWrite(conf.DefaultBranch)
 76	if err != nil {
 77		return oops.Wrapf(err, "can't will write")
 78	}
 79
 80	pluginsContent, err := repo.ContentPluginsConf()
 81	if err != nil {
 82		return oops.Wrapf(err, "can't get plugin conf")
 83	}
 84	plugins, err := plugin.ParsePlugins(pluginsContent, false)
 85	if err != nil {
 86		return oops.Wrapf(err, "can't parse plugin conf")
 87	}
 88
 89	usersToAdd := make([]user.SimpleUser, 0)
 90	usersToDel := make([]user.SimpleUser, 0)
 91
 92	for _, ap := range availablePlugins {
 93		found := false
 94		for i, p := range plugins {
 95			if ap.Name == p.Name {
 96				found = true
 97				plugins[i] = p.OverrideWith(ap).SetActive(p.Active).UpdateVersion(ap.Version) //replace plugin in project by fullloaded one
 98				break
 99			}
100		}
101		if !found {
102			plugins = append(plugins, ap.SetActive(false))
103		}
104		usersToAdd = append(usersToAdd, ap.Commiter().SimpleUser)
105	}
106
107	if len(availablePlugins) > 0 {
108		for i, p := range plugins {
109			found := false
110			for _, ap := range availablePlugins {
111				if ap.Name == p.Name {
112					found = true
113					break
114				}
115			}
116			if !found {
117				plugins = append(plugins[:i], plugins[i+1:]...)
118				if p.Commiter() != nil {
119					usersToDel = append(usersToDel, p.Commiter().SimpleUser)
120				}
121			}
122		}
123	}
124
125	newPluginsContent, _ := m.pluginManager.ToNewRepo(isRootRepo, false, plugins)
126
127	if err := repoWriter.Write(m.conf.PathFilePlugins(), newPluginsContent); err != nil {
128		return oops.Wrapf(err, "can't write new plugin conf")
129	}
130
131	fileUsersContent, err := repo.AppendUserToGroup("owner", usersToAdd...)
132	if err != nil {
133		return oops.With("userToAdd", usersToAdd).Wrapf(err, "can't add user to users file")
134	}
135	err = repoWriter.Write(m.conf.PathFileUsers(), fileUsersContent)
136	if err != nil {
137		return oops.Wrapf(err, "can't write users file")
138	}
139
140	// TODO delete
141
142	m.logger.Debug("sync plugins", logger.NewLoggerPair("repo", repoName))
143	newHash, err := repoWriter.CommitAll("sync plugins", m.userManager.RootCommiter())
144	if err != nil {
145		return err
146	}
147
148	if newHash == plumbing.ZeroHash { //nothing change == nothing todo
149		return nil
150	}
151
152	m.PostPush(m.userManager.RootCommiter().SimpleUser, repoName, []*packp.Command{{Name: status.Name(), Old: status.Hash(), New: newHash}})
153
154	return nil
155}