forked from mislav/hub
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ci_status.go
205 lines (170 loc) · 4.36 KB
/
ci_status.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package commands
import (
"fmt"
"os"
"sort"
"github.com/github/hub/git"
"github.com/github/hub/github"
"github.com/github/hub/ui"
"github.com/github/hub/utils"
)
var cmdCiStatus = &Command{
Run: ciStatus,
Usage: "ci-status [-v] [<COMMIT>]",
Long: `Display status of GitHub checks for a commit.
## Options:
-v, --verbose
Print detailed report of all status checks and their URLs.
-f, --format <FORMAT>
Pretty print all status checks using <FORMAT> (implies '--verbose'). See the
"PRETTY FORMATS" section of git-log(1) for some additional details on how
placeholders are used in format. The available placeholders for issues are:
%U: the URL of this status check
%S: check state (e.g. "success", "failure")
%sC: set color to red, green, or yellow, depending on state
%t: name of the status check
--color[=<WHEN>]
Enable colored output even if stdout is not a terminal. <WHEN> can be one
of "always" (default for '--color'), "never", or "auto" (default).
<COMMIT>
A commit SHA or branch name (default: "HEAD").
Possible outputs and exit statuses:
- success, neutral: 0
- failure, error, action_required, cancelled, timed_out: 1
- pending: 2
## See also:
hub-pull-request(1), hub(1)
`,
}
var severityList []string
func init() {
CmdRunner.Use(cmdCiStatus)
severityList = []string{
"neutral",
"success",
"pending",
"cancelled",
"timed_out",
"action_required",
"failure",
"error",
}
}
func checkSeverity(targetState string) int {
for i, state := range severityList {
if state == targetState {
return i
}
}
return -1
}
func ciStatus(cmd *Command, args *Args) {
ref := "HEAD"
if !args.IsParamsEmpty() {
ref = args.RemoveParam(0)
}
localRepo, err := github.LocalRepo()
utils.Check(err)
project, err := localRepo.MainProject()
utils.Check(err)
sha, err := git.Ref(ref)
if err != nil {
err = fmt.Errorf("Aborted: no revision could be determined from '%s'", ref)
}
utils.Check(err)
if args.Noop {
ui.Printf("Would request CI status for %s\n", sha)
} else {
gh := github.NewClient(project.Host)
response, err := gh.FetchCIStatus(project, sha)
utils.Check(err)
state := ""
if len(response.Statuses) > 0 {
for _, status := range response.Statuses {
if checkSeverity(status.State) > checkSeverity(state) {
state = status.State
}
}
}
var exitCode int
switch state {
case "success", "neutral":
exitCode = 0
case "failure", "error", "action_required", "cancelled", "timed_out":
exitCode = 1
case "pending":
exitCode = 2
default:
exitCode = 3
}
verbose := args.Flag.Bool("--verbose") || args.Flag.HasReceived("--format")
if verbose && len(response.Statuses) > 0 {
colorize := colorizeOutput(args.Flag.HasReceived("--color"), args.Flag.Value("--color"))
ciVerboseFormat(response.Statuses, args.Flag.Value("--format"), colorize)
} else {
if state != "" {
ui.Println(state)
} else {
ui.Println("no status")
}
}
os.Exit(exitCode)
}
}
func ciVerboseFormat(statuses []github.CIStatus, formatString string, colorize bool) {
contextWidth := 0
for _, status := range statuses {
if len(status.Context) > contextWidth {
contextWidth = len(status.Context)
}
}
sort.SliceStable(statuses, func(a, b int) bool {
return stateRank(statuses[a].State) < stateRank(statuses[b].State)
})
for _, status := range statuses {
var color int
var stateMarker string
switch status.State {
case "success":
stateMarker = "✔︎"
color = 32
case "failure", "error", "action_required", "cancelled", "timed_out":
stateMarker = "✖︎"
color = 31
case "neutral":
stateMarker = "◦"
color = 30
case "pending":
stateMarker = "●"
color = 33
}
placeholders := map[string]string{
"S": status.State,
"sC": "",
"t": status.Context,
"U": status.TargetUrl,
}
if colorize {
placeholders["sC"] = fmt.Sprintf("\033[%dm", color)
}
format := formatString
if format == "" {
if status.TargetUrl == "" {
format = fmt.Sprintf("%%sC%s%%Creset\t%%t\n", stateMarker)
} else {
format = fmt.Sprintf("%%sC%s%%Creset\t%%<(%d)%%t\t%%U\n", stateMarker, contextWidth)
}
}
ui.Print(ui.Expand(format, placeholders, colorize))
}
}
func stateRank(state string) uint32 {
switch state {
case "failure", "error", "action_required", "cancelled", "timed_out":
return 1
case "success", "neutral":
return 3
default:
return 2
}
}