diff options
author | Runxi Yu <me@runxiyu.org> | 2025-02-19 17:08:14 +0800 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2025-02-19 17:08:14 +0800 |
commit | 7f9705a29c29b77f9049f2d1222a2d08530fc225 (patch) | |
tree | 5c8edb97352e10dd0a347d497329c019f7ef1195 /git_hooks_handle.go | |
parent | *.go: Reformat (diff) | |
download | forge-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.go | 231 |
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 { |