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}