-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Add agit flow support in gitea #14295
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
36c77da
b8784f9
a757ed5
f96e9e7
501f850
7294bfa
c1291fa
0d9442e
86acbe1
95ca26a
f68899a
9eeaf3d
5c67694
95eebb1
434704c
5be1bde
3c01930
300765d
5140fd7
e1119e8
9f95719
96d2072
5fd466f
dc13bfe
60d3c32
0d85ba7
10a45c5
166ce7b
beb0a2e
6e0f5d5
aee62fe
b740e9b
5547e9a
4a8e7a7
fb9a0ac
e33d0aa
a6481fa
a409d22
5ae5b84
e9c84e1
ce04bd2
d833ea1
638e8f7
02e49eb
91d1c11
fbfb003
3d03249
cd01c08
1a8096e
ab351e5
b096315
59a8552
22fd300
d322b82
4d4aa45
19edf9a
fe505de
5a840ac
a86ee55
25c4b5c
b42ebdd
3b6594a
f351805
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ var ( | |
subcmdHookPreReceive, | ||
subcmdHookUpdate, | ||
subcmdHookPostReceive, | ||
subcmdHookProcReceive, | ||
}, | ||
} | ||
|
||
|
@@ -74,6 +75,18 @@ var ( | |
}, | ||
}, | ||
} | ||
// Note: new hook since git 2.29 | ||
subcmdHookProcReceive = cli.Command{ | ||
Name: "proc-receive", | ||
Usage: "Delegate proc-receive Git hook", | ||
Description: "This command should only be called by Git", | ||
Action: runHookProcReceive, | ||
Flags: []cli.Flag{ | ||
cli.BoolFlag{ | ||
Name: "debug", | ||
}, | ||
}, | ||
} | ||
) | ||
|
||
type delayWriter struct { | ||
|
@@ -460,3 +473,261 @@ func pushOptions() map[string]string { | |
} | ||
return opts | ||
} | ||
|
||
func runHookProcReceive(c *cli.Context) error { | ||
setup("hooks/proc-receive.log", c.Bool("debug")) | ||
|
||
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { | ||
if setting.OnlyAllowPushIfGiteaEnvironmentSet { | ||
fail(`Rejecting changes as Gitea environment not set. | ||
If you are pushing over SSH you must push with a key managed by | ||
Gitea or set your environment appropriately.`, "") | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
lf, err := os.OpenFile("test.log", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777) | ||
if err != nil { | ||
fail("Internal Server Error", "open log file failed: %v", err) | ||
} | ||
defer lf.Close() | ||
|
||
if git.CheckGitVersionAtLeast("2.29") != nil { | ||
fail("Internal Server Error", "git not support proc-receive.") | ||
} | ||
|
||
reader := bufio.NewReader(os.Stdin) | ||
repoUser := os.Getenv(models.EnvRepoUsername) | ||
repoName := os.Getenv(models.EnvRepoName) | ||
pusherID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64) | ||
pusherName := os.Getenv(models.EnvPusherName) | ||
|
||
// 1. Version and features negotiation. | ||
// S: PKT-LINE(version=1\0push-options atomic...) | ||
// S: flush-pkt | ||
// H: PKT-LINE(version=1\0push-options...) | ||
// H: flush-pkt | ||
|
||
rs, err := readPktLine(reader) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line format is wrong :%v", err) | ||
} | ||
if rs.Type != pktLineTypeData { | ||
fail("Internal Server Error", "Pkt-Line format is wrong. get %v", rs) | ||
} | ||
|
||
const VersionHead string = "version=1" | ||
|
||
if !strings.HasPrefix(rs.Data, VersionHead) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should split |
||
fail("Internal Server Error", "Pkt-Line format is wrong. get %v", rs) | ||
} | ||
|
||
hasPushOptions := false | ||
response := []byte(VersionHead) | ||
if strings.Contains(rs.Data, "push-options") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should split capabilities using space character, and try to match each capability. |
||
response = append(response, byte(0)) | ||
response = append(response, []byte("push-options")...) | ||
hasPushOptions = true | ||
} | ||
response = append(response, []byte("\n")...) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
rs, err = readPktLine(reader) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line format is wrong :%v", err) | ||
} | ||
if rs.Type != pktLineTypeFlush { | ||
fail("Internal Server Error", "Pkt-Line format is wrong. get %v", rs) | ||
} | ||
|
||
err = writePktLine(os.Stdout, pktLineTypeData, response) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
|
||
err = writePktLine(os.Stdout, pktLineTypeFlush, nil) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
|
||
// 2. receive commands from server. | ||
// S: PKT-LINE(<old-oid> <new-oid> <ref>) | ||
// S: ... ... | ||
// S: flush-pkt | ||
// # receive push-options | ||
// S: PKT-LINE(push-option) | ||
// S: ... ... | ||
// S: flush-pkt | ||
hookOptions := private.HookOptions{ | ||
UserName: pusherName, | ||
UserID: pusherID, | ||
} | ||
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize) | ||
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize) | ||
hookOptions.RefFullNames = make([]string, 0, hookBatchSize) | ||
|
||
for { | ||
rs, err = readPktLine(reader) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line format is wrong :%v", err) | ||
} | ||
if rs.Type == pktLineTypeFlush { | ||
break | ||
} | ||
t := strings.SplitN(rs.Data, " ", 3) | ||
if len(t) != 3 { | ||
continue | ||
} | ||
hookOptions.OldCommitIDs = append(hookOptions.OldCommitIDs, t[0]) | ||
hookOptions.NewCommitIDs = append(hookOptions.NewCommitIDs, t[1]) | ||
hookOptions.RefFullNames = append(hookOptions.RefFullNames, t[2]) | ||
zeripath marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
hookOptions.GitPushOptions = make(map[string]string) | ||
|
||
if hasPushOptions { | ||
for { | ||
rs, err = readPktLine(reader) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line format is wrong :%v", err) | ||
} | ||
if rs.Type == pktLineTypeFlush { | ||
break | ||
} | ||
|
||
kv := strings.SplitN(rs.Data, "=", 2) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if len(kv) == 2 { | ||
hookOptions.GitPushOptions[kv[0]] = kv[1] | ||
} | ||
} | ||
} | ||
|
||
// run hook | ||
resp, err := private.HookProcReceive(repoUser, repoName, hookOptions) | ||
if err != nil { | ||
fail("Internal Server Error", "run proc-receive hook failed :%v", err) | ||
} | ||
|
||
// 3 response result to service. | ||
// # OK, but has an alternate reference. The alternate reference name | ||
// # and other status can be given in option directives. | ||
// H: PKT-LINE(ok <ref>) | ||
// H: PKT-LINE(option refname <refname>) | ||
// H: PKT-LINE(option old-oid <old-oid>) | ||
// H: PKT-LINE(option new-oid <new-oid>) | ||
// H: PKT-LINE(option forced-update) | ||
// H: ... ... | ||
// H: flush-pkt | ||
for _, rs := range resp.Results { | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
err = writePktLine(os.Stdout, pktLineTypeData, []byte("ok "+rs.OrignRef)) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
err = writePktLine(os.Stdout, pktLineTypeData, []byte("option refname "+rs.Ref)) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
err = writePktLine(os.Stdout, pktLineTypeData, []byte("option old-oid "+rs.OldOID)) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
err = writePktLine(os.Stdout, pktLineTypeData, []byte("option new-oid "+rs.NewOID)) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
} | ||
err = writePktLine(os.Stdout, pktLineTypeFlush, nil) | ||
if err != nil { | ||
fail("Internal Server Error", "Pkt-Line response failed: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
// git PKT-Line api | ||
// pktLineType message type of pkt-line | ||
type pktLineType int64 | ||
|
||
const ( | ||
// UnKnow type | ||
pktLineTypeUnknow pktLineType = 0 | ||
// flush-pkt "0000" | ||
pktLineTypeFlush pktLineType = iota | ||
// data line | ||
pktLineTypeData | ||
) | ||
|
||
// gitPktLine pkt-line api | ||
type gitPktLine struct { | ||
Type pktLineType | ||
Length int64 | ||
6543 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Data string | ||
} | ||
|
||
func readPktLine(in *bufio.Reader) (r *gitPktLine, err error) { | ||
// read prefix | ||
lengthBytes := make([]byte, 4) | ||
for i := 0; i < 4; i++ { | ||
lengthBytes[i], err = in.ReadByte() | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
r = new(gitPktLine) | ||
r.Length, err = strconv.ParseInt(string(lengthBytes), 16, 64) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if r.Length == 0 { | ||
r.Type = pktLineTypeFlush | ||
return r, nil | ||
} | ||
|
||
if r.Length <= 4 || r.Length > 65520 { | ||
r.Type = pktLineTypeUnknow | ||
return r, nil | ||
} | ||
|
||
tmp := make([]byte, r.Length-4) | ||
for i := range tmp { | ||
tmp[i], err = in.ReadByte() | ||
6543 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
r.Type = pktLineTypeData | ||
r.Data = string(tmp) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return r, nil | ||
} | ||
|
||
func writePktLine(out io.Writer, typ pktLineType, data []byte) error { | ||
if typ == pktLineTypeFlush { | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
l, err := out.Write([]byte("0000")) | ||
if err != nil { | ||
return err | ||
} | ||
if l != 4 { | ||
return fmt.Errorf("real write length is different with request, want %v, real %v", 4, l) | ||
} | ||
} | ||
|
||
if typ != pktLineTypeData { | ||
return nil | ||
} | ||
|
||
l := len(data) + 4 | ||
tmp := []byte(fmt.Sprintf("%04x", l)) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tmp = append(tmp, data...) | ||
a1012112796 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
lr, err := out.Write(tmp) | ||
if err != nil { | ||
return err | ||
} | ||
if l != lr { | ||
return fmt.Errorf("real write length is different with request, want %v, real %v", l, lr) | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright 2021 The Gitea 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 ( | ||
"bufio" | ||
"bytes" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPktLine(t *testing.T) { | ||
// test read | ||
s := strings.NewReader("0000") | ||
r := bufio.NewReader(s) | ||
result, err := readPktLine(r) | ||
assert.NoError(t, err) | ||
assert.Equal(t, pktLineTypeFlush, result.Type) | ||
|
||
s = strings.NewReader("0006a\n") | ||
r = bufio.NewReader(s) | ||
result, err = readPktLine(r) | ||
assert.NoError(t, err) | ||
assert.Equal(t, pktLineTypeData, result.Type) | ||
assert.Equal(t, "a\n", result.Data) | ||
|
||
// test write | ||
w := bytes.NewBuffer([]byte{}) | ||
err = writePktLine(w, pktLineTypeFlush, nil) | ||
assert.NoError(t, err) | ||
assert.Equal(t, []byte("0000"), w.Bytes()) | ||
|
||
w.Reset() | ||
err = writePktLine(w, pktLineTypeData, []byte("a\nb")) | ||
assert.NoError(t, err) | ||
assert.Equal(t, []byte("0007a\nb"), w.Bytes()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright 2021 The Gitea 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 migrations | ||
|
||
import ( | ||
"fmt" | ||
|
||
"xorm.io/xorm" | ||
) | ||
|
||
func addAgitStylePullRequest(x *xorm.Engine) error { | ||
type PullRequestStyle int | ||
|
||
type PullRequest struct { | ||
TopicBranch string | ||
Style PullRequestStyle | ||
} | ||
|
||
if err := x.Sync2(new(PullRequest)); err != nil { | ||
return fmt.Errorf("Sync2: %v", err) | ||
} | ||
return nil | ||
} |
Uh oh!
There was an error while loading. Please reload this page.