fix(git): hang on git i/o error

Ayman Bagabas created

Change summary

pkg/git/service.go | 43 ++++++++++++++++++++++++++++---------------
1 file changed, 28 insertions(+), 15 deletions(-)

Detailed changes

pkg/git/service.go 🔗

@@ -8,8 +8,9 @@ import (
 	"os"
 	"os/exec"
 	"strings"
+	"sync"
 
-	"golang.org/x/sync/errgroup"
+	"github.com/charmbracelet/log"
 )
 
 // Service is a Git daemon service.
@@ -118,34 +119,46 @@ func gitServiceHandler(ctx context.Context, svc Service, scmd ServiceCommand) er
 		return err
 	}
 
-	errg, _ := errgroup.WithContext(ctx)
+	wg := &sync.WaitGroup{}
 
 	// stdin
 	if scmd.Stdin != nil {
-		errg.Go(func() error {
+		go func() {
 			defer stdin.Close() // nolint: errcheck
-			_, err := io.Copy(stdin, scmd.Stdin)
-			return err
-		})
+			if _, err := io.Copy(stdin, scmd.Stdin); err != nil {
+				log.Errorf("gitServiceHandler: failed to copy stdin: %v", err)
+			}
+		}()
 	}
 
 	// stdout
 	if scmd.Stdout != nil {
-		errg.Go(func() error {
-			_, err := io.Copy(scmd.Stdout, stdout)
-			return err
-		})
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			if _, err := io.Copy(scmd.Stdout, stdout); err != nil {
+				log.Errorf("gitServiceHandler: failed to copy stdout: %v", err)
+			}
+		}()
 	}
 
 	// stderr
 	if scmd.Stderr != nil {
-		errg.Go(func() error {
-			_, erro := io.Copy(scmd.Stderr, stderr)
-			return erro
-		})
+		wg.Add(1)
+		go func() {
+			defer wg.Done()
+			if _, erro := io.Copy(scmd.Stderr, stderr); err != nil {
+				log.Errorf("gitServiceHandler: failed to copy stderr: %v", erro)
+			}
+		}()
 	}
 
-	err = errors.Join(errg.Wait(), cmd.Wait())
+	// Ensure all the output is written before waiting for the command to
+	// finish.
+	// Stdin is handled by the client side.
+	wg.Wait()
+
+	err = cmd.Wait()
 	if err != nil && errors.Is(err, os.ErrNotExist) {
 		return ErrInvalidRepo
 	} else if err != nil {