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}