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/v6/plumbing"
 12	"github.com/go-git/go-git/v6/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	entriesName := make([]string, len(entries))
 38	for i, e := range entries {
 39		entriesName[i] = e.Name()
 40	}
 41
 42	m.logger.Debug("start sync plugins in repos", logger.NewLoggerPairNamedSlice("plugins", plugins), logger.NewLoggerPair("repos", entriesName))
 43
 44	for _, repoName := range entriesName {
 45		m.Start(Task{
 46			call: m.checkPlugins,
 47			t: checkPluginsTaskInput{
 48				repoName:         repoName,
 49				isRootRepo:       m.conf.IsForgeRepo(repoName),
 50				availablePlugins: plugins,
 51			},
 52		})
 53	}
 54}
 55
 56func (m *Manager) checkPlugins(input interface{}) error {
 57	checkInput := input.(checkPluginsTaskInput)
 58	repoName := checkInput.repoName
 59	isRootRepo := checkInput.isRootRepo
 60	availablePlugins := checkInput.availablePlugins
 61
 62	repo, err := m.repoManager.Open(logger.AddCaller(m.ctx, "checkPlugins"), repoName)
 63	if err != nil {
 64		return oops.Wrapf(err, "can't open repo")
 65	}
 66	defer repo.Close()
 67
 68	defaultBranch, err := repo.DefaultBranch()
 69	if err != nil {
 70		return oops.Wrapf(err, "can't get default branch")
 71	}
 72
 73	hash, err := repo.DefaultBranchCurrentHash()
 74	if errors.Is(err, plumbing.ErrReferenceNotFound) { //repo has no head == it is empty == nothing todo
 75		return nil
 76	}
 77	if err != nil {
 78		return oops.Wrapf(err, "can't status")
 79	}
 80
 81	conf, err := repo.Configuration()
 82	if err != nil {
 83		return oops.Wrapf(err, "can't get conf")
 84	}
 85
 86	repoWriter, err := repo.WillWrite(conf.DefaultBranch)
 87	if err != nil {
 88		return oops.Wrapf(err, "can't will write")
 89	}
 90
 91	pluginsContent, err := repo.ContentPluginsConf()
 92	if err != nil {
 93		return oops.Wrapf(err, "can't get plugin conf")
 94	}
 95	plugins, err := plugin.ParsePlugins(pluginsContent, false)
 96	if err != nil {
 97		return oops.Wrapf(err, "can't parse plugin conf")
 98	}
 99
100	usersToAdd := make([]user.SimpleUser, 0)
101	usersToDel := make([]user.SimpleUser, 0)
102
103	for _, ap := range availablePlugins {
104		found := false
105		for i, p := range plugins {
106			if ap.Equal(p) {
107				found = true
108				plugins[i] = p.OverrideWith(ap).SetActive(p.Active) //replace plugin in project by fullloaded one
109				break
110			}
111		}
112		if !found {
113			plugins = append(plugins, ap.SetActive(false))
114		}
115		usersToAdd = append(usersToAdd, ap.Commiter().SimpleUser)
116	}
117
118	if len(availablePlugins) > 0 {
119		for i, p := range plugins {
120			found := false
121			for _, ap := range availablePlugins {
122				if ap.Equal(p) {
123					found = true
124					break
125				}
126			}
127			if !found {
128				plugins = append(plugins[:i], plugins[i+1:]...)
129				if p.Commiter() != nil {
130					usersToDel = append(usersToDel, p.Commiter().SimpleUser)
131				}
132			}
133		}
134	}
135
136	newPluginsContent, _ := m.pluginManager.ToNewRepo(isRootRepo, false, plugins)
137
138	if err := repoWriter.Write(m.conf.PathFilePlugins(), newPluginsContent); err != nil {
139		return oops.Wrapf(err, "can't write new plugin conf")
140	}
141
142	fileUsersContent, err := repo.AppendUserToGroup("owner", usersToAdd...)
143	if err != nil {
144		return oops.With("userToAdd", usersToAdd).Wrapf(err, "can't add user to users file")
145	}
146	err = repoWriter.Write(m.conf.PathFileUsers(), fileUsersContent)
147	if err != nil {
148		return oops.Wrapf(err, "can't write users file")
149	}
150
151	// TODO delete
152
153	m.logger.Debug("sync plugins", logger.NewLoggerPair("repo", repoName))
154	newHash, err := repoWriter.CommitAll("sync plugins", m.userManager.RootCommiter())
155	if err != nil {
156		return err
157	}
158
159	if newHash == plumbing.ZeroHash { //nothing change == nothing todo
160		return nil
161	}
162
163	m.PostPush(
164		m.userManager.RootCommiter().SimpleUser,
165		repoName,
166		[]*packp.Command{{Name: defaultBranch, Old: hash, New: newHash}},
167		map[plumbing.ReferenceName][]plumbing.Hash{defaultBranch: {newHash}},
168	)
169
170	return nil
171}