arche / internal/archesrv/serverhooks.go

commit 154431fd
 1package archesrv
 2
 3import (
 4	"context"
 5	"fmt"
 6	"log/slog"
 7	"os"
 8	"os/exec"
 9	"strings"
10	"time"
11)
12
13func runPreReceiveHook(scriptPath, refName, oldHex, newHex string, timeoutSec int) error {
14	if scriptPath == "" {
15		return nil
16	}
17
18	if timeoutSec <= 0 {
19		timeoutSec = 30
20	}
21	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSec)*time.Second)
22	defer cancel()
23
24	input := fmt.Sprintf("%s %s %s\n", oldHex, newHex, refName)
25	c := exec.CommandContext(ctx, scriptPath, refName, oldHex, newHex)
26	c.Stdin = strings.NewReader(input)
27	c.Stdout = os.Stdout
28	c.Stderr = os.Stderr
29	if err := c.Run(); err != nil {
30		if ctx.Err() != nil {
31			return fmt.Errorf("hook %s timed out after %ds", scriptPath, timeoutSec)
32		}
33		return fmt.Errorf("hook %s: %w", scriptPath, err)
34	}
35	return nil
36}
37
38func runPostReceiveHook(scriptPath, refName, oldHex, newHex string, timeoutSec int) {
39	if scriptPath == "" {
40		return
41	}
42	if timeoutSec <= 0 {
43		timeoutSec = 30
44	}
45	input := fmt.Sprintf("%s %s %s\n", oldHex, newHex, refName)
46	c := exec.Command(scriptPath, refName, oldHex, newHex)
47	c.Stdin = strings.NewReader(input)
48	c.Stdout = os.Stdout
49	c.Stderr = os.Stderr
50
51	done := make(chan error, 1)
52	go func() { done <- c.Run() }()
53
54	select {
55	case err := <-done:
56		if err != nil {
57			slog.Error("post-receive hook failed", "script", scriptPath, "err", err)
58		}
59	case <-time.After(time.Duration(timeoutSec) * time.Second):
60		if c.Process != nil {
61			c.Process.Kill() //nolint:errcheck
62		}
63		slog.Error("post-receive hook timed out", "script", scriptPath, "timeout_sec", timeoutSec)
64	}
65}