// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: EUPL-1.2 package background import ( "errors" "os" "github.com/go-git/go-git/v6/plumbing" "github.com/go-git/go-git/v6/plumbing/protocol/packp" "github.com/samber/oops" "gitroot.dev/server/logger" "gitroot.dev/server/plugin" "gitroot.dev/server/user" ) type checkPluginsTaskInput struct { repoName string isRootRepo bool availablePlugins []plugin.Plugin } func (m *Manager) syncPluginsInExistingRepos() { plugins, err := m.pluginManager.Availables(m.ctx) if err != nil { m.logger.Error("can't get availables plugins", err) return } entries, err := os.ReadDir(m.conf.PathRepositories()) if err != nil { m.logger.Error("can't read dir of repositories", err) return } entriesName := make([]string, len(entries)) for i, e := range entries { entriesName[i] = e.Name() } m.logger.Debug("start sync plugins in repos", logger.NewLoggerPairNamedSlice("plugins", plugins), logger.NewLoggerPair("repos", entriesName)) for _, repoName := range entriesName { m.Start(Task{ call: m.checkPlugins, t: checkPluginsTaskInput{ repoName: repoName, isRootRepo: m.conf.IsForgeRepo(repoName), availablePlugins: plugins, }, }) } } func (m *Manager) checkPlugins(input interface{}) error { checkInput := input.(checkPluginsTaskInput) repoName := checkInput.repoName isRootRepo := checkInput.isRootRepo availablePlugins := checkInput.availablePlugins repo, err := m.repoManager.Open(logger.AddCaller(m.ctx, "checkPlugins"), repoName) if err != nil { return oops.Wrapf(err, "can't open repo") } defer repo.Close() status, err := repo.Status() if errors.Is(err, plumbing.ErrReferenceNotFound) { //repo has no head == it is empty == nothing todo return nil } if err != nil { return oops.Wrapf(err, "can't status") } conf, err := repo.Configuration() if err != nil { return oops.Wrapf(err, "can't get conf") } repoWriter, err := repo.WillWrite(conf.DefaultBranch) if err != nil { return oops.Wrapf(err, "can't will write") } pluginsContent, err := repo.ContentPluginsConf() if err != nil { return oops.Wrapf(err, "can't get plugin conf") } plugins, err := plugin.ParsePlugins(pluginsContent, false) if err != nil { return oops.Wrapf(err, "can't parse plugin conf") } usersToAdd := make([]user.SimpleUser, 0) usersToDel := make([]user.SimpleUser, 0) for _, ap := range availablePlugins { found := false for i, p := range plugins { if ap.Name == p.Name { found = true plugins[i] = p.OverrideWith(ap).SetActive(p.Active).UpdateVersion(ap.Version) //replace plugin in project by fullloaded one break } } if !found { plugins = append(plugins, ap.SetActive(false)) } usersToAdd = append(usersToAdd, ap.Commiter().SimpleUser) } if len(availablePlugins) > 0 { for i, p := range plugins { found := false for _, ap := range availablePlugins { if ap.Name == p.Name { found = true break } } if !found { plugins = append(plugins[:i], plugins[i+1:]...) if p.Commiter() != nil { usersToDel = append(usersToDel, p.Commiter().SimpleUser) } } } } newPluginsContent, _ := m.pluginManager.ToNewRepo(isRootRepo, false, plugins) if err := repoWriter.Write(m.conf.PathFilePlugins(), newPluginsContent); err != nil { return oops.Wrapf(err, "can't write new plugin conf") } fileUsersContent, err := repo.AppendUserToGroup("owner", usersToAdd...) if err != nil { return oops.With("userToAdd", usersToAdd).Wrapf(err, "can't add user to users file") } err = repoWriter.Write(m.conf.PathFileUsers(), fileUsersContent) if err != nil { return oops.Wrapf(err, "can't write users file") } // TODO delete m.logger.Debug("sync plugins", logger.NewLoggerPair("repo", repoName)) newHash, err := repoWriter.CommitAll("sync plugins", m.userManager.RootCommiter()) if err != nil { return err } if newHash == plumbing.ZeroHash { //nothing change == nothing todo return nil } m.PostPush(m.userManager.RootCommiter().SimpleUser, repoName, []*packp.Command{{Name: status.Name(), Old: status.Hash(), New: newHash}}) return nil }