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 [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 }