http.go
·
85 lines
·
2.1 KB
| 1 | package git |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "context" |
| 6 | "errors" |
| 7 | "fmt" |
| 8 | "io" |
| 9 | "os" |
| 10 | "os/exec" |
| 11 | "strings" |
| 12 | "time" |
| 13 | ) |
| 14 | |
| 15 | var ErrUnsupportedService = errors.New("unsupported git service") |
| 16 | |
| 17 | func subcommandForService(service string) (string, error) { |
| 18 | switch service { |
| 19 | case "git-upload-pack": |
| 20 | return "upload-pack", nil |
| 21 | case "git-receive-pack": |
| 22 | return "receive-pack", nil |
| 23 | default: |
| 24 | return "", ErrUnsupportedService |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | func pktLine(payload string) string { |
| 29 | return fmt.Sprintf("%04x%s", len(payload)+4, payload) |
| 30 | } |
| 31 | |
| 32 | func AdvertiseRefs(ctx context.Context, repoPath, service string, out io.Writer) error { |
| 33 | subcmd, err := subcommandForService(service) |
| 34 | if err != nil { |
| 35 | return err |
| 36 | } |
| 37 | |
| 38 | cctx, cancel := context.WithTimeout(ctx, 30*time.Second) |
| 39 | defer cancel() |
| 40 | |
| 41 | var stdout, stderr bytes.Buffer |
| 42 | cmd := exec.CommandContext(cctx, "git", subcmd, "--stateless-rpc", "--advertise-refs", repoPath) |
| 43 | cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0", "LC_ALL=C") |
| 44 | cmd.Stdout = &stdout |
| 45 | cmd.Stderr = &stderr |
| 46 | |
| 47 | if err := cmd.Run(); err != nil { |
| 48 | return fmt.Errorf("git %s advertise-refs: %w (%s)", subcmd, err, strings.TrimSpace(stderr.String())) |
| 49 | } |
| 50 | |
| 51 | header := pktLine(fmt.Sprintf("# service=%s\n", service)) + "0000" |
| 52 | if _, err := io.WriteString(out, header); err != nil { |
| 53 | return err |
| 54 | } |
| 55 | _, err = out.Write(stdout.Bytes()) |
| 56 | return err |
| 57 | } |
| 58 | |
| 59 | func ServiceRPC(ctx context.Context, repoPath, service string, in io.Reader, out io.Writer) error { |
| 60 | subcmd, err := subcommandForService(service) |
| 61 | if err != nil { |
| 62 | return err |
| 63 | } |
| 64 | |
| 65 | cctx, cancel := context.WithTimeout(ctx, 2*time.Minute) |
| 66 | defer cancel() |
| 67 | |
| 68 | var stderr bytes.Buffer |
| 69 | cmd := exec.CommandContext(cctx, "git", subcmd, "--stateless-rpc", repoPath) |
| 70 | cmd.Env = append(os.Environ(), "GIT_TERMINAL_PROMPT=0", "LC_ALL=C") |
| 71 | cmd.Stdin = in |
| 72 | cmd.Stdout = out |
| 73 | cmd.Stderr = &stderr |
| 74 | |
| 75 | if err := cmd.Run(); err != nil { |
| 76 | return fmt.Errorf("git %s stateless-rpc: %w (%s)", subcmd, err, strings.TrimSpace(stderr.String())) |
| 77 | } |
| 78 | |
| 79 | if service == "git-receive-pack" { |
| 80 | _, _ = EnsureDefaultBranch(ctx, repoPath, "main") |
| 81 | _, _ = runGit(ctx, repoPath, 5*time.Second, "update-server-info") |
| 82 | } |
| 83 | |
| 84 | return nil |
| 85 | } |
| 86 |