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 "github.com/go-git/go-git/v6/plumbing"
9 "github.com/go-git/go-git/v6/plumbing/protocol/packp"
10 "github.com/samber/oops"
11 "gitroot.dev/server/logger"
12 "gitroot.dev/server/plugin"
13 "gitroot.dev/server/repository"
14 "gitroot.dev/server/user"
15)
16
17type postPushTaskInput struct {
18 pusher user.SimpleUser
19 repoName string
20 commands []*packp.Command
21 commitsByRef map[plumbing.ReferenceName][]plumbing.Hash
22}
23
24func (m *Manager) PostPush(pusher user.SimpleUser, repoName string, commands []*packp.Command, commitsByRef map[plumbing.ReferenceName][]plumbing.Hash) {
25 m.Start(Task{
26 call: m.postPush,
27 t: postPushTaskInput{
28 pusher: pusher,
29 repoName: repoName,
30 commands: commands,
31 commitsByRef: commitsByRef,
32 },
33 })
34}
35
36func (m *Manager) postPush(input interface{}) error {
37 postPushInput := input.(postPushTaskInput)
38 errorHandler := oops.
39 With("pusher", postPushInput.pusher).
40 With("repoName", postPushInput.repoName).
41 With("commands", len(postPushInput.commands))
42
43 if len(postPushInput.commands) == 0 { //nothing todo (push just a tag or a branch)
44 return nil
45 }
46
47 repo, err := m.repoManager.Open(logger.AddCaller(m.ctx, "postPush"), postPushInput.repoName)
48 if err != nil {
49 return errorHandler.Wrapf(err, "can't open repository")
50 }
51 defer repo.Close()
52
53 for _, c := range postPushInput.commands {
54 m.logger.Info("before needToAddUser", logger.NewLoggerPair("old", c.Old.String()), logger.NewLoggerPair("new", c.New.String()))
55 }
56
57 for _, c := range postPushInput.commands {
58 if ok, currentUser, branch, err := needToAddUser(c, repo, postPushInput.pusher); ok {
59 writer, err := repo.WillWrite(c.Name)
60 if err != nil {
61 return errorHandler.Wrapf(err, "can't WillWrite")
62 }
63 newCommitHash, err := writer.AddUserInfo(currentUser, branch)
64 if err != nil {
65 return errorHandler.Wrapf(err, "can't AddUserInfo")
66 }
67 writer.Accept()
68 m.logger.Debug("accept commit to add user", logger.NewLoggerPair("hash", newCommitHash.String()))
69 repo.StopWrite()
70 m.logger.Debug("stop write")
71 c.New = newCommitHash
72 postPushInput.commitsByRef[c.Name] = append(postPushInput.commitsByRef[c.Name], newCommitHash)
73 } else if err != nil {
74 return errorHandler.Wrapf(err, "NeedToAddUser error")
75 }
76 }
77
78 for _, c := range postPushInput.commands {
79 m.logger.Info("after needToAddUser", logger.NewLoggerPair("old", c.Old.String()), logger.NewLoggerPair("new", c.New.String()))
80 }
81
82 cmds, err := plugin.CommandForDiffFromPackpCmd(m.ctx, m.logger, repo, postPushInput.commands, postPushInput.commitsByRef, postPushInput.pusher)
83 if err != nil {
84 return errorHandler.Wrapf(err, "CommandForDiffFromPackpCmd error")
85 }
86
87 repoConfiguration, err := repo.Configuration()
88 if err != nil {
89 return errorHandler.Wrapf(err, "can't read configuration in forgerepo")
90 }
91
92 repoPluginsFileTouchedInDefaultBranch := plugin.IsFileTouched(cmds, repoConfiguration.DefaultBranch, m.conf.PathFilePlugins())
93
94 if repoPluginsFileTouchedInDefaultBranch {
95 m.scedulerManager.LoadChanges(m.ctx, postPushInput.repoName)
96 }
97
98 if m.conf.IsForgeRepo(postPushInput.repoName) {
99 forgeRepoRepositoriesFileTouchedInDefaultBranch := plugin.IsFileTouched(cmds, repoConfiguration.DefaultBranch, m.conf.PathFileRepositories())
100 m.logger.Info(
101 "postpush commands",
102 logger.NewLoggerPair("repo", postPushInput.repoName),
103 logger.NewLoggerPair("defaultBranch", repoConfiguration.DefaultBranch),
104 logger.NewLoggerPair("forgeRepoRepositoriesFileTouchedInDefaultBranch", forgeRepoRepositoriesFileTouchedInDefaultBranch),
105 logger.NewLoggerPair("forgeRepoPluginsFileTouchedInDefaultBranch", repoPluginsFileTouchedInDefaultBranch),
106 )
107
108 if repoPluginsFileTouchedInDefaultBranch {
109 filePlugin, err := repo.ContentPluginsConf()
110 if err != nil {
111 return errorHandler.Wrapf(err, "can't open file plugin in forgerepo")
112 }
113 m.logger.Debug("start sync")
114 if err := m.pluginManager.Sync(filePlugin); err != nil {
115 return errorHandler.Wrapf(err, "can't sync plugins files")
116 }
117 defer m.syncPluginsInExistingRepos()
118 }
119
120 if forgeRepoRepositoriesFileTouchedInDefaultBranch {
121 fileContent, err := repo.ContentRepositoriesConf()
122 if err != nil {
123 errorHandler.Wrapf(err, "can't open file repositories")
124 }
125 reposToCreate, reposToDelete, err := m.repoManager.ForgeRepoMakeDiffRepos(m.ctx, fileContent)
126 if err != nil {
127 return errorHandler.Wrapf(err, "can't diff repositories between forgeConfg and local")
128 }
129 m.logger.Debug("to create", logger.NewLoggerPair("repos", reposToCreate))
130 m.syncRepos(reposToCreate, reposToDelete, postPushInput.pusher)
131 }
132 }
133
134 if err := m.pluginManager.Run(m.ctx, postPushInput.repoName, cmds); err != nil {
135 return errorHandler.Wrapf(err, "plugin error")
136 }
137
138 return nil
139}
140
141func needToAddUser(c *packp.Command, repo *repository.GitRootRepository, pusher user.SimpleUser) (bool, *user.GitRootUser, string, error) {
142 if c.Action() == packp.Delete || c.Action() == packp.Update {
143 return false, nil, "", nil
144 }
145 canWrite, currentUser, err := repo.CanWrite(pusher.Ssh, c.Name.Short())
146 needToBeAdded := canWrite && currentUser.NeedToBeCreated(c.Name.Short())
147 return needToBeAdded, currentUser, c.Name.Short(), err
148}