mirror of
https://github.com/go-gitea/gitea
synced 2024-11-21 23:11:05 +01:00
Move push update to post-receive and protected branch check to pre-receive (#1030)
* move all push update to git hook post-receive and protected branch check to git hook pre-receive * add SSH_ORIGINAL_COMMAND check back * remove all unused codes * fix the import
This commit is contained in:
parent
e8e56da9ac
commit
cd1821a7e2
9 changed files with 175 additions and 415 deletions
135
cmd/hook.go
135
cmd/hook.go
|
@ -5,11 +5,22 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/git"
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/httplib"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
|
"github.com/Unknwon/com"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -57,10 +68,59 @@ func runHookPreReceive(c *cli.Context) error {
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setup("hooks/pre-receive.log"); err != nil {
|
if err := setup("hooks/pre-receive.log"); err != nil {
|
||||||
fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
|
fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the environment setted on serv command
|
||||||
|
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
||||||
|
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
for scanner.Scan() {
|
||||||
|
buf.Write(scanner.Bytes())
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
|
||||||
|
// TODO: support news feeds for wiki
|
||||||
|
if isWiki {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := bytes.Fields(scanner.Bytes())
|
||||||
|
if len(fields) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
oldCommitID := string(fields[0])
|
||||||
|
newCommitID := string(fields[1])
|
||||||
|
refFullName := string(fields[2])
|
||||||
|
|
||||||
|
branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
|
||||||
|
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
|
||||||
|
if err != nil {
|
||||||
|
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if protectBranch != nil {
|
||||||
|
fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check and deletion
|
||||||
|
if newCommitID == git.EmptySHA {
|
||||||
|
fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check force push
|
||||||
|
output, err := git.NewCommand("rev-list", oldCommitID, "^"+newCommitID).Run()
|
||||||
|
if err != nil {
|
||||||
|
fail("Internal error", "Fail to detect force push: %v", err)
|
||||||
|
} else if len(output) > 0 {
|
||||||
|
fail(fmt.Sprintf("Branch '%s' is protected from force push", branchName), "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,23 +133,6 @@ func runHookUpdate(c *cli.Context) error {
|
||||||
fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
|
fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
args := c.Args()
|
|
||||||
if len(args) != 3 {
|
|
||||||
fail("Arguments received are not equal to three", "Arguments received are not equal to three")
|
|
||||||
} else if len(args[0]) == 0 {
|
|
||||||
fail("First argument 'refName' is empty", "First argument 'refName' is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid := os.Getenv(envUpdateTaskUUID)
|
|
||||||
if err := models.AddUpdateTask(&models.UpdateTask{
|
|
||||||
UUID: uuid,
|
|
||||||
RefName: args[0],
|
|
||||||
OldCommitID: args[1],
|
|
||||||
NewCommitID: args[2],
|
|
||||||
}); err != nil {
|
|
||||||
fail("Internal error", "Fail to add update task '%s': %v", uuid, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,5 +145,63 @@ func runHookPostReceive(c *cli.Context) error {
|
||||||
fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
|
fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the environment setted on serv command
|
||||||
|
repoUser := os.Getenv(models.EnvRepoUsername)
|
||||||
|
repoUserSalt := os.Getenv(models.EnvRepoUserSalt)
|
||||||
|
isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
|
||||||
|
repoName := os.Getenv(models.EnvRepoName)
|
||||||
|
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||||
|
pusherName := os.Getenv(models.EnvPusherName)
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
scanner := bufio.NewScanner(os.Stdin)
|
||||||
|
for scanner.Scan() {
|
||||||
|
buf.Write(scanner.Bytes())
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
|
||||||
|
// TODO: support news feeds for wiki
|
||||||
|
if isWiki {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := bytes.Fields(scanner.Bytes())
|
||||||
|
if len(fields) != 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
oldCommitID := string(fields[0])
|
||||||
|
newCommitID := string(fields[1])
|
||||||
|
refFullName := string(fields[2])
|
||||||
|
|
||||||
|
if err := models.PushUpdate(models.PushUpdateOptions{
|
||||||
|
RefFullName: refFullName,
|
||||||
|
OldCommitID: oldCommitID,
|
||||||
|
NewCommitID: newCommitID,
|
||||||
|
PusherID: pusherID,
|
||||||
|
PusherName: pusherName,
|
||||||
|
RepoUserName: repoUser,
|
||||||
|
RepoName: repoName,
|
||||||
|
}); err != nil {
|
||||||
|
log.GitLogger.Error(2, "Update: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask for running deliver hook and test pull request tasks.
|
||||||
|
reqURL := setting.LocalURL + repoUser + "/" + repoName + "/tasks/trigger?branch=" +
|
||||||
|
strings.TrimPrefix(refFullName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUserSalt) + "&pusher=" + com.ToStr(pusherID)
|
||||||
|
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
||||||
|
|
||||||
|
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
}).Response()
|
||||||
|
if err == nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
if resp.StatusCode/100 != 2 {
|
||||||
|
log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.GitLogger.Error(2, "Failed to trigger task: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
76
cmd/serv.go
76
cmd/serv.go
|
@ -6,7 +6,6 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -15,22 +14,17 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/git"
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/base"
|
|
||||||
"code.gitea.io/gitea/modules/httplib"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"github.com/Unknwon/com"
|
"github.com/Unknwon/com"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
gouuid "github.com/satori/go.uuid"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
accessDenied = "Repository does not exist or you do not have access"
|
accessDenied = "Repository does not exist or you do not have access"
|
||||||
lfsAuthenticateVerb = "git-lfs-authenticate"
|
lfsAuthenticateVerb = "git-lfs-authenticate"
|
||||||
envUpdateTaskUUID = "GITEA_UUID"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdServ represents the available serv sub-command.
|
// CmdServ represents the available serv sub-command.
|
||||||
|
@ -96,52 +90,6 @@ func fail(userMessage, logMessage string, args ...interface{}) {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string, isWiki bool) {
|
|
||||||
task, err := models.GetUpdateTaskByUUID(uuid)
|
|
||||||
if err != nil {
|
|
||||||
if models.IsErrUpdateTaskNotExist(err) {
|
|
||||||
log.GitLogger.Trace("No update task is presented: %s", uuid)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.GitLogger.Fatal(2, "GetUpdateTaskByUUID: %v", err)
|
|
||||||
} else if err = models.DeleteUpdateTaskByUUID(uuid); err != nil {
|
|
||||||
log.GitLogger.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isWiki {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = models.PushUpdate(models.PushUpdateOptions{
|
|
||||||
RefFullName: task.RefName,
|
|
||||||
OldCommitID: task.OldCommitID,
|
|
||||||
NewCommitID: task.NewCommitID,
|
|
||||||
PusherID: user.ID,
|
|
||||||
PusherName: user.Name,
|
|
||||||
RepoUserName: repoUser.Name,
|
|
||||||
RepoName: reponame,
|
|
||||||
}); err != nil {
|
|
||||||
log.GitLogger.Error(2, "Update: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ask for running deliver hook and test pull request tasks.
|
|
||||||
reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
|
|
||||||
strings.TrimPrefix(task.RefName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
|
|
||||||
log.GitLogger.Trace("Trigger task: %s", reqURL)
|
|
||||||
|
|
||||||
resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
}).Response()
|
|
||||||
if err == nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
if resp.StatusCode/100 != 2 {
|
|
||||||
log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.GitLogger.Error(2, "Failed to trigger task: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func runServ(c *cli.Context) error {
|
func runServ(c *cli.Context) error {
|
||||||
if c.IsSet("config") {
|
if c.IsSet("config") {
|
||||||
setting.CustomConf = c.String("config")
|
setting.CustomConf = c.String("config")
|
||||||
|
@ -187,6 +135,7 @@ func runServ(c *cli.Context) error {
|
||||||
if len(rr) != 2 {
|
if len(rr) != 2 {
|
||||||
fail("Invalid repository path", "Invalid repository path: %v", args)
|
fail("Invalid repository path", "Invalid repository path: %v", args)
|
||||||
}
|
}
|
||||||
|
|
||||||
username := strings.ToLower(rr[0])
|
username := strings.ToLower(rr[0])
|
||||||
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
|
||||||
|
|
||||||
|
@ -196,6 +145,14 @@ func runServ(c *cli.Context) error {
|
||||||
reponame = reponame[:len(reponame)-5]
|
reponame = reponame[:len(reponame)-5]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Setenv(models.EnvRepoUsername, username)
|
||||||
|
if isWiki {
|
||||||
|
os.Setenv(models.EnvRepoIsWiki, "true")
|
||||||
|
} else {
|
||||||
|
os.Setenv(models.EnvRepoIsWiki, "false")
|
||||||
|
}
|
||||||
|
os.Setenv(models.EnvRepoName, reponame)
|
||||||
|
|
||||||
repoUser, err := models.GetUserByName(username)
|
repoUser, err := models.GetUserByName(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserNotExist(err) {
|
if models.IsErrUserNotExist(err) {
|
||||||
|
@ -204,6 +161,8 @@ func runServ(c *cli.Context) error {
|
||||||
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
|
fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
os.Setenv(models.EnvRepoUserSalt, repoUser.Salt)
|
||||||
|
|
||||||
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrRepoNotExist(err) {
|
if models.IsErrRepoNotExist(err) {
|
||||||
|
@ -286,7 +245,8 @@ func runServ(c *cli.Context) error {
|
||||||
user.Name, requestedMode, repoPath)
|
user.Name, requestedMode, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Setenv("GITEA_PUSHER_NAME", user.Name)
|
os.Setenv(models.EnvPusherName, user.Name)
|
||||||
|
os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,11 +283,6 @@ func runServ(c *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid := gouuid.NewV4().String()
|
|
||||||
os.Setenv(envUpdateTaskUUID, uuid)
|
|
||||||
// Keep the old env variable name for backward compability
|
|
||||||
os.Setenv("uuid", uuid)
|
|
||||||
|
|
||||||
// Special handle for Windows.
|
// Special handle for Windows.
|
||||||
if setting.IsWindows {
|
if setting.IsWindows {
|
||||||
verb = strings.Replace(verb, "-", " ", 1)
|
verb = strings.Replace(verb, "-", " ", 1)
|
||||||
|
@ -341,7 +296,6 @@ func runServ(c *cli.Context) error {
|
||||||
gitcmd = exec.Command(verb, repoPath)
|
gitcmd = exec.Command(verb, repoPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String())
|
|
||||||
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
|
os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
|
||||||
|
|
||||||
gitcmd.Dir = setting.RepoRootPath
|
gitcmd.Dir = setting.RepoRootPath
|
||||||
|
@ -352,10 +306,6 @@ func runServ(c *cli.Context) error {
|
||||||
fail("Internal error", "Failed to execute git command: %v", err)
|
fail("Internal error", "Failed to execute git command: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestedMode == models.AccessModeWrite {
|
|
||||||
handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update user key activity.
|
// Update user key activity.
|
||||||
if keyID > 0 {
|
if keyID > 0 {
|
||||||
key, err := models.GetPublicKeyByID(keyID)
|
key, err := models.GetPublicKeyByID(keyID)
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
|
|
||||||
"code.gitea.io/git"
|
|
||||||
"code.gitea.io/gitea/models"
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
// CmdUpdate represents the available update sub-command.
|
|
||||||
var CmdUpdate = cli.Command{
|
|
||||||
Name: "update",
|
|
||||||
Usage: "This command should only be called by Git hook",
|
|
||||||
Description: `Update get pushed info and insert into database`,
|
|
||||||
Action: runUpdate,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config, c",
|
|
||||||
Value: "custom/conf/app.ini",
|
|
||||||
Usage: "Custom configuration file path",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runUpdate(c *cli.Context) error {
|
|
||||||
if c.IsSet("config") {
|
|
||||||
setting.CustomConf = c.String("config")
|
|
||||||
}
|
|
||||||
|
|
||||||
setup("update.log")
|
|
||||||
|
|
||||||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
|
|
||||||
log.GitLogger.Trace("SSH_ORIGINAL_COMMAND is empty")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
args := c.Args()
|
|
||||||
if len(args) != 3 {
|
|
||||||
log.GitLogger.Fatal(2, "Arguments received are not equal to three")
|
|
||||||
} else if len(args[0]) == 0 {
|
|
||||||
log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use")
|
|
||||||
}
|
|
||||||
|
|
||||||
// protected branch check
|
|
||||||
branchName := strings.TrimPrefix(args[0], git.BranchPrefix)
|
|
||||||
repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
|
|
||||||
log.GitLogger.Trace("pushing to %d %v", repoID, branchName)
|
|
||||||
accessMode := models.ParseAccessMode(os.Getenv(models.ProtectedBranchAccessMode))
|
|
||||||
// skip admin or owner AccessMode
|
|
||||||
if accessMode == models.AccessModeWrite {
|
|
||||||
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
|
|
||||||
if err != nil {
|
|
||||||
log.GitLogger.Fatal(2, "retrieve protected branches information failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
if protectBranch != nil {
|
|
||||||
log.GitLogger.Fatal(2, "protected branches can not be pushed to")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task := models.UpdateTask{
|
|
||||||
UUID: os.Getenv("GITEA_UUID"),
|
|
||||||
RefName: args[0],
|
|
||||||
OldCommitID: args[1],
|
|
||||||
NewCommitID: args[2],
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := models.AddUpdateTask(&task); err != nil {
|
|
||||||
log.GitLogger.Fatal(2, "AddUpdateTask: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
1
main.go
1
main.go
|
@ -40,5 +40,4 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(4, "Failed to run app with %s: %v", os.Args, err)
|
log.Fatal(4, "Failed to run app with %s: %v", os.Args, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
-
|
|
||||||
id: 1
|
|
||||||
uuid: uuid1
|
|
||||||
ref_name: refName1
|
|
||||||
old_commit_id: oldCommitId1
|
|
||||||
new_commit_id: newCommitId1
|
|
||||||
|
|
||||||
-
|
|
||||||
id: 2
|
|
||||||
uuid: uuid2
|
|
||||||
ref_name: refName2
|
|
||||||
old_commit_id: oldCommitId2
|
|
||||||
new_commit_id: newCommitId2
|
|
||||||
|
|
||||||
-
|
|
||||||
id: 3
|
|
||||||
uuid: uuid3
|
|
||||||
ref_name: refName3
|
|
||||||
old_commit_id: oldCommitId3
|
|
||||||
new_commit_id: newCommitId3
|
|
|
@ -100,7 +100,6 @@ func init() {
|
||||||
new(Release),
|
new(Release),
|
||||||
new(LoginSource),
|
new(LoginSource),
|
||||||
new(Webhook),
|
new(Webhook),
|
||||||
new(UpdateTask),
|
|
||||||
new(HookTask),
|
new(HookTask),
|
||||||
new(Team),
|
new(Team),
|
||||||
new(OrgUser),
|
new(OrgUser),
|
||||||
|
@ -316,7 +315,6 @@ func GetStatistic() (stats Statistic) {
|
||||||
stats.Counter.Label, _ = x.Count(new(Label))
|
stats.Counter.Label, _ = x.Count(new(Label))
|
||||||
stats.Counter.HookTask, _ = x.Count(new(HookTask))
|
stats.Counter.HookTask, _ = x.Count(new(HookTask))
|
||||||
stats.Counter.Team, _ = x.Count(new(Team))
|
stats.Counter.Team, _ = x.Count(new(Team))
|
||||||
stats.Counter.UpdateTask, _ = x.Count(new(UpdateTask))
|
|
||||||
stats.Counter.Attachment, _ = x.Count(new(Attachment))
|
stats.Counter.Attachment, _ = x.Count(new(Attachment))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,40 +15,15 @@ import (
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateTask defines an UpdateTask
|
// env keys for git hooks need
|
||||||
type UpdateTask struct {
|
const (
|
||||||
ID int64 `xorm:"pk autoincr"`
|
EnvRepoName = "GITEA_REPO_NAME"
|
||||||
UUID string `xorm:"index"`
|
EnvRepoUsername = "GITEA_REPO_USER_NAME"
|
||||||
RefName string
|
EnvRepoUserSalt = "GITEA_REPO_USER_SALT"
|
||||||
OldCommitID string
|
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
|
||||||
NewCommitID string
|
EnvPusherName = "GITEA_PUSHER_NAME"
|
||||||
}
|
EnvPusherID = "GITEA_PUSHER_ID"
|
||||||
|
)
|
||||||
// AddUpdateTask adds an UpdateTask
|
|
||||||
func AddUpdateTask(task *UpdateTask) error {
|
|
||||||
_, err := x.Insert(task)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUpdateTaskByUUID returns update task by given UUID.
|
|
||||||
func GetUpdateTaskByUUID(uuid string) (*UpdateTask, error) {
|
|
||||||
task := &UpdateTask{
|
|
||||||
UUID: uuid,
|
|
||||||
}
|
|
||||||
has, err := x.Get(task)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrUpdateTaskNotExist{uuid}
|
|
||||||
}
|
|
||||||
return task, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteUpdateTaskByUUID deletes an UpdateTask from the database
|
|
||||||
func DeleteUpdateTaskByUUID(uuid string) error {
|
|
||||||
_, err := x.Delete(&UpdateTask{UUID: uuid})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
// CommitToPushCommit transforms a git.Commit to PushCommit type.
|
||||||
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
func CommitToPushCommit(commit *git.Commit) *PushCommit {
|
||||||
|
|
|
@ -14,40 +14,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAddUpdateTask(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
|
||||||
task := &UpdateTask{
|
|
||||||
UUID: "uuid4",
|
|
||||||
RefName: "refName4",
|
|
||||||
OldCommitID: "oldCommitId4",
|
|
||||||
NewCommitID: "newCommitId4",
|
|
||||||
}
|
|
||||||
assert.NoError(t, AddUpdateTask(task))
|
|
||||||
AssertExistsAndLoadBean(t, task)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetUpdateTaskByUUID(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
|
||||||
task, err := GetUpdateTaskByUUID("uuid1")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, "uuid1", task.UUID)
|
|
||||||
assert.Equal(t, "refName1", task.RefName)
|
|
||||||
assert.Equal(t, "oldCommitId1", task.OldCommitID)
|
|
||||||
assert.Equal(t, "newCommitId1", task.NewCommitID)
|
|
||||||
|
|
||||||
_, err = GetUpdateTaskByUUID("invalid")
|
|
||||||
assert.Error(t, err)
|
|
||||||
assert.True(t, IsErrUpdateTaskNotExist(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteUpdateTaskByUUID(t *testing.T) {
|
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
|
||||||
assert.NoError(t, DeleteUpdateTaskByUUID("uuid1"))
|
|
||||||
AssertNotExistsBean(t, &UpdateTask{UUID: "uuid1"})
|
|
||||||
|
|
||||||
assert.NoError(t, DeleteUpdateTaskByUUID("invalid"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCommitToPushCommit(t *testing.T) {
|
func TestCommitToPushCommit(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
sig := &git.Signature{
|
sig := &git.Signature{
|
||||||
|
|
|
@ -8,20 +8,15 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/git"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
@ -59,7 +54,7 @@ func HTTP(ctx *context.Context) {
|
||||||
isWiki := false
|
isWiki := false
|
||||||
if strings.HasSuffix(reponame, ".wiki") {
|
if strings.HasSuffix(reponame, ".wiki") {
|
||||||
isWiki = true
|
isWiki = true
|
||||||
reponame = reponame[:len(reponame) - 5]
|
reponame = reponame[:len(reponame)-5]
|
||||||
}
|
}
|
||||||
|
|
||||||
repoUser, err := models.GetUserByName(username)
|
repoUser, err := models.GetUserByName(username)
|
||||||
|
@ -89,6 +84,7 @@ func HTTP(ctx *context.Context) {
|
||||||
authUser *models.User
|
authUser *models.User
|
||||||
authUsername string
|
authUsername string
|
||||||
authPasswd string
|
authPasswd string
|
||||||
|
environ []string
|
||||||
)
|
)
|
||||||
|
|
||||||
// check access
|
// check access
|
||||||
|
@ -182,94 +178,42 @@ func HTTP(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
callback := func(rpc string, input []byte) {
|
environ = []string{
|
||||||
if rpc != "receive-pack" || isWiki {
|
models.EnvRepoUsername + "=" + username,
|
||||||
return
|
models.EnvRepoName + "=" + reponame,
|
||||||
|
models.EnvRepoUserSalt + "=" + repoUser.Salt,
|
||||||
|
models.EnvPusherName + "=" + authUser.Name,
|
||||||
|
models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
|
||||||
|
models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
|
||||||
}
|
}
|
||||||
|
if isWiki {
|
||||||
var lastLine int64
|
environ = append(environ, models.EnvRepoIsWiki+"=true")
|
||||||
for {
|
} else {
|
||||||
head := input[lastLine: lastLine + 2]
|
environ = append(environ, models.EnvRepoIsWiki+"=false")
|
||||||
if head[0] == '0' && head[1] == '0' {
|
|
||||||
size, err := strconv.ParseInt(string(input[lastLine + 2:lastLine + 4]), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(4, "%v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if size == 0 {
|
|
||||||
//fmt.Println(string(input[lastLine:]))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
line := input[lastLine: lastLine + size]
|
|
||||||
idx := bytes.IndexRune(line, '\000')
|
|
||||||
if idx > -1 {
|
|
||||||
line = line[:idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := strings.Fields(string(line))
|
|
||||||
if len(fields) >= 3 {
|
|
||||||
oldCommitID := fields[0][4:]
|
|
||||||
newCommitID := fields[1]
|
|
||||||
refFullName := fields[2]
|
|
||||||
|
|
||||||
// FIXME: handle error.
|
|
||||||
if err = models.PushUpdate(models.PushUpdateOptions{
|
|
||||||
RefFullName: refFullName,
|
|
||||||
OldCommitID: oldCommitID,
|
|
||||||
NewCommitID: newCommitID,
|
|
||||||
PusherID: authUser.ID,
|
|
||||||
PusherName: authUser.Name,
|
|
||||||
RepoUserName: username,
|
|
||||||
RepoName: reponame,
|
|
||||||
}); err == nil {
|
|
||||||
go models.AddTestPullRequestTask(authUser, repo.ID, strings.TrimPrefix(refFullName, git.BranchPrefix), true)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
lastLine = lastLine + size
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
params := make(map[string]string)
|
|
||||||
|
|
||||||
if askAuth {
|
|
||||||
params[models.ProtectedBranchUserID] = fmt.Sprintf("%d", authUser.ID)
|
|
||||||
if err == nil {
|
|
||||||
params[models.ProtectedBranchAccessMode] = accessMode.String()
|
|
||||||
}
|
|
||||||
params[models.ProtectedBranchRepoID] = fmt.Sprintf("%d", repo.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPBackend(ctx, &serviceConfig{
|
HTTPBackend(ctx, &serviceConfig{
|
||||||
UploadPack: true,
|
UploadPack: true,
|
||||||
ReceivePack: true,
|
ReceivePack: true,
|
||||||
Params: params,
|
Env: environ,
|
||||||
OnSucceed: callback,
|
|
||||||
})(ctx.Resp, ctx.Req.Request)
|
})(ctx.Resp, ctx.Req.Request)
|
||||||
|
|
||||||
runtime.GC()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceConfig struct {
|
type serviceConfig struct {
|
||||||
UploadPack bool
|
UploadPack bool
|
||||||
ReceivePack bool
|
ReceivePack bool
|
||||||
Params map[string]string
|
Env []string
|
||||||
OnSucceed func(rpc string, input []byte)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceHandler struct {
|
type serviceHandler struct {
|
||||||
cfg *serviceConfig
|
cfg *serviceConfig
|
||||||
w http.ResponseWriter
|
w http.ResponseWriter
|
||||||
r *http.Request
|
r *http.Request
|
||||||
dir string
|
dir string
|
||||||
file string
|
file string
|
||||||
|
environ []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *serviceHandler) setHeaderNoCache() {
|
func (h *serviceHandler) setHeaderNoCache() {
|
||||||
|
@ -278,42 +222,6 @@ func (h *serviceHandler) setHeaderNoCache() {
|
||||||
h.w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
|
h.w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *serviceHandler) getBranch(input []byte) string {
|
|
||||||
var lastLine int64
|
|
||||||
var branchName string
|
|
||||||
for {
|
|
||||||
head := input[lastLine : lastLine+2]
|
|
||||||
if head[0] == '0' && head[1] == '0' {
|
|
||||||
size, err := strconv.ParseInt(string(input[lastLine+2:lastLine+4]), 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(4, "%v", err)
|
|
||||||
return branchName
|
|
||||||
}
|
|
||||||
|
|
||||||
if size == 0 {
|
|
||||||
//fmt.Println(string(input[lastLine:]))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
line := input[lastLine : lastLine+size]
|
|
||||||
idx := bytes.IndexRune(line, '\000')
|
|
||||||
if idx > -1 {
|
|
||||||
line = line[:idx]
|
|
||||||
}
|
|
||||||
|
|
||||||
fields := strings.Fields(string(line))
|
|
||||||
if len(fields) >= 3 {
|
|
||||||
refFullName := fields[2]
|
|
||||||
branchName = strings.TrimPrefix(refFullName, git.BranchPrefix)
|
|
||||||
}
|
|
||||||
lastLine = lastLine + size
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return branchName
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *serviceHandler) setHeaderCacheForever() {
|
func (h *serviceHandler) setHeaderCacheForever() {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
expires := now + 31536000
|
expires := now + 31536000
|
||||||
|
@ -370,7 +278,7 @@ func gitCommand(dir string, args ...string) []byte {
|
||||||
|
|
||||||
func getGitConfig(option, dir string) string {
|
func getGitConfig(option, dir string) string {
|
||||||
out := string(gitCommand(dir, "config", option))
|
out := string(gitCommand(dir, "config", option))
|
||||||
return out[0: len(out) - 1]
|
return out[0 : len(out)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func getConfigSetting(service, dir string) bool {
|
func getConfigSetting(service, dir string) bool {
|
||||||
|
@ -414,13 +322,8 @@ func serviceRPC(h serviceHandler, service string) {
|
||||||
|
|
||||||
h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service))
|
h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service))
|
||||||
|
|
||||||
var (
|
var err error
|
||||||
reqBody = h.r.Body
|
var reqBody = h.r.Body
|
||||||
input []byte
|
|
||||||
br io.Reader
|
|
||||||
err error
|
|
||||||
branchName string
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handle GZIP.
|
// Handle GZIP.
|
||||||
if h.r.Header.Get("Content-Encoding") == "gzip" {
|
if h.r.Header.Get("Content-Encoding") == "gzip" {
|
||||||
|
@ -432,52 +335,23 @@ func serviceRPC(h serviceHandler, service string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.cfg.OnSucceed != nil {
|
// set this for allow pre-receive and post-receive execute
|
||||||
input, err = ioutil.ReadAll(reqBody)
|
h.environ = append(h.environ, "SSH_ORIGINAL_COMMAND="+service)
|
||||||
if err != nil {
|
|
||||||
log.GitLogger.Error(2, "fail to read request body: %v", err)
|
|
||||||
h.w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
branchName = h.getBranch(input)
|
|
||||||
br = bytes.NewReader(input)
|
|
||||||
} else {
|
|
||||||
br = reqBody
|
|
||||||
}
|
|
||||||
|
|
||||||
// check protected branch
|
|
||||||
repoID, _ := strconv.ParseInt(h.cfg.Params[models.ProtectedBranchRepoID], 10, 64)
|
|
||||||
accessMode := models.ParseAccessMode(h.cfg.Params[models.ProtectedBranchAccessMode])
|
|
||||||
// skip admin or owner AccessMode
|
|
||||||
if accessMode == models.AccessModeWrite {
|
|
||||||
protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
|
|
||||||
if err != nil {
|
|
||||||
log.GitLogger.Error(2, "fail to get protected branch information: %v", err)
|
|
||||||
h.w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if protectBranch != nil {
|
|
||||||
log.GitLogger.Error(2, "protected branches can not be pushed to")
|
|
||||||
h.w.WriteHeader(http.StatusForbidden)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var stderr bytes.Buffer
|
||||||
cmd := exec.Command("git", service, "--stateless-rpc", h.dir)
|
cmd := exec.Command("git", service, "--stateless-rpc", h.dir)
|
||||||
cmd.Dir = h.dir
|
cmd.Dir = h.dir
|
||||||
|
if service == "receive-pack" {
|
||||||
|
cmd.Env = append(os.Environ(), h.environ...)
|
||||||
|
}
|
||||||
cmd.Stdout = h.w
|
cmd.Stdout = h.w
|
||||||
cmd.Stdin = br
|
cmd.Stdin = reqBody
|
||||||
|
cmd.Stderr = &stderr
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.GitLogger.Error(2, "fail to serve RPC(%s): %v", service, err)
|
log.GitLogger.Error(2, "fail to serve RPC(%s): %v - %v", service, err, stderr)
|
||||||
h.w.WriteHeader(http.StatusInternalServerError)
|
h.w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.cfg.OnSucceed != nil {
|
|
||||||
h.cfg.OnSucceed(service, input)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceUploadPack(h serviceHandler) {
|
func serviceUploadPack(h serviceHandler) {
|
||||||
|
@ -501,7 +375,7 @@ func updateServerInfo(dir string) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func packetWrite(str string) []byte {
|
func packetWrite(str string) []byte {
|
||||||
s := strconv.FormatInt(int64(len(str) + 4), 16)
|
s := strconv.FormatInt(int64(len(str)+4), 16)
|
||||||
if len(s)%4 != 0 {
|
if len(s)%4 != 0 {
|
||||||
s = strings.Repeat("0", 4-len(s)%4) + s
|
s = strings.Repeat("0", 4-len(s)%4) + s
|
||||||
}
|
}
|
||||||
|
@ -593,7 +467,7 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
route.handler(serviceHandler{cfg, w, r, dir, file})
|
route.handler(serviceHandler{cfg, w, r, dir, file, cfg.Env})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue