Browse Source

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
Lunny Xiao 3 years ago
parent
commit
cd1821a7e2
9 changed files with 175 additions and 415 deletions
  1. 118 17
      cmd/hook.go
  2. 13 63
      cmd/serv.go
  3. 0 83
      cmd/update.go
  4. 0 1
      main.go
  5. 0 20
      models/fixtures/update_task.yml
  6. 0 2
      models/models.go
  7. 9 34
      models/update.go
  8. 0 34
      models/update_test.go
  9. 35 161
      routers/repo/http.go

+ 118 - 17
cmd/hook.go

@@ -5,11 +5,22 @@
5 5
 package cmd
6 6
 
7 7
 import (
8
+	"bufio"
9
+	"bytes"
10
+	"crypto/tls"
8 11
 	"fmt"
9 12
 	"os"
13
+	"strconv"
14
+	"strings"
10 15
 
16
+	"code.gitea.io/git"
11 17
 	"code.gitea.io/gitea/models"
18
+	"code.gitea.io/gitea/modules/base"
19
+	"code.gitea.io/gitea/modules/httplib"
20
+	"code.gitea.io/gitea/modules/log"
21
+	"code.gitea.io/gitea/modules/setting"
12 22
 
23
+	"github.com/Unknwon/com"
13 24
 	"github.com/urfave/cli"
14 25
 )
15 26
 
@@ -57,10 +68,59 @@ func runHookPreReceive(c *cli.Context) error {
57 68
 	if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
58 69
 		return nil
59 70
 	}
71
+
60 72
 	if err := setup("hooks/pre-receive.log"); err != nil {
61 73
 		fail("Hook pre-receive init failed", fmt.Sprintf("setup: %v", err))
62 74
 	}
63 75
 
76
+	// the environment setted on serv command
77
+	repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
78
+	isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
79
+
80
+	buf := bytes.NewBuffer(nil)
81
+	scanner := bufio.NewScanner(os.Stdin)
82
+	for scanner.Scan() {
83
+		buf.Write(scanner.Bytes())
84
+		buf.WriteByte('\n')
85
+
86
+		// TODO: support news feeds for wiki
87
+		if isWiki {
88
+			continue
89
+		}
90
+
91
+		fields := bytes.Fields(scanner.Bytes())
92
+		if len(fields) != 3 {
93
+			continue
94
+		}
95
+
96
+		oldCommitID := string(fields[0])
97
+		newCommitID := string(fields[1])
98
+		refFullName := string(fields[2])
99
+
100
+		branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
101
+		protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
102
+		if err != nil {
103
+			log.GitLogger.Fatal(2, "retrieve protected branches information failed")
104
+		}
105
+
106
+		if protectBranch != nil {
107
+			fail(fmt.Sprintf("protected branch %s can not be pushed to", branchName), "")
108
+		}
109
+
110
+		// check and deletion
111
+		if newCommitID == git.EmptySHA {
112
+			fail(fmt.Sprintf("Branch '%s' is protected from deletion", branchName), "")
113
+		}
114
+
115
+		// Check force push
116
+		output, err := git.NewCommand("rev-list", oldCommitID, "^"+newCommitID).Run()
117
+		if err != nil {
118
+			fail("Internal error", "Fail to detect force push: %v", err)
119
+		} else if len(output) > 0 {
120
+			fail(fmt.Sprintf("Branch '%s' is protected from force push", branchName), "")
121
+		}
122
+	}
123
+
64 124
 	return nil
65 125
 }
66 126
 
@@ -73,23 +133,6 @@ func runHookUpdate(c *cli.Context) error {
73 133
 		fail("Hook update init failed", fmt.Sprintf("setup: %v", err))
74 134
 	}
75 135
 
76
-	args := c.Args()
77
-	if len(args) != 3 {
78
-		fail("Arguments received are not equal to three", "Arguments received are not equal to three")
79
-	} else if len(args[0]) == 0 {
80
-		fail("First argument 'refName' is empty", "First argument 'refName' is empty")
81
-	}
82
-
83
-	uuid := os.Getenv(envUpdateTaskUUID)
84
-	if err := models.AddUpdateTask(&models.UpdateTask{
85
-		UUID:        uuid,
86
-		RefName:     args[0],
87
-		OldCommitID: args[1],
88
-		NewCommitID: args[2],
89
-	}); err != nil {
90
-		fail("Internal error", "Fail to add update task '%s': %v", uuid, err)
91
-	}
92
-
93 136
 	return nil
94 137
 }
95 138
 
@@ -102,5 +145,63 @@ func runHookPostReceive(c *cli.Context) error {
102 145
 		fail("Hook post-receive init failed", fmt.Sprintf("setup: %v", err))
103 146
 	}
104 147
 
148
+	// the environment setted on serv command
149
+	repoUser := os.Getenv(models.EnvRepoUsername)
150
+	repoUserSalt := os.Getenv(models.EnvRepoUserSalt)
151
+	isWiki := (os.Getenv(models.EnvRepoIsWiki) == "true")
152
+	repoName := os.Getenv(models.EnvRepoName)
153
+	pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
154
+	pusherName := os.Getenv(models.EnvPusherName)
155
+
156
+	buf := bytes.NewBuffer(nil)
157
+	scanner := bufio.NewScanner(os.Stdin)
158
+	for scanner.Scan() {
159
+		buf.Write(scanner.Bytes())
160
+		buf.WriteByte('\n')
161
+
162
+		// TODO: support news feeds for wiki
163
+		if isWiki {
164
+			continue
165
+		}
166
+
167
+		fields := bytes.Fields(scanner.Bytes())
168
+		if len(fields) != 3 {
169
+			continue
170
+		}
171
+
172
+		oldCommitID := string(fields[0])
173
+		newCommitID := string(fields[1])
174
+		refFullName := string(fields[2])
175
+
176
+		if err := models.PushUpdate(models.PushUpdateOptions{
177
+			RefFullName:  refFullName,
178
+			OldCommitID:  oldCommitID,
179
+			NewCommitID:  newCommitID,
180
+			PusherID:     pusherID,
181
+			PusherName:   pusherName,
182
+			RepoUserName: repoUser,
183
+			RepoName:     repoName,
184
+		}); err != nil {
185
+			log.GitLogger.Error(2, "Update: %v", err)
186
+		}
187
+
188
+		// Ask for running deliver hook and test pull request tasks.
189
+		reqURL := setting.LocalURL + repoUser + "/" + repoName + "/tasks/trigger?branch=" +
190
+			strings.TrimPrefix(refFullName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUserSalt) + "&pusher=" + com.ToStr(pusherID)
191
+		log.GitLogger.Trace("Trigger task: %s", reqURL)
192
+
193
+		resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
194
+			InsecureSkipVerify: true,
195
+		}).Response()
196
+		if err == nil {
197
+			resp.Body.Close()
198
+			if resp.StatusCode/100 != 2 {
199
+				log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
200
+			}
201
+		} else {
202
+			log.GitLogger.Error(2, "Failed to trigger task: %v", err)
203
+		}
204
+	}
205
+
105 206
 	return nil
106 207
 }

+ 13 - 63
cmd/serv.go

@@ -6,7 +6,6 @@
6 6
 package cmd
7 7
 
8 8
 import (
9
-	"crypto/tls"
10 9
 	"encoding/json"
11 10
 	"fmt"
12 11
 	"os"
@@ -15,22 +14,17 @@ import (
15 14
 	"strings"
16 15
 	"time"
17 16
 
18
-	"code.gitea.io/git"
19 17
 	"code.gitea.io/gitea/models"
20
-	"code.gitea.io/gitea/modules/base"
21
-	"code.gitea.io/gitea/modules/httplib"
22 18
 	"code.gitea.io/gitea/modules/log"
23 19
 	"code.gitea.io/gitea/modules/setting"
24 20
 	"github.com/Unknwon/com"
25 21
 	"github.com/dgrijalva/jwt-go"
26
-	gouuid "github.com/satori/go.uuid"
27 22
 	"github.com/urfave/cli"
28 23
 )
29 24
 
30 25
 const (
31 26
 	accessDenied        = "Repository does not exist or you do not have access"
32 27
 	lfsAuthenticateVerb = "git-lfs-authenticate"
33
-	envUpdateTaskUUID   = "GITEA_UUID"
34 28
 )
35 29
 
36 30
 // CmdServ represents the available serv sub-command.
@@ -96,52 +90,6 @@ func fail(userMessage, logMessage string, args ...interface{}) {
96 90
 	os.Exit(1)
97 91
 }
98 92
 
99
-func handleUpdateTask(uuid string, user, repoUser *models.User, reponame string, isWiki bool) {
100
-	task, err := models.GetUpdateTaskByUUID(uuid)
101
-	if err != nil {
102
-		if models.IsErrUpdateTaskNotExist(err) {
103
-			log.GitLogger.Trace("No update task is presented: %s", uuid)
104
-			return
105
-		}
106
-		log.GitLogger.Fatal(2, "GetUpdateTaskByUUID: %v", err)
107
-	} else if err = models.DeleteUpdateTaskByUUID(uuid); err != nil {
108
-		log.GitLogger.Fatal(2, "DeleteUpdateTaskByUUID: %v", err)
109
-	}
110
-
111
-	if isWiki {
112
-		return
113
-	}
114
-
115
-	if err = models.PushUpdate(models.PushUpdateOptions{
116
-		RefFullName:  task.RefName,
117
-		OldCommitID:  task.OldCommitID,
118
-		NewCommitID:  task.NewCommitID,
119
-		PusherID:     user.ID,
120
-		PusherName:   user.Name,
121
-		RepoUserName: repoUser.Name,
122
-		RepoName:     reponame,
123
-	}); err != nil {
124
-		log.GitLogger.Error(2, "Update: %v", err)
125
-	}
126
-
127
-	// Ask for running deliver hook and test pull request tasks.
128
-	reqURL := setting.LocalURL + repoUser.Name + "/" + reponame + "/tasks/trigger?branch=" +
129
-		strings.TrimPrefix(task.RefName, git.BranchPrefix) + "&secret=" + base.EncodeMD5(repoUser.Salt) + "&pusher=" + com.ToStr(user.ID)
130
-	log.GitLogger.Trace("Trigger task: %s", reqURL)
131
-
132
-	resp, err := httplib.Head(reqURL).SetTLSClientConfig(&tls.Config{
133
-		InsecureSkipVerify: true,
134
-	}).Response()
135
-	if err == nil {
136
-		resp.Body.Close()
137
-		if resp.StatusCode/100 != 2 {
138
-			log.GitLogger.Error(2, "Failed to trigger task: not 2xx response code")
139
-		}
140
-	} else {
141
-		log.GitLogger.Error(2, "Failed to trigger task: %v", err)
142
-	}
143
-}
144
-
145 93
 func runServ(c *cli.Context) error {
146 94
 	if c.IsSet("config") {
147 95
 		setting.CustomConf = c.String("config")
@@ -187,6 +135,7 @@ func runServ(c *cli.Context) error {
187 135
 	if len(rr) != 2 {
188 136
 		fail("Invalid repository path", "Invalid repository path: %v", args)
189 137
 	}
138
+
190 139
 	username := strings.ToLower(rr[0])
191 140
 	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
192 141
 
@@ -196,6 +145,14 @@ func runServ(c *cli.Context) error {
196 145
 		reponame = reponame[:len(reponame)-5]
197 146
 	}
198 147
 
148
+	os.Setenv(models.EnvRepoUsername, username)
149
+	if isWiki {
150
+		os.Setenv(models.EnvRepoIsWiki, "true")
151
+	} else {
152
+		os.Setenv(models.EnvRepoIsWiki, "false")
153
+	}
154
+	os.Setenv(models.EnvRepoName, reponame)
155
+
199 156
 	repoUser, err := models.GetUserByName(username)
200 157
 	if err != nil {
201 158
 		if models.IsErrUserNotExist(err) {
@@ -204,6 +161,8 @@ func runServ(c *cli.Context) error {
204 161
 		fail("Internal error", "Failed to get repository owner (%s): %v", username, err)
205 162
 	}
206 163
 
164
+	os.Setenv(models.EnvRepoUserSalt, repoUser.Salt)
165
+
207 166
 	repo, err := models.GetRepositoryByName(repoUser.ID, reponame)
208 167
 	if err != nil {
209 168
 		if models.IsErrRepoNotExist(err) {
@@ -286,7 +245,8 @@ func runServ(c *cli.Context) error {
286 245
 					user.Name, requestedMode, repoPath)
287 246
 			}
288 247
 
289
-			os.Setenv("GITEA_PUSHER_NAME", user.Name)
248
+			os.Setenv(models.EnvPusherName, user.Name)
249
+			os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
290 250
 		}
291 251
 	}
292 252
 
@@ -323,11 +283,6 @@ func runServ(c *cli.Context) error {
323 283
 		return nil
324 284
 	}
325 285
 
326
-	uuid := gouuid.NewV4().String()
327
-	os.Setenv(envUpdateTaskUUID, uuid)
328
-	// Keep the old env variable name for backward compability
329
-	os.Setenv("uuid", uuid)
330
-
331 286
 	// Special handle for Windows.
332 287
 	if setting.IsWindows {
333 288
 		verb = strings.Replace(verb, "-", " ", 1)
@@ -341,7 +296,6 @@ func runServ(c *cli.Context) error {
341 296
 		gitcmd = exec.Command(verb, repoPath)
342 297
 	}
343 298
 
344
-	os.Setenv(models.ProtectedBranchAccessMode, requestedMode.String())
345 299
 	os.Setenv(models.ProtectedBranchRepoID, fmt.Sprintf("%d", repo.ID))
346 300
 
347 301
 	gitcmd.Dir = setting.RepoRootPath
@@ -352,10 +306,6 @@ func runServ(c *cli.Context) error {
352 306
 		fail("Internal error", "Failed to execute git command: %v", err)
353 307
 	}
354 308
 
355
-	if requestedMode == models.AccessModeWrite {
356
-		handleUpdateTask(uuid, user, repoUser, reponame, isWiki)
357
-	}
358
-
359 309
 	// Update user key activity.
360 310
 	if keyID > 0 {
361 311
 		key, err := models.GetPublicKeyByID(keyID)

+ 0 - 83
cmd/update.go

@@ -1,83 +0,0 @@
1
-// Copyright 2014 The Gogs Authors. All rights reserved.
2
-// Use of this source code is governed by a MIT-style
3
-// license that can be found in the LICENSE file.
4
-
5
-package cmd
6
-
7
-import (
8
-	"os"
9
-	"strconv"
10
-	"strings"
11
-
12
-	"github.com/urfave/cli"
13
-
14
-	"code.gitea.io/git"
15
-	"code.gitea.io/gitea/models"
16
-	"code.gitea.io/gitea/modules/log"
17
-	"code.gitea.io/gitea/modules/setting"
18
-)
19
-
20
-// CmdUpdate represents the available update sub-command.
21
-var CmdUpdate = cli.Command{
22
-	Name:        "update",
23
-	Usage:       "This command should only be called by Git hook",
24
-	Description: `Update get pushed info and insert into database`,
25
-	Action:      runUpdate,
26
-	Flags: []cli.Flag{
27
-		cli.StringFlag{
28
-			Name:  "config, c",
29
-			Value: "custom/conf/app.ini",
30
-			Usage: "Custom configuration file path",
31
-		},
32
-	},
33
-}
34
-
35
-func runUpdate(c *cli.Context) error {
36
-	if c.IsSet("config") {
37
-		setting.CustomConf = c.String("config")
38
-	}
39
-
40
-	setup("update.log")
41
-
42
-	if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
43
-		log.GitLogger.Trace("SSH_ORIGINAL_COMMAND is empty")
44
-		return nil
45
-	}
46
-
47
-	args := c.Args()
48
-	if len(args) != 3 {
49
-		log.GitLogger.Fatal(2, "Arguments received are not equal to three")
50
-	} else if len(args[0]) == 0 {
51
-		log.GitLogger.Fatal(2, "First argument 'refName' is empty, shouldn't use")
52
-	}
53
-
54
-	// protected branch check
55
-	branchName := strings.TrimPrefix(args[0], git.BranchPrefix)
56
-	repoID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchRepoID), 10, 64)
57
-	log.GitLogger.Trace("pushing to %d %v", repoID, branchName)
58
-	accessMode := models.ParseAccessMode(os.Getenv(models.ProtectedBranchAccessMode))
59
-	// skip admin or owner AccessMode
60
-	if accessMode == models.AccessModeWrite {
61
-		protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
62
-		if err != nil {
63
-			log.GitLogger.Fatal(2, "retrieve protected branches information failed")
64
-		}
65
-
66
-		if protectBranch != nil {
67
-			log.GitLogger.Fatal(2, "protected branches can not be pushed to")
68
-		}
69
-	}
70
-
71
-	task := models.UpdateTask{
72
-		UUID:        os.Getenv("GITEA_UUID"),
73
-		RefName:     args[0],
74
-		OldCommitID: args[1],
75
-		NewCommitID: args[2],
76
-	}
77
-
78
-	if err := models.AddUpdateTask(&task); err != nil {
79
-		log.GitLogger.Fatal(2, "AddUpdateTask: %v", err)
80
-	}
81
-
82
-	return nil
83
-}

+ 0 - 1
main.go

@@ -40,5 +40,4 @@ func main() {
40 40
 	if err != nil {
41 41
 		log.Fatal(4, "Failed to run app with %s: %v", os.Args, err)
42 42
 	}
43
-
44 43
 }

+ 0 - 20
models/fixtures/update_task.yml

@@ -1,20 +0,0 @@
1
--
2
-  id: 1
3
-  uuid: uuid1
4
-  ref_name: refName1
5
-  old_commit_id: oldCommitId1
6
-  new_commit_id: newCommitId1
7
-
8
--
9
-  id: 2
10
-  uuid: uuid2
11
-  ref_name: refName2
12
-  old_commit_id: oldCommitId2
13
-  new_commit_id: newCommitId2
14
-
15
--
16
-  id: 3
17
-  uuid: uuid3
18
-  ref_name: refName3
19
-  old_commit_id: oldCommitId3
20
-  new_commit_id: newCommitId3

+ 0 - 2
models/models.go

@@ -100,7 +100,6 @@ func init() {
100 100
 		new(Release),
101 101
 		new(LoginSource),
102 102
 		new(Webhook),
103
-		new(UpdateTask),
104 103
 		new(HookTask),
105 104
 		new(Team),
106 105
 		new(OrgUser),
@@ -316,7 +315,6 @@ func GetStatistic() (stats Statistic) {
316 315
 	stats.Counter.Label, _ = x.Count(new(Label))
317 316
 	stats.Counter.HookTask, _ = x.Count(new(HookTask))
318 317
 	stats.Counter.Team, _ = x.Count(new(Team))
319
-	stats.Counter.UpdateTask, _ = x.Count(new(UpdateTask))
320 318
 	stats.Counter.Attachment, _ = x.Count(new(Attachment))
321 319
 	return
322 320
 }

+ 9 - 34
models/update.go

@@ -15,40 +15,15 @@ import (
15 15
 	"code.gitea.io/gitea/modules/log"
16 16
 )
17 17
 
18
-// UpdateTask defines an UpdateTask
19
-type UpdateTask struct {
20
-	ID          int64  `xorm:"pk autoincr"`
21
-	UUID        string `xorm:"index"`
22
-	RefName     string
23
-	OldCommitID string
24
-	NewCommitID string
25
-}
26
-
27
-// AddUpdateTask adds an UpdateTask
28
-func AddUpdateTask(task *UpdateTask) error {
29
-	_, err := x.Insert(task)
30
-	return err
31
-}
32
-
33
-// GetUpdateTaskByUUID returns update task by given UUID.
34
-func GetUpdateTaskByUUID(uuid string) (*UpdateTask, error) {
35
-	task := &UpdateTask{
36
-		UUID: uuid,
37
-	}
38
-	has, err := x.Get(task)
39
-	if err != nil {
40
-		return nil, err
41
-	} else if !has {
42
-		return nil, ErrUpdateTaskNotExist{uuid}
43
-	}
44
-	return task, nil
45
-}
46
-
47
-// DeleteUpdateTaskByUUID deletes an UpdateTask from the database
48
-func DeleteUpdateTaskByUUID(uuid string) error {
49
-	_, err := x.Delete(&UpdateTask{UUID: uuid})
50
-	return err
51
-}
18
+// env keys for git hooks need
19
+const (
20
+	EnvRepoName     = "GITEA_REPO_NAME"
21
+	EnvRepoUsername = "GITEA_REPO_USER_NAME"
22
+	EnvRepoUserSalt = "GITEA_REPO_USER_SALT"
23
+	EnvRepoIsWiki   = "GITEA_REPO_IS_WIKI"
24
+	EnvPusherName   = "GITEA_PUSHER_NAME"
25
+	EnvPusherID     = "GITEA_PUSHER_ID"
26
+)
52 27
 
53 28
 // CommitToPushCommit transforms a git.Commit to PushCommit type.
54 29
 func CommitToPushCommit(commit *git.Commit) *PushCommit {

+ 0 - 34
models/update_test.go

@@ -14,40 +14,6 @@ import (
14 14
 	"github.com/stretchr/testify/assert"
15 15
 )
16 16
 
17
-func TestAddUpdateTask(t *testing.T) {
18
-	assert.NoError(t, PrepareTestDatabase())
19
-	task := &UpdateTask{
20
-		UUID:        "uuid4",
21
-		RefName:     "refName4",
22
-		OldCommitID: "oldCommitId4",
23
-		NewCommitID: "newCommitId4",
24
-	}
25
-	assert.NoError(t, AddUpdateTask(task))
26
-	AssertExistsAndLoadBean(t, task)
27
-}
28
-
29
-func TestGetUpdateTaskByUUID(t *testing.T) {
30
-	assert.NoError(t, PrepareTestDatabase())
31
-	task, err := GetUpdateTaskByUUID("uuid1")
32
-	assert.NoError(t, err)
33
-	assert.Equal(t, "uuid1", task.UUID)
34
-	assert.Equal(t, "refName1", task.RefName)
35
-	assert.Equal(t, "oldCommitId1", task.OldCommitID)
36
-	assert.Equal(t, "newCommitId1", task.NewCommitID)
37
-
38
-	_, err = GetUpdateTaskByUUID("invalid")
39
-	assert.Error(t, err)
40
-	assert.True(t, IsErrUpdateTaskNotExist(err))
41
-}
42
-
43
-func TestDeleteUpdateTaskByUUID(t *testing.T) {
44
-	assert.NoError(t, PrepareTestDatabase())
45
-	assert.NoError(t, DeleteUpdateTaskByUUID("uuid1"))
46
-	AssertNotExistsBean(t, &UpdateTask{UUID: "uuid1"})
47
-
48
-	assert.NoError(t, DeleteUpdateTaskByUUID("invalid"))
49
-}
50
-
51 17
 func TestCommitToPushCommit(t *testing.T) {
52 18
 	now := time.Now()
53 19
 	sig := &git.Signature{

+ 35 - 161
routers/repo/http.go

@@ -8,20 +8,15 @@ import (
8 8
 	"bytes"
9 9
 	"compress/gzip"
10 10
 	"fmt"
11
-	"io"
12
-	"io/ioutil"
13 11
 	"net/http"
14 12
 	"os"
15 13
 	"os/exec"
16 14
 	"path"
17 15
 	"regexp"
18
-	"runtime"
19 16
 	"strconv"
20 17
 	"strings"
21 18
 	"time"
22 19
 
23
-	"code.gitea.io/git"
24
-
25 20
 	"code.gitea.io/gitea/models"
26 21
 	"code.gitea.io/gitea/modules/base"
27 22
 	"code.gitea.io/gitea/modules/context"
@@ -59,7 +54,7 @@ func HTTP(ctx *context.Context) {
59 54
 	isWiki := false
60 55
 	if strings.HasSuffix(reponame, ".wiki") {
61 56
 		isWiki = true
62
-		reponame = reponame[:len(reponame) - 5]
57
+		reponame = reponame[:len(reponame)-5]
63 58
 	}
64 59
 
65 60
 	repoUser, err := models.GetUserByName(username)
@@ -89,6 +84,7 @@ func HTTP(ctx *context.Context) {
89 84
 		authUser     *models.User
90 85
 		authUsername string
91 86
 		authPasswd   string
87
+		environ      []string
92 88
 	)
93 89
 
94 90
 	// check access
@@ -182,94 +178,42 @@ func HTTP(ctx *context.Context) {
182 178
 				}
183 179
 			}
184 180
 		}
185
-	}
186 181
 
187
-	callback := func(rpc string, input []byte) {
188
-		if rpc != "receive-pack" || isWiki {
189
-			return
182
+		environ = []string{
183
+			models.EnvRepoUsername + "=" + username,
184
+			models.EnvRepoName + "=" + reponame,
185
+			models.EnvRepoUserSalt + "=" + repoUser.Salt,
186
+			models.EnvPusherName + "=" + authUser.Name,
187
+			models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
188
+			models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
190 189
 		}
191
-
192
-		var lastLine int64
193
-		for {
194
-			head := input[lastLine: lastLine + 2]
195
-			if head[0] == '0' && head[1] == '0' {
196
-				size, err := strconv.ParseInt(string(input[lastLine + 2:lastLine + 4]), 16, 32)
197
-				if err != nil {
198
-					log.Error(4, "%v", err)
199
-					return
200
-				}
201
-
202
-				if size == 0 {
203
-					//fmt.Println(string(input[lastLine:]))
204
-					break
205
-				}
206
-
207
-				line := input[lastLine: lastLine + size]
208
-				idx := bytes.IndexRune(line, '\000')
209
-				if idx > -1 {
210
-					line = line[:idx]
211
-				}
212
-
213
-				fields := strings.Fields(string(line))
214
-				if len(fields) >= 3 {
215
-					oldCommitID := fields[0][4:]
216
-					newCommitID := fields[1]
217
-					refFullName := fields[2]
218
-
219
-					// FIXME: handle error.
220
-					if err = models.PushUpdate(models.PushUpdateOptions{
221
-						RefFullName:  refFullName,
222
-						OldCommitID:  oldCommitID,
223
-						NewCommitID:  newCommitID,
224
-						PusherID:     authUser.ID,
225
-						PusherName:   authUser.Name,
226
-						RepoUserName: username,
227
-						RepoName:     reponame,
228
-					}); err == nil {
229
-						go models.AddTestPullRequestTask(authUser, repo.ID, strings.TrimPrefix(refFullName, git.BranchPrefix), true)
230
-					}
231
-
232
-				}
233
-				lastLine = lastLine + size
234
-			} else {
235
-				break
236
-			}
237
-		}
238
-	}
239
-
240
-	params := make(map[string]string)
241
-
242
-	if askAuth {
243
-		params[models.ProtectedBranchUserID] = fmt.Sprintf("%d", authUser.ID)
244
-		if err == nil {
245
-			params[models.ProtectedBranchAccessMode] = accessMode.String()
190
+		if isWiki {
191
+			environ = append(environ, models.EnvRepoIsWiki+"=true")
192
+		} else {
193
+			environ = append(environ, models.EnvRepoIsWiki+"=false")
246 194
 		}
247
-		params[models.ProtectedBranchRepoID] = fmt.Sprintf("%d", repo.ID)
248 195
 	}
249 196
 
250 197
 	HTTPBackend(ctx, &serviceConfig{
251 198
 		UploadPack:  true,
252 199
 		ReceivePack: true,
253
-		Params:      params,
254
-		OnSucceed:   callback,
200
+		Env:         environ,
255 201
 	})(ctx.Resp, ctx.Req.Request)
256
-
257
-	runtime.GC()
258 202
 }
259 203
 
260 204
 type serviceConfig struct {
261 205
 	UploadPack  bool
262 206
 	ReceivePack bool
263
-	Params      map[string]string
264
-	OnSucceed   func(rpc string, input []byte)
207
+	Env         []string
265 208
 }
266 209
 
267 210
 type serviceHandler struct {
268
-	cfg  *serviceConfig
269
-	w    http.ResponseWriter
270
-	r    *http.Request
271
-	dir  string
272
-	file string
211
+	cfg     *serviceConfig
212
+	w       http.ResponseWriter
213
+	r       *http.Request
214
+	dir     string
215
+	file    string
216
+	environ []string
273 217
 }
274 218
 
275 219
 func (h *serviceHandler) setHeaderNoCache() {
@@ -278,42 +222,6 @@ func (h *serviceHandler) setHeaderNoCache() {
278 222
 	h.w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
279 223
 }
280 224
 
281
-func (h *serviceHandler) getBranch(input []byte) string {
282
-	var lastLine int64
283
-	var branchName string
284
-	for {
285
-		head := input[lastLine : lastLine+2]
286
-		if head[0] == '0' && head[1] == '0' {
287
-			size, err := strconv.ParseInt(string(input[lastLine+2:lastLine+4]), 16, 32)
288
-			if err != nil {
289
-				log.Error(4, "%v", err)
290
-				return branchName
291
-			}
292
-
293
-			if size == 0 {
294
-				//fmt.Println(string(input[lastLine:]))
295
-				break
296
-			}
297
-
298
-			line := input[lastLine : lastLine+size]
299
-			idx := bytes.IndexRune(line, '\000')
300
-			if idx > -1 {
301
-				line = line[:idx]
302
-			}
303
-
304
-			fields := strings.Fields(string(line))
305
-			if len(fields) >= 3 {
306
-				refFullName := fields[2]
307
-				branchName = strings.TrimPrefix(refFullName, git.BranchPrefix)
308
-			}
309
-			lastLine = lastLine + size
310
-		} else {
311
-			break
312
-		}
313
-	}
314
-	return branchName
315
-}
316
-
317 225
 func (h *serviceHandler) setHeaderCacheForever() {
318 226
 	now := time.Now().Unix()
319 227
 	expires := now + 31536000
@@ -370,7 +278,7 @@ func gitCommand(dir string, args ...string) []byte {
370 278
 
371 279
 func getGitConfig(option, dir string) string {
372 280
 	out := string(gitCommand(dir, "config", option))
373
-	return out[0: len(out) - 1]
281
+	return out[0 : len(out)-1]
374 282
 }
375 283
 
376 284
 func getConfigSetting(service, dir string) bool {
@@ -414,13 +322,8 @@ func serviceRPC(h serviceHandler, service string) {
414 322
 
415 323
 	h.w.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-result", service))
416 324
 
417
-	var (
418
-		reqBody    = h.r.Body
419
-		input      []byte
420
-		br         io.Reader
421
-		err        error
422
-		branchName string
423
-	)
325
+	var err error
326
+	var reqBody = h.r.Body
424 327
 
425 328
 	// Handle GZIP.
426 329
 	if h.r.Header.Get("Content-Encoding") == "gzip" {
@@ -432,52 +335,23 @@ func serviceRPC(h serviceHandler, service string) {
432 335
 		}
433 336
 	}
434 337
 
435
-	if h.cfg.OnSucceed != nil {
436
-		input, err = ioutil.ReadAll(reqBody)
437
-		if err != nil {
438
-			log.GitLogger.Error(2, "fail to read request body: %v", err)
439
-			h.w.WriteHeader(http.StatusInternalServerError)
440
-			return
441
-		}
442
-
443
-		branchName = h.getBranch(input)
444
-		br = bytes.NewReader(input)
445
-	} else {
446
-		br = reqBody
447
-	}
448
-
449
-	// check protected branch
450
-	repoID, _ := strconv.ParseInt(h.cfg.Params[models.ProtectedBranchRepoID], 10, 64)
451
-	accessMode := models.ParseAccessMode(h.cfg.Params[models.ProtectedBranchAccessMode])
452
-	// skip admin or owner AccessMode
453
-	if accessMode == models.AccessModeWrite {
454
-		protectBranch, err := models.GetProtectedBranchBy(repoID, branchName)
455
-		if err != nil {
456
-			log.GitLogger.Error(2, "fail to get protected branch information: %v", err)
457
-			h.w.WriteHeader(http.StatusInternalServerError)
458
-			return
459
-		}
460
-
461
-		if protectBranch != nil {
462
-			log.GitLogger.Error(2, "protected branches can not be pushed to")
463
-			h.w.WriteHeader(http.StatusForbidden)
464
-			return
465
-		}
466
-	}
338
+	// set this for allow pre-receive and post-receive execute
339
+	h.environ = append(h.environ, "SSH_ORIGINAL_COMMAND="+service)
467 340
 
341
+	var stderr bytes.Buffer
468 342
 	cmd := exec.Command("git", service, "--stateless-rpc", h.dir)
469 343
 	cmd.Dir = h.dir
344
+	if service == "receive-pack" {
345
+		cmd.Env = append(os.Environ(), h.environ...)
346
+	}
470 347
 	cmd.Stdout = h.w
471
-	cmd.Stdin = br
348
+	cmd.Stdin = reqBody
349
+	cmd.Stderr = &stderr
472 350
 	if err := cmd.Run(); err != nil {
473
-		log.GitLogger.Error(2, "fail to serve RPC(%s): %v", service, err)
351
+		log.GitLogger.Error(2, "fail to serve RPC(%s): %v - %v", service, err, stderr)
474 352
 		h.w.WriteHeader(http.StatusInternalServerError)
475 353
 		return
476 354
 	}
477
-
478
-	if h.cfg.OnSucceed != nil {
479
-		h.cfg.OnSucceed(service, input)
480
-	}
481 355
 }
482 356
 
483 357
 func serviceUploadPack(h serviceHandler) {
@@ -501,7 +375,7 @@ func updateServerInfo(dir string) []byte {
501 375
 }
502 376
 
503 377
 func packetWrite(str string) []byte {
504
-	s := strconv.FormatInt(int64(len(str) + 4), 16)
378
+	s := strconv.FormatInt(int64(len(str)+4), 16)
505 379
 	if len(s)%4 != 0 {
506 380
 		s = strings.Repeat("0", 4-len(s)%4) + s
507 381
 	}
@@ -593,7 +467,7 @@ func HTTPBackend(ctx *context.Context, cfg *serviceConfig) http.HandlerFunc {
593 467
 					return
594 468
 				}
595 469
 
596
-				route.handler(serviceHandler{cfg, w, r, dir, file})
470
+				route.handler(serviceHandler{cfg, w, r, dir, file, cfg.Env})
597 471
 				return
598 472
 			}
599 473
 		}