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 if m.conf.IsForgeRepo(postPushInput.repoName) {
88 repoConfiguration, err := repo.Configuration()
89 if err != nil {
90 return errorHandler.Wrapf(err, "can't read configuration in forgerepo")
91 }
92 forgeRepoRepositoriesFileTouchedInDefaultBranch := plugin.IsFileTouched(cmds, repoConfiguration.DefaultBranch, m.conf.PathFileRepositories())
93 forgeRepoPluginsFileTouchedInDefaultBranch := plugin.IsFileTouched(cmds, repoConfiguration.DefaultBranch, m.conf.PathFilePlugins())
94 m.logger.Info(
95 "postpush commands",
96 logger.NewLoggerPair("repo", postPushInput.repoName),
97 logger.NewLoggerPair("defaultBranch", repoConfiguration.DefaultBranch),
98 logger.NewLoggerPair("forgeRepoRepositoriesFileTouchedInDefaultBranch", forgeRepoRepositoriesFileTouchedInDefaultBranch),
99 logger.NewLoggerPair("forgeRepoPluginsFileTouchedInDefaultBranch", forgeRepoPluginsFileTouchedInDefaultBranch),
100 )
101
102 if forgeRepoPluginsFileTouchedInDefaultBranch {
103 filePlugin, err := repo.ContentPluginsConf()
104 if err != nil {
105 return errorHandler.Wrapf(err, "can't open file plugin in forgerepo")
106 }
107 m.logger.Debug("start sync")
108 if err := m.pluginManager.Sync(filePlugin); err != nil {
109 return errorHandler.Wrapf(err, "can't sync plugins files")
110 }
111 defer m.syncPluginsInExistingRepos()
112 }
113
114 if forgeRepoRepositoriesFileTouchedInDefaultBranch {
115 fileContent, err := repo.ContentRepositoriesConf()
116 if err != nil {
117 errorHandler.Wrapf(err, "can't open file repositories")
118 }
119 reposToCreate, reposToDelete, err := m.repoManager.ForgeRepoMakeDiffRepos(m.ctx, fileContent)
120 if err != nil {
121 return errorHandler.Wrapf(err, "can't diff repositories between forgeConfg and local")
122 }
123 m.logger.Debug("to create", logger.NewLoggerPair("repos", reposToCreate))
124 m.syncRepos(reposToCreate, reposToDelete, postPushInput.pusher)
125 }
126 }
127
128 if err := m.pluginManager.Run(m.ctx, postPushInput.repoName, cmds); err != nil {
129 return errorHandler.Wrapf(err, "plugin error")
130 }
131
132 return nil
133}
134
135func needToAddUser(c *packp.Command, repo *repository.GitRootRepository, pusher user.SimpleUser) (bool, *user.GitRootUser, string, error) {
136 if c.Action() == packp.Delete || c.Action() == packp.Update {
137 return false, nil, "", nil
138 }
139 canWrite, currentUser, err := repo.CanWrite(pusher.Ssh, c.Name.Short())
140 needToBeAdded := canWrite && currentUser.Group == nil
141 return needToBeAdded, currentUser, c.Name.Short(), err
142}