forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package hook
2
3import (
4 "bufio"
5 "context"
6 "fmt"
7 "net/http"
8 "os"
9 "strings"
10
11 "github.com/urfave/cli/v3"
12)
13
14// The hook command is nested like so:
15//
16// knot hook --[flags] [hook]
17func Command() *cli.Command {
18 return &cli.Command{
19 Name: "hook",
20 Usage: "run git hooks",
21 Flags: []cli.Flag{
22 &cli.StringFlag{
23 Name: "git-dir",
24 Usage: "base directory for git repos",
25 },
26 &cli.StringFlag{
27 Name: "user-did",
28 Usage: "git user's did",
29 },
30 &cli.StringFlag{
31 Name: "user-handle",
32 Usage: "git user's handle",
33 },
34 &cli.StringFlag{
35 Name: "internal-api",
36 Usage: "endpoint for the internal API",
37 Value: "http://localhost:5444",
38 },
39 &cli.StringSliceFlag{
40 Name: "push-option",
41 Usage: "any push option from git",
42 },
43 },
44 Commands: []*cli.Command{
45 {
46 Name: "post-recieve",
47 Usage: "sends a post-recieve hook to the knot (waits for stdin)",
48 Action: postRecieve,
49 },
50 },
51 }
52}
53
54func postRecieve(ctx context.Context, cmd *cli.Command) error {
55 gitDir := cmd.String("git-dir")
56 userDid := cmd.String("user-did")
57 userHandle := cmd.String("user-handle")
58 endpoint := cmd.String("internal-api")
59 pushOptions := cmd.StringSlice("push-option")
60
61 payloadReader := bufio.NewReader(os.Stdin)
62 payload, _ := payloadReader.ReadString('\n')
63
64 client := &http.Client{}
65
66 req, err := http.NewRequest("POST", "http://"+endpoint+"/hooks/post-receive", strings.NewReader(payload))
67 if err != nil {
68 return fmt.Errorf("failed to create request: %w", err)
69 }
70
71 req.Header.Set("Content-Type", "text/plain; charset=utf-8")
72 req.Header.Set("X-Git-Dir", gitDir)
73 req.Header.Set("X-Git-User-Did", userDid)
74 req.Header.Set("X-Git-User-Handle", userHandle)
75 if pushOptions != nil {
76 for _, option := range pushOptions {
77 req.Header.Add("X-Git-Push-Option", option)
78 }
79 }
80
81 resp, err := client.Do(req)
82 if err != nil {
83 return fmt.Errorf("failed to execute request: %w", err)
84 }
85 defer resp.Body.Close()
86
87 if resp.StatusCode != http.StatusOK {
88 return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
89 }
90
91 return nil
92}