From cd1821a7e292b05e04fcc2a969b42d06ab512849 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Feb 2017 22:54:40 +0800 Subject: [PATCH] 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 --- cmd/hook.go | 135 +++++++++++++++++++--- cmd/serv.go | 76 +++---------- cmd/update.go | 83 -------------- main.go | 1 - models/fixtures/update_task.yml | 20 ---- models/models.go | 2 - models/update.go | 43 ++----- models/update_test.go | 34 ------ routers/repo/http.go | 196 ++++++-------------------------- 9 files changed, 175 insertions(+), 415 deletions(-) delete mode 100644 cmd/update.go delete mode 100644 models/fixtures/update_task.yml diff --git a/cmd/hook.go b/cmd/hook.go index 15ad74f8e0..a89c3741bf 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -5,11 +5,22 @@ package cmd import ( + "bufio" + "bytes" + "crypto/tls" "fmt" "os" + "strconv" + "strings" + "code.gitea.io/git" "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" ) @@ -57,10 +68,59 @@ func runHookPreReceive(c *cli.Context) error { if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { return nil } + if err := setup("hooks/pre-receive.log"); err != nil { 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 } @@ -73,23 +133,6 @@ func runHookUpdate(c *cli.Context) error { 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 } @@ -102,5 +145,63 @@ func runHookPostReceive(c *cli.Context) error { 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 } diff --git a/cmd/serv.go b/cmd/serv.go index 141a58a2ac..5b1caf4d34 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -6,7 +6,6 @@ package cmd import ( - "crypto/tls" "encoding/json" "fmt" "os" @@ -15,22 +14,17 @@ import ( "strings" "time" - "code.gitea.io/git" "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/dgrijalva/jwt-go" - gouuid "github.com/satori/go.uuid" "github.com/urfave/cli" ) const ( accessDenied = "Repository does not exist or you do not have access" lfsAuthenticateVerb = "git-lfs-authenticate" - envUpdateTaskUUID = "GITEA_UUID" ) // CmdServ represents the available serv sub-command. @@ -96,52 +90,6 @@ func fail(userMessage, logMessage string, args ...interface{}) { 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 { if c.IsSet("config") { setting.CustomConf = c.String("config") @@ -187,6 +135,7 @@ func runServ(c *cli.Context) error { if len(rr) != 2 { fail("Invalid repository path", "Invalid repository path: %v", args) } + username := strings.ToLower(rr[0]) reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git")) @@ -196,6 +145,14 @@ func runServ(c *cli.Context) error { 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) if err != nil { 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) } + os.Setenv(models.EnvRepoUserSalt, repoUser.Salt) + repo, err := models.GetRepositoryByName(repoUser.ID, reponame) if err != nil { if models.IsErrRepoNotExist(err) { @@ -286,7 +245,8 @@ func runServ(c *cli.Context) error { 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 } - 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. if setting.IsWindows { verb = strings.Replace(verb, "-", " ", 1) @@ -341,7 +296,6 @@ func runServ(c *cli.Context) error { gitcmd = exec.Command(verb, repoPath) } - os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String()) os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID)) gitcmd.Dir = setting.RepoRootPath @@ -352,10 +306,6 @@ func runServ(c *cli.Context) error { fail("Internal error", "Failed to execute git command: %v", err) } - if requestedMode == models.AccessModeWrite { - handleUpdateTask(uuid, user, repoUser, reponame, isWiki) - } - // Update user key activity. if keyID > 0 { key, err := models.GetPublicKeyByID(keyID) diff --git a/cmd/update.go b/cmd/update.go deleted file mode 100644 index 58e60493d0..0000000000 --- a/cmd/update.go +++ /dev/null @@ -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 -} diff --git a/main.go b/main.go index b7e8244b4b..6576bf3e89 100644 --- a/main.go +++ b/main.go @@ -40,5 +40,4 @@ func main() { if err != nil { log.Fatal(4, "Failed to run app with %s: %v", os.Args, err) } - } diff --git a/models/fixtures/update_task.yml b/models/fixtures/update_task.yml deleted file mode 100644 index ffcd5fd6fa..0000000000 --- a/models/fixtures/update_task.yml +++ /dev/null @@ -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 diff --git a/models/models.go b/models/models.go index 0840b4600e..29428dba04 100644 --- a/models/models.go +++ b/models/models.go @@ -100,7 +100,6 @@ func init() { new(Release), new(LoginSource), new(Webhook), - new(UpdateTask), new(HookTask), new(Team), new(OrgUser), @@ -316,7 +315,6 @@ func GetStatistic() (stats Statistic) { stats.Counter.Label, _ = x.Count(new(Label)) stats.Counter.HookTask, _ = x.Count(new(HookTask)) stats.Counter.Team, _ = x.Count(new(Team)) - stats.Counter.UpdateTask, _ = x.Count(new(UpdateTask)) stats.Counter.Attachment, _ = x.Count(new(Attachment)) return } diff --git a/models/update.go b/models/update.go index 677a9bda31..3cb0608594 100644 --- a/models/update.go +++ b/models/update.go @@ -15,40 +15,15 @@ import ( "code.gitea.io/gitea/modules/log" ) -// UpdateTask defines an UpdateTask -type UpdateTask struct { - ID int64 `xorm:"pk autoincr"` - UUID string `xorm:"index"` - RefName string - OldCommitID string - NewCommitID string -} - -// 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 -} +// env keys for git hooks need +const ( + EnvRepoName = "GITEA_REPO_NAME" + EnvRepoUsername = "GITEA_REPO_USER_NAME" + EnvRepoUserSalt = "GITEA_REPO_USER_SALT" + EnvRepoIsWiki = "GITEA_REPO_IS_WIKI" + EnvPusherName = "GITEA_PUSHER_NAME" + EnvPusherID = "GITEA_PUSHER_ID" +) // CommitToPushCommit transforms a git.Commit to PushCommit type. func CommitToPushCommit(commit *git.Commit) *PushCommit { diff --git a/models/update_test.go b/models/update_test.go index db9d7b94b9..ae95a9641b 100644 --- a/models/update_test.go +++ b/models/update_test.go @@ -14,40 +14,6 @@ import ( "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) { now := time.Now() sig := &git.Signature{ diff --git a/routers/repo/http.go b/routers/repo/http.go index 4c143f66d0..dc29651847 100644 --- a/routers/repo/http.go +++ b/routers/repo/http.go @@ -8,20 +8,15 @@ import ( "bytes" "compress/gzip" "fmt" - "io" - "io/ioutil" "net/http" "os" "os/exec" "path" "regexp" - "runtime" "strconv" "strings" "time" - "code.gitea.io/git" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -59,7 +54,7 @@ func HTTP(ctx *context.Context) { isWiki := false if strings.HasSuffix(reponame, ".wiki") { isWiki = true - reponame = reponame[:len(reponame) - 5] + reponame = reponame[:len(reponame)-5] } repoUser, err := models.GetUserByName(username) @@ -89,6 +84,7 @@ func HTTP(ctx *context.Context) { authUser *models.User authUsername string authPasswd string + environ []string ) // check access @@ -182,94 +178,42 @@ func HTTP(ctx *context.Context) { } } } - } - callback := func(rpc string, input []byte) { - if rpc != "receive-pack" || isWiki { - return + environ = []string{ + models.EnvRepoUsername + "=" + username, + 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), } - - var lastLine int64 - 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 - } - - 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 - } + if isWiki { + environ = append(environ, models.EnvRepoIsWiki+"=true") + } else { + environ = append(environ, models.EnvRepoIsWiki+"=false") } } - 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{ UploadPack: true, ReceivePack: true, - Params: params, - OnSucceed: callback, + Env: environ, })(ctx.Resp, ctx.Req.Request) - - runtime.GC() } type serviceConfig struct { UploadPack bool ReceivePack bool - Params map[string]string - OnSucceed func(rpc string, input []byte) + Env []string } type serviceHandler struct { - cfg *serviceConfig - w http.ResponseWriter - r *http.Request - dir string - file string + cfg *serviceConfig + w http.ResponseWriter + r *http.Request + dir string + file string + environ []string } 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") } -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() { now := time.Now().Unix() expires := now + 31536000 @@ -370,7 +278,7 @@ func gitCommand(dir string, args ...string) []byte { func getGitConfig(option, dir string) string { out := string(gitCommand(dir, "config", option)) - return out[0: len(out) - 1] + return out[0 : len(out)-1] } 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)) - var ( - reqBody = h.r.Body - input []byte - br io.Reader - err error - branchName string - ) + var err error + var reqBody = h.r.Body // Handle GZIP. if h.r.Header.Get("Content-Encoding") == "gzip" { @@ -432,52 +335,23 @@ func serviceRPC(h serviceHandler, service string) { } } - if h.cfg.OnSucceed != nil { - input, err = ioutil.ReadAll(reqBody) - 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 - } - } + // set this for allow pre-receive and post-receive execute + h.environ = append(h.environ, "SSH_ORIGINAL_COMMAND="+service) + var stderr bytes.Buffer cmd := exec.Command("git", service, "--stateless-rpc", h.dir) cmd.Dir = h.dir + if service == "receive-pack" { + cmd.Env = append(os.Environ(), h.environ...) + } cmd.Stdout = h.w - cmd.Stdin = br + cmd.Stdin = reqBody + cmd.Stderr = &stderr 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) return } - - if h.cfg.OnSucceed != nil { - h.cfg.OnSucceed(service, input) - } } func serviceUploadPack(h serviceHandler) { @@ -501,7 +375,7 @@ func updateServerInfo(dir 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 { s = strings.Repeat("0", 4-len(s)%4) + s } @@ -593,7 +467,7 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc { return } - route.handler(serviceHandler{cfg, w, r, dir, file}) + route.handler(serviceHandler{cfg, w, r, dir, file, cfg.Env}) return } }