* 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 importtags/v1.1.0
@@ -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 | |||
} |
@@ -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) |
@@ -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 | |||
} |
@@ -40,5 +40,4 @@ func main() { | |||
if err != nil { | |||
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(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 | |||
} |
@@ -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 { |
@@ -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{ |
@@ -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 | |||
} | |||
} | |||
} | |||
params := make(map[string]string) | |||
if askAuth { | |||
params[models.ProtectedBranchUserID] = fmt.Sprintf("%d", authUser.ID) | |||
if err == nil { | |||
params[models.ProtectedBranchAccessMode] = accessMode.String() | |||
if isWiki { | |||
environ = append(environ, models.EnvRepoIsWiki+"=true") | |||
} else { | |||
environ = append(environ, models.EnvRepoIsWiki+"=false") | |||
} | |||
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 | |||
} | |||
} |