7e81775184
Merging PR may fail because of various problems. The pull request may have a dirty state because there is no transaction when merging a pull request. ref https://github.com/go-gitea/gitea/pull/25741#issuecomment-2074126393 This PR moves all database update operations to post-receive handler for merging a pull request and having a database transaction. That means if database operations fail, then the git merging will fail, the git client will get a fail result. There are already many tests for pull request merging, so we don't need to add a new one. --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> (cherry picked from commit ebf0c969403d91ed80745ff5bd7dfbdb08174fc7) Conflicts: modules/private/hook.go routers/private/hook_post_receive.go trivial conflicts because 263a716cb5 * Performance optimization for git push (#30104) was not cherry-picked and because of 998a431747a15cc95f7056a2029b736551eb037b Do not update PRs based on events that happened before they existed (cherry picked from commit eb792d9f8a4c6972f5a4cfea6e9cb643b1d6a7ce) (cherry picked from commit ec3f5f9992d7ff8250c044a4467524d53bd50210)
145 lines
4.9 KiB
Go
145 lines
4.9 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package private
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/repository"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
)
|
|
|
|
// Git environment variables
|
|
const (
|
|
GitAlternativeObjectDirectories = "GIT_ALTERNATE_OBJECT_DIRECTORIES"
|
|
GitObjectDirectory = "GIT_OBJECT_DIRECTORY"
|
|
GitQuarantinePath = "GIT_QUARANTINE_PATH"
|
|
GitPushOptionCount = "GIT_PUSH_OPTION_COUNT"
|
|
)
|
|
|
|
// GitPushOptions is a wrapper around a map[string]string
|
|
type GitPushOptions map[string]string
|
|
|
|
// GitPushOptions keys
|
|
const (
|
|
GitPushOptionRepoPrivate = "repo.private"
|
|
GitPushOptionRepoTemplate = "repo.template"
|
|
)
|
|
|
|
// Bool checks for a key in the map and parses as a boolean
|
|
func (g GitPushOptions) Bool(key string, def bool) bool {
|
|
if val, ok := g[key]; ok {
|
|
if b, err := strconv.ParseBool(val); err == nil {
|
|
return b
|
|
}
|
|
}
|
|
return def
|
|
}
|
|
|
|
// HookOptions represents the options for the Hook calls
|
|
type HookOptions struct {
|
|
OldCommitIDs []string
|
|
NewCommitIDs []string
|
|
RefFullNames []git.RefName
|
|
UserID int64
|
|
UserName string
|
|
GitObjectDirectory string
|
|
GitAlternativeObjectDirectories string
|
|
GitQuarantinePath string
|
|
GitPushOptions GitPushOptions
|
|
PullRequestID int64
|
|
PushTrigger repository.PushTrigger
|
|
DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
|
|
IsWiki bool
|
|
ActionPerm int
|
|
}
|
|
|
|
// SSHLogOption ssh log options
|
|
type SSHLogOption struct {
|
|
IsError bool
|
|
Message string
|
|
}
|
|
|
|
// HookPostReceiveResult represents an individual result from PostReceive
|
|
type HookPostReceiveResult struct {
|
|
Results []HookPostReceiveBranchResult
|
|
RepoWasEmpty bool
|
|
Err string
|
|
}
|
|
|
|
// HookPostReceiveBranchResult represents an individual branch result from PostReceive
|
|
type HookPostReceiveBranchResult struct {
|
|
Message bool
|
|
Create bool
|
|
Branch string
|
|
URL string
|
|
}
|
|
|
|
// HookProcReceiveResult represents an individual result from ProcReceive
|
|
type HookProcReceiveResult struct {
|
|
Results []HookProcReceiveRefResult
|
|
Err string
|
|
}
|
|
|
|
// HookProcReceiveRefResult represents an individual result from ProcReceive
|
|
type HookProcReceiveRefResult struct {
|
|
OldOID string
|
|
NewOID string
|
|
Ref string
|
|
OriginalRef git.RefName
|
|
IsForcePush bool
|
|
IsNotMatched bool
|
|
Err string
|
|
}
|
|
|
|
// HookPreReceive check whether the provided commits are allowed
|
|
func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) ResponseExtra {
|
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
|
|
req := newInternalRequest(ctx, reqURL, "POST", opts)
|
|
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
|
|
_, extra := requestJSONResp(req, &ResponseText{})
|
|
return extra
|
|
}
|
|
|
|
// HookPostReceive updates services and users
|
|
func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, ResponseExtra) {
|
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
|
|
req := newInternalRequest(ctx, reqURL, "POST", opts)
|
|
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
|
|
return requestJSONResp(req, &HookPostReceiveResult{})
|
|
}
|
|
|
|
// HookProcReceive proc-receive hook
|
|
func HookProcReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookProcReceiveResult, ResponseExtra) {
|
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/proc-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
|
|
|
|
req := newInternalRequest(ctx, reqURL, "POST", opts)
|
|
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
|
|
return requestJSONResp(req, &HookProcReceiveResult{})
|
|
}
|
|
|
|
// SetDefaultBranch will set the default branch to the provided branch for the provided repository
|
|
func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) ResponseExtra {
|
|
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/set-default-branch/%s/%s/%s",
|
|
url.PathEscape(ownerName),
|
|
url.PathEscape(repoName),
|
|
url.PathEscape(branch),
|
|
)
|
|
req := newInternalRequest(ctx, reqURL, "POST")
|
|
_, extra := requestJSONResp(req, &ResponseText{})
|
|
return extra
|
|
}
|
|
|
|
// SSHLog sends ssh error log response
|
|
func SSHLog(ctx context.Context, isErr bool, msg string) error {
|
|
reqURL := setting.LocalURL + "api/internal/ssh/log"
|
|
req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg})
|
|
_, extra := requestJSONResp(req, &ResponseText{})
|
|
return extra.Error
|
|
}
|