forked from buildkite/agent
-
Notifications
You must be signed in to change notification settings - Fork 0
/
annotate.go
164 lines (135 loc) · 4.69 KB
/
annotate.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
package clicommand
import (
"io/ioutil"
"os"
"time"
"github.com/buildkite/agent/stdin"
"github.com/buildkite/agent/agent"
"github.com/buildkite/agent/api"
"github.com/buildkite/agent/cliconfig"
"github.com/buildkite/agent/logger"
"github.com/buildkite/agent/retry"
"github.com/urfave/cli"
)
var AnnotateHelpDescription = `Usage:
buildkite-agent annotate <file> [arguments...]
Description:
Build annotations allow you to customize the Buildkite build interface to
show information that may surface from your builds. Some examples include:
- Links to artifacts generated by your jobs
- Test result summaries
- Graphs that include analysis about your codebase
- Helpful information for team members about what happened during a build
Annotations are written in CommonMark-compliant Markdown, with "GitHub
Flavored Markdown" extensions.
You can update an existing annotation's body by running the annotate command
again and provide the same context as the one you want to update. Or if you
leave context blank, it will use the default context.
You can also just update the style of an existing annotation by omitting the
body and just providing a new style value.
Example:
$ buildkite-agent annotate "All tests passed! :rocket:"
$ cat annotation.md | buildkite-agent annotate --style "warning"
$ buildkite-agent annotate --style "success" --context "junit"
$ ./script/dynamic_annotation_generator | buildkite-agent annotate --style "success"`
type AnnotateConfig struct {
Body string `cli:"arg:0" label:"annotation body"`
Style string `cli:"style"`
Context string `cli:"context"`
Append bool `cli:"append"`
Job string `cli:"job" validate:"required"`
AgentAccessToken string `cli:"agent-access-token" validate:"required"`
Endpoint string `cli:"endpoint" validate:"required"`
NoColor bool `cli:"no-color"`
Debug bool `cli:"debug"`
DebugHTTP bool `cli:"debug-http"`
}
var AnnotateCommand = cli.Command{
Name: "annotate",
Usage: "Annotate the build page within the Buildkite UI with text from within a Buildkite job",
Description: AnnotateHelpDescription,
Flags: []cli.Flag{
cli.StringFlag{
Name: "context",
Usage: "The context of the annotation used to differentiate this annotation from others",
EnvVar: "BUILDKITE_ANNOTATION_CONTEXT",
},
cli.StringFlag{
Name: "style",
Usage: "The style of the annotation (`success`, `info`, `warning` or `error`)",
EnvVar: "BUILDKITE_ANNOTATION_STYLE",
},
cli.BoolFlag{
Name: "append",
Usage: "Append to the body of an existing annotation",
EnvVar: "BUILDKITE_ANNOTATION_APPEND",
},
cli.StringFlag{
Name: "job",
Value: "",
Usage: "Which job should the annotation come from",
EnvVar: "BUILDKITE_JOB_ID",
},
AgentAccessTokenFlag,
EndpointFlag,
NoColorFlag,
DebugFlag,
DebugHTTPFlag,
},
Action: func(c *cli.Context) {
// The configuration will be loaded into this struct
cfg := AnnotateConfig{}
// Load the configuration
loader := cliconfig.Loader{CLI: c, Config: &cfg}
if err := loader.Load(); err != nil {
logger.Fatal("%s", err)
}
// Setup the any global configuration options
HandleGlobalFlags(cfg)
var body string
var err error
if cfg.Body != "" {
body = cfg.Body
} else if stdin.IsReadable() {
logger.Info("Reading annotation body from STDIN")
// Actually read the file from STDIN
stdin, err := ioutil.ReadAll(os.Stdin)
if err != nil {
logger.Fatal("Failed to read from STDIN: %s", err)
}
body = string(stdin[:])
}
// Create the API client
client := agent.APIClient{
Endpoint: cfg.Endpoint,
Token: cfg.AgentAccessToken,
}.Create()
// Create the annotation we'll send to the Buildkite API
annotation := &api.Annotation{
Body: body,
Style: cfg.Style,
Context: cfg.Context,
Append: cfg.Append,
}
// Retry the annotation a few times before giving up
err = retry.Do(func(s *retry.Stats) error {
// Attempt ot create the annotation
resp, err := client.Annotations.Create(cfg.Job, annotation)
// Don't bother retrying if the response was one of these statuses
if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404 || resp.StatusCode == 400) {
s.Break()
return err
}
// Show the unexpected error
if err != nil {
logger.Warn("%s (%s)", err, s)
}
return err
}, &retry.Config{Maximum: 5, Interval: 1 * time.Second, Jitter: true})
// Show a fatal error if we gave up trying to create the annotation
if err != nil {
logger.Fatal("Failed to annotate build: %s", err)
}
logger.Info("Successfully annotated build")
},
}