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}