diff --git a/internal/gitaly/service/hook/pre_receive.go b/internal/gitaly/service/hook/pre_receive.go index 4062546dc2acc2d07b1543170a3a6240e58a453b..6beeb4da461c12550582f30e38e82d3b06aca2b2 100644 --- a/internal/gitaly/service/hook/pre_receive.go +++ b/internal/gitaly/service/hook/pre_receive.go @@ -7,10 +7,14 @@ import ( "os/exec" "sync" + "github.com/golang/protobuf/ptypes/wrappers" "gitlab.com/gitlab-org/gitaly/v16/internal/gitaly/storage" + "gitlab.com/gitlab-org/gitaly/v16/internal/helper/chunk" "gitlab.com/gitlab-org/gitaly/v16/internal/structerr" "gitlab.com/gitlab-org/gitaly/v16/proto/go/gitalypb" "gitlab.com/gitlab-org/gitaly/v16/streamio" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" ) func (s *server) PreReceiveHook(stream gitalypb.HookService_PreReceiveHookServer) error { @@ -61,11 +65,56 @@ func validatePreReceiveHookRequest(ctx context.Context, locator storage.Locator, return locator.ValidateRepository(ctx, in.GetRepository()) } +type preReceiveHookErrorSender struct { + errorCode int32 + bytes *wrappers.BytesValue + send func(*wrappers.BytesValue, int32) error +} + +func (p *preReceiveHookErrorSender) Reset() { + if p.bytes != nil { + p.bytes.Value = p.bytes.Value[:0] + return + } + + p.bytes = &wrappers.BytesValue{} +} + +func (p *preReceiveHookErrorSender) Append(m proto.Message) { + p.bytes.Value = append(p.bytes.Value, m.(*wrappers.BytesValue).Value...) +} + +func (p *preReceiveHookErrorSender) Send() error { + return p.send(p.bytes, p.errorCode) +} + func preReceiveHookResponse(stream gitalypb.HookService_PreReceiveHookServer, code int32, stderr string) error { - if err := stream.Send(&gitalypb.PreReceiveHookResponse{ - ExitStatus: &gitalypb.ExitStatus{Value: code}, - Stderr: []byte(stderr), - }); err != nil { + if stderr == "" { + if err := stream.Send(&gitalypb.PreReceiveHookResponse{ + ExitStatus: &gitalypb.ExitStatus{Value: code}, + Stderr: []byte(""), + }); err != nil { + return structerr.NewInternal("sending response: %w", err) + } + + return nil + } + + chunker := chunk.New(&preReceiveHookErrorSender{ + errorCode: code, + send: func(bytes *wrappers.BytesValue, errorCode int32) error { + return stream.Send(&gitalypb.PreReceiveHookResponse{ + ExitStatus: &gitalypb.ExitStatus{Value: errorCode}, + Stderr: []byte(bytes.Value), + }) + }, + }) + + if err := chunker.Send(wrapperspb.Bytes([]byte(stderr))); err != nil { + return structerr.NewInternal("sending response: %w", err) + } + + if err := chunker.Flush(); err != nil { return structerr.NewInternal("sending response: %w", err) }