-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
148 lines (121 loc) · 3.25 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package main
import (
"context"
"flag"
"fmt"
"io"
"net/url"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"github.com/google/go-github/github"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/plumbing"
)
type pullRequest struct {
owner string
project string
id int
}
func parseURL(rawurl string) (*pullRequest, error) {
URL, err := url.Parse(rawurl)
if err != nil {
return nil, err
}
if URL.Hostname() != "github.com" {
return nil, fmt.Errorf("Only Github.com pull requests are supported")
}
elements := strings.Split(URL.Path, "/")
if !(len(elements) == 5 && elements[3] == "pull") {
return nil, fmt.Errorf("URL doesn't match expected format, eg. https://github.com/<owner>/<project>/pull/<ID>")
}
id, err := strconv.Atoi(elements[4])
if err != nil {
return nil, fmt.Errorf("URL doesn't have a numeric pull request ID")
}
return &pullRequest{owner: elements[1], project: elements[2], id: id}, nil
}
func (pr *pullRequest) commits(ctx context.Context, client *github.Client) ([]*github.RepositoryCommit, error) {
s := client.PullRequests
commits, _, err := s.ListCommits(ctx, pr.owner, pr.project, pr.id, &github.ListOptions{})
return commits, err
}
func openRepo(repopath string) (*git.Worktree, error) {
repo, err := git.PlainOpen(repopath)
if err != nil {
return nil, err
}
return repo.Worktree()
}
func run(cmd *exec.Cmd, wt *git.Worktree, commit *github.RepositoryCommit, out io.Writer) error {
fmt.Fprintf(out, "Checking out commit: %s\n", commit.GetSHA())
err := wt.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(commit.GetSHA()),
})
if err != nil {
return fmt.Errorf("git checkout, %s\n", err)
}
fmt.Fprintf(out, "Running: %s %s\n", cmd.Path, strings.Join(cmd.Args[1:], " "))
return cmd.Run()
}
func main() {
var (
help bool
url string
repopath string
cwd string
)
flag.BoolVar(&help, "h", false, "Display this help screen and quit")
flag.StringVar(&url, "u", "", "Pull request URL, eg: https://github.com/<owner>/<project>/pull/<ID>")
flag.StringVar(&repopath, "r", "", "Directory of cloned repository")
flag.StringVar(&cwd, "d", "", "Working directory, defaults to repository path")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s [options] cmd [args...]:\n", filepath.Base(os.Args[0]))
flag.PrintDefaults()
}
flag.Parse()
args := flag.Args()
if help {
flag.Usage()
os.Exit(2)
}
if url == "" || repopath == "" {
flag.Usage()
os.Exit(2)
}
if cwd == "" {
cwd = repopath
}
wt, err := openRepo(repopath)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
pr, err := parseURL(url)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
client := github.NewClient(nil)
commits, err := pr.commits(ctx, client)
for _, commit := range commits {
cmd := exec.Command(args[0], args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = cwd
if err := run(cmd, wt, commit, os.Stderr); err != nil {
fmt.Fprintf(os.Stderr, "error, %s\n", err)
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
os.Exit(status.ExitStatus())
}
}
os.Exit(1)
}
}
}