Files
simplegit/hooks/trigger.go
T
2026-07-17 05:55:31 -04:00

139 lines
2.5 KiB
Go

package hooks
import (
"bufio"
"encoding/json"
"fmt"
"io"
"log"
"net"
"os"
"path/filepath"
"strings"
"time"
)
func RunClient(args []string) {
root, rest := parseRoot(args)
if root == "" || len(rest) < 1 {
fmt.Fprintln(os.Stderr, "simplegit hook: usage: simplegit hook --root=PATH <hook-type> [args...]")
os.Exit(0)
}
hookType := rest[0]
hookArgs := rest[1:]
stdin, _ := io.ReadAll(os.Stdin)
cwd, _ := os.Getwd()
ev := HookEvent{
Type: hookType,
Repo: resolveRepo(root, os.Getenv("GIT_DIR"), cwd),
RepoPath: cwd,
Args: hookArgs,
Stdin: string(stdin),
Env: captureGitEnv(),
}
os.Exit(forward(ev, SockPath(root)))
}
func parseRoot(args []string) (root string, rest []string) {
for i := 0; i < len(args); i++ {
a := args[i]
switch {
case strings.HasPrefix(a, "--root="):
root = strings.TrimPrefix(a, "--root=")
case a == "--root" && i+1 < len(args):
root = args[i+1]
i++
default:
rest = append(rest, a)
}
}
return root, rest
}
func forward(ev HookEvent, sock string) int {
body, err := json.Marshal(ev)
if err != nil {
log.Printf("hook %s: marshal: %v", ev.Type, err)
return 0
}
conn, err := net.DialTimeout("unix", sock, 5*time.Second)
if err != nil {
return 0
}
defer conn.Close()
_ = conn.SetDeadline(time.Now().Add(10 * time.Second))
if _, err := conn.Write(append(body, '\n')); err != nil {
log.Printf("hook %s: write: %v", ev.Type, err)
return 0
}
reader := bufio.NewReader(conn)
line, err := reader.ReadString('\n')
if err != nil {
log.Printf("hook %s: read response: %v", ev.Type, err)
return 0
}
var resp struct {
Exit int `json:"exit"`
}
if err := json.Unmarshal([]byte(line), &resp); err != nil {
log.Printf("hook %s: parse response: %v", ev.Type, err)
return 0
}
return resp.Exit
}
func resolveRepo(root, gitDir, cwd string) string {
dir := gitDir
if dir == "" || !filepath.IsAbs(dir) {
dir = filepath.Join(cwd, dir)
}
if resolved, err := filepath.EvalSymlinks(dir); err == nil {
dir = resolved
}
reposRoot := filepath.Join(root, "repos")
if resolved, err := filepath.EvalSymlinks(reposRoot); err == nil {
reposRoot = resolved
}
rel, err := filepath.Rel(reposRoot, dir)
if err != nil || strings.HasPrefix(rel, "..") || rel == "." {
return ""
}
return strings.TrimSuffix(rel, ".git")
}
func captureGitEnv() map[string]string {
env := map[string]string{}
for _, kv := range os.Environ() {
if k, v, ok := strings.Cut(kv, "="); ok && strings.HasPrefix(k, "GIT_") {
env[k] = v
}
}
return env
}