aboutsummaryrefslogtreecommitdiff
path: root/git_hooks_handle.go
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2025-02-19 17:08:14 +0800
committerRunxi Yu <me@runxiyu.org>2025-02-19 17:08:14 +0800
commit7f9705a29c29b77f9049f2d1222a2d08530fc225 (patch)
tree5c8edb97352e10dd0a347d497329c019f7ef1195 /git_hooks_handle.go
parent*.go: Reformat (diff)
downloadforge-7f9705a29c29b77f9049f2d1222a2d08530fc225.tar.gz
forge-7f9705a29c29b77f9049f2d1222a2d08530fc225.tar.zst
forge-7f9705a29c29b77f9049f2d1222a2d08530fc225.zip
hooks: Use ssh stderr directly instead of going through hook
Diffstat (limited to 'git_hooks_handle.go')
-rw-r--r--git_hooks_handle.go231
1 files changed, 99 insertions, 132 deletions
diff --git a/git_hooks_handle.go b/git_hooks_handle.go
index 954cbfd..b0383a2 100644
--- a/git_hooks_handle.go
+++ b/git_hooks_handle.go
@@ -63,165 +63,132 @@ func hooks_handle_connection(conn net.Conn) {
return
}
- var argc64 uint64
- err = binary.Read(conn, binary.NativeEndian, &argc64)
- if err != nil {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ ssh_stderr := pack_to_hook.session.Stderr()
+
+ hook_return_value := func() byte {
+ var argc64 uint64
+ err = binary.Read(conn, binary.NativeEndian, &argc64)
+ if err != nil {
+ fmt.Fprintln(ssh_stderr, "Failed to read argc:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Failed to read argc:", err.Error())
- return
- }
- var args []string
- for i := uint64(0); i < argc64; i++ {
- var arg bytes.Buffer
- for {
- b := make([]byte, 1)
- n, err := conn.Read(b)
- if err != nil || n != 1 {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ var args []string
+ for i := uint64(0); i < argc64; i++ {
+ var arg bytes.Buffer
+ for {
+ b := make([]byte, 1)
+ n, err := conn.Read(b)
+ if err != nil || n != 1 {
+ fmt.Fprintln(ssh_stderr, "Failed to read arg:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Failed to read arg:", err.Error())
- return
- }
- if b[0] == 0 {
- break
+ if b[0] == 0 {
+ break
+ }
+ arg.WriteByte(b[0])
}
- arg.WriteByte(b[0])
+ args = append(args, arg.String())
}
- args = append(args, arg.String())
- }
-
- var stdin bytes.Buffer
- _, err = io.Copy(&stdin, conn)
- if err != nil {
- fmt.Fprintln(conn, "Failed to read to the stdin buffer:", err.Error())
- }
- switch filepath.Base(args[0]) {
- case "pre-receive":
- if pack_to_hook.direct_access {
- if _, err := conn.Write([]byte{0}); err != nil {
- return
- }
- } else {
- all_ok := true
- messages := make([]string, 0)
+ var stdin bytes.Buffer
+ _, err = io.Copy(&stdin, conn)
+ if err != nil {
+ fmt.Fprintln(conn, "Failed to read to the stdin buffer:", err.Error())
+ }
- for {
- line, err := stdin.ReadString('\n')
- if errors.Is(err, io.EOF) {
- break
- }
- line = line[:len(line)-1]
+ switch filepath.Base(args[0]) {
+ case "pre-receive":
+ if pack_to_hook.direct_access {
+ return 0
+ } else {
+ all_ok := true
+ for {
+ line, err := stdin.ReadString('\n')
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ line = line[:len(line)-1]
- old_oid, rest, found := strings.Cut(line, " ")
- if !found {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ old_oid, rest, found := strings.Cut(line, " ")
+ if !found {
+ fmt.Fprintln(ssh_stderr, "Invalid pre-receive line:", line)
+ return 1
}
- fmt.Fprintln(conn, "Invalid pre-receive line:", line)
- break
- }
- new_oid, ref_name, found := strings.Cut(rest, " ")
- if !found {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ new_oid, ref_name, found := strings.Cut(rest, " ")
+ if !found {
+ fmt.Fprintln(ssh_stderr, "Invalid pre-receive line:", line)
+ return 1
}
- fmt.Fprintln(conn, "Invalid pre-receive line:", line)
- break
- }
- if strings.HasPrefix(ref_name, "refs/heads/contrib/") {
- if all_zero_num_string(old_oid) { // New branch
- messages = append(messages, "Acceptable push to new contrib branch: "+ref_name)
- // TODO: Create a merge request. If that fails,
- // we should just reject this entire push
- // immediately.
- } else { // Existing contrib branch
- // TODO: Check if the current user is authorized
- // to push to this contrib branch.
- repo, err := git.PlainOpen(pack_to_hook.repo_path)
- if err != nil {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ if strings.HasPrefix(ref_name, "refs/heads/contrib/") {
+ if all_zero_num_string(old_oid) { // New branch
+ fmt.Fprintln(ssh_stderr, "Acceptable push to new contrib branch: "+ref_name)
+ // TODO: Create a merge request. If that fails,
+ // we should just reject this entire push
+ // immediately.
+ } else { // Existing contrib branch
+ // TODO: Check if the current user is authorized
+ // to push to this contrib branch.
+ repo, err := git.PlainOpen(pack_to_hook.repo_path)
+ if err != nil {
+ fmt.Fprintln(ssh_stderr, "Daemon failed to open repo:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Daemon failed to open repo:", err.Error())
- return
- }
- old_hash := plumbing.NewHash(old_oid)
- fmt.Println(old_hash)
+ old_hash := plumbing.NewHash(old_oid)
- old_commit, err := repo.CommitObject(old_hash)
- if err != nil {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ old_commit, err := repo.CommitObject(old_hash)
+ if err != nil {
+ fmt.Fprintln(ssh_stderr, "Daemon failed to get old commit:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Daemon failed to get old commit:", err.Error())
- return
- }
- // Potential BUG: I'm not sure if new_commit is guaranteed to be
- // detectable as they haven't been merged into the main repo's
- // objects yet. But it seems to work, and I don't think there's
- // any reason for this to only work intermitently.
- new_hash := plumbing.NewHash(new_oid)
- new_commit, err := repo.CommitObject(new_hash)
- if err != nil {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ // Potential BUG: I'm not sure if new_commit is guaranteed to be
+ // detectable as they haven't been merged into the main repo's
+ // objects yet. But it seems to work, and I don't think there's
+ // any reason for this to only work intermitently.
+ new_hash := plumbing.NewHash(new_oid)
+ new_commit, err := repo.CommitObject(new_hash)
+ if err != nil {
+ fmt.Fprintln(ssh_stderr, "Daemon failed to get new commit:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Daemon failed to get new commit:", err.Error())
- return
- }
- is_ancestor, err := old_commit.IsAncestor(new_commit)
- if err != nil {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ is_ancestor, err := old_commit.IsAncestor(new_commit)
+ if err != nil {
+ fmt.Fprintln(ssh_stderr, "Daemon failed to check if old commit is ancestor:", err.Error())
+ return 1
}
- fmt.Fprintln(conn, "Daemon failed to check if old commit is ancestor:", err.Error())
- return
- }
- if !is_ancestor {
- // TODO: Create MR snapshot ref instead
- all_ok = false
- messages = append(messages, "Rejecting force push to contrib branch: "+ref_name)
- continue
- }
+ if !is_ancestor {
+ // TODO: Create MR snapshot ref instead
+ all_ok = false
+ fmt.Fprintln(ssh_stderr, "Rejecting force push to contrib branch: "+ref_name)
+ continue
+ }
- messages = append(messages, "Acceptable push to existing contrib branch: "+ref_name)
+ fmt.Fprintln(ssh_stderr, "Acceptable push to existing contrib branch: "+ref_name)
+ }
+ } else { // Non-contrib branch
+ all_ok = false
+ fmt.Fprintln(ssh_stderr, "Rejecting push to non-contrib branch: "+ref_name)
}
- } else { // Non-contrib branch
- all_ok = false
- messages = append(messages, "Rejecting push to non-contrib branch: "+ref_name)
}
- }
- if all_ok {
- if _, err := conn.Write([]byte{0}); err != nil {
- return
- }
- } else {
- if _, err := conn.Write([]byte{1}); err != nil {
- return
+ if all_ok {
+ return 0
+ } else {
+ return 1
}
}
-
- for _, message := range messages {
- fmt.Fprintln(conn, message)
- }
+ default:
+ fmt.Fprintln(ssh_stderr, "Invalid hook:", args[0])
+ return 1
}
- default:
- if _, err := conn.Write([]byte{1}); err != nil {
- return
- }
- fmt.Fprintln(conn, "Invalid hook:", args[0])
- }
+ }()
+
+ conn.Write([]byte{hook_return_value})
}
func serve_git_hooks(listener net.Listener) error {