/
protector.go
146 lines (123 loc) · 3.64 KB
/
protector.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
package main
import (
"context"
"flag"
"fmt"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
currentVersion "github.com/jcgay/protector/version"
"os"
"regexp"
"sync"
)
const (
banner = "protector - %s (%s)\n"
)
var (
ghToken string
dryrun bool
version bool
unprotect bool
protectBranches []*regexp.Regexp
protectRepositories stringsFlag
orgs stringsFlag
)
type stringsFlag []string
func (s *stringsFlag) String() string {
return fmt.Sprintf("%s", *s)
}
func (s *stringsFlag) Set(value string) error {
*s = append(*s, value)
return nil
}
type repositoriesService interface {
GetBranch(ctx context.Context, owner, repo, branchName string) (*github.Branch, *github.Response, error)
ListBranches(ctx context.Context, owner string, repo string, opt *github.ListOptions) ([]*github.Branch, *github.Response, error)
UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *github.ProtectionRequest) (*github.Protection, *github.Response, error)
RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*github.Response, error)
}
func main() {
// parse flags
flag.StringVar(&ghToken, "token", "", "GitHub API token")
flag.BoolVar(&dryrun, "dry-run", false, "do not make any changes, just print out what would have been done")
flag.BoolVar(&version, "version", false, "print version and exit")
flag.BoolVar(&version, "v", false, "print version and exit (shorthand)")
flag.BoolVar(&unprotect, "free", false, "remove branch protection")
flag.Var(&protectRepositories, "repos", "repositories fullname to protect (ex: jcgay/maven-color)")
flag.Var(&orgs, "orgs", "organizations name to protect")
var branches stringsFlag
flag.Var(&branches, "branches", "branches to include (as regexp)")
flag.Usage = func() {
fmt.Fprint(os.Stderr, fmt.Sprintf(banner, currentVersion.VERSION, currentVersion.GITCOMMIT))
flag.PrintDefaults()
}
flag.Parse()
if version {
fmt.Printf("%s (%s)", currentVersion.VERSION, currentVersion.GITCOMMIT)
os.Exit(0)
}
if ghToken == "" {
usageAndExit("GitHub token cannot be empty.", 1)
}
if len(orgs) > 0 && len(protectRepositories) > 0 {
usageAndExit("Can't filter repositories by name and organization at the same time", 1)
}
protectBranches = make([]*regexp.Regexp, 0)
for _, branch := range branches {
protectBranches = append(protectBranches, regexp.MustCompile(branch))
}
if len(protectBranches) == 0 {
protectBranches = append(protectBranches, regexp.MustCompile("^master$"))
}
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: ghToken},
)
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)
var ghr repositories
if len(protectRepositories) > 0 {
ghr = &selectedGitHubRepositories{
client: client,
selectedRepos: protectRepositories,
}
} else if len(orgs) > 0 {
ghr = &orgsGitHubRepositories{
client: client,
orgs: orgs,
}
} else {
ghr = &allGitHubRepositories{
client: client,
}
}
repos := ghr.fetch()
gp := &githubProtection{
repositoriesService: client.Repositories,
branchPatterns: protectBranches,
successOutput: os.Stdout,
failureOutput: os.Stderr,
}
var wg sync.WaitGroup
for repo := range repos {
wg.Add(1)
go func(repository *github.Repository) {
defer wg.Done()
if unprotect {
gp.free(repository)
} else {
gp.protect(repository)
}
}(repo)
}
wg.Wait()
os.Exit(0)
}
func usageAndExit(message string, exitCode int) {
if message != "" {
fmt.Fprintf(os.Stderr, message)
fmt.Fprint(os.Stderr, "\n\n")
}
flag.Usage()
fmt.Fprint(os.Stderr, "\n")
os.Exit(exitCode)
}