github.com/bshelton229/agent@v3.5.4+incompatible/clicommand/annotate.go (about)

     1  package clicommand
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"time"
     7  
     8  	"github.com/buildkite/agent/stdin"
     9  
    10  	"github.com/buildkite/agent/agent"
    11  	"github.com/buildkite/agent/api"
    12  	"github.com/buildkite/agent/cliconfig"
    13  	"github.com/buildkite/agent/logger"
    14  	"github.com/buildkite/agent/retry"
    15  	"github.com/urfave/cli"
    16  )
    17  
    18  var AnnotateHelpDescription = `Usage:
    19  
    20     buildkite-agent annotate [<body>] [arguments...]
    21  
    22  Description:
    23  
    24     Build annotations allow you to customize the Buildkite build interface to
    25     show information that may surface from your builds. Some examples include:
    26  
    27     - Links to artifacts generated by your jobs
    28     - Test result summaries
    29     - Graphs that include analysis about your codebase
    30     - Helpful information for team members about what happened during a build
    31  
    32     Annotations are written in CommonMark-compliant Markdown, with "GitHub
    33     Flavored Markdown" extensions.
    34  
    35     The annotation body can be supplied as a command line argument, or by piping
    36     content into the command.
    37  
    38     You can update an existing annotation's body by running the annotate command
    39     again and provide the same context as the one you want to update. Or if you
    40     leave context blank, it will use the default context.
    41  
    42     You can also update just the style of an existing annotation by omitting the
    43     body entirely and providing a new style value.
    44  
    45  Example:
    46  
    47     $ buildkite-agent annotate "All tests passed! :rocket:"
    48     $ cat annotation.md | buildkite-agent annotate --style "warning"
    49     $ buildkite-agent annotate --style "success" --context "junit"
    50     $ ./script/dynamic_annotation_generator | buildkite-agent annotate --style "success"`
    51  
    52  type AnnotateConfig struct {
    53  	Body             string `cli:"arg:0" label:"annotation body"`
    54  	Style            string `cli:"style"`
    55  	Context          string `cli:"context"`
    56  	Append           bool   `cli:"append"`
    57  	Job              string `cli:"job" validate:"required"`
    58  	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
    59  	Endpoint         string `cli:"endpoint" validate:"required"`
    60  	NoColor          bool   `cli:"no-color"`
    61  	Debug            bool   `cli:"debug"`
    62  	DebugHTTP        bool   `cli:"debug-http"`
    63  }
    64  
    65  var AnnotateCommand = cli.Command{
    66  	Name:        "annotate",
    67  	Usage:       "Annotate the build page within the Buildkite UI with text from within a Buildkite job",
    68  	Description: AnnotateHelpDescription,
    69  	Flags: []cli.Flag{
    70  		cli.StringFlag{
    71  			Name:   "context",
    72  			Usage:  "The context of the annotation used to differentiate this annotation from others",
    73  			EnvVar: "BUILDKITE_ANNOTATION_CONTEXT",
    74  		},
    75  		cli.StringFlag{
    76  			Name:   "style",
    77  			Usage:  "The style of the annotation (`success`, `info`, `warning` or `error`)",
    78  			EnvVar: "BUILDKITE_ANNOTATION_STYLE",
    79  		},
    80  		cli.BoolFlag{
    81  			Name:   "append",
    82  			Usage:  "Append to the body of an existing annotation",
    83  			EnvVar: "BUILDKITE_ANNOTATION_APPEND",
    84  		},
    85  		cli.StringFlag{
    86  			Name:   "job",
    87  			Value:  "",
    88  			Usage:  "Which job should the annotation come from",
    89  			EnvVar: "BUILDKITE_JOB_ID",
    90  		},
    91  		AgentAccessTokenFlag,
    92  		EndpointFlag,
    93  		NoColorFlag,
    94  		DebugFlag,
    95  		DebugHTTPFlag,
    96  	},
    97  	Action: func(c *cli.Context) {
    98  		// The configuration will be loaded into this struct
    99  		cfg := AnnotateConfig{}
   100  
   101  		// Load the configuration
   102  		loader := cliconfig.Loader{CLI: c, Config: &cfg}
   103  		if err := loader.Load(); err != nil {
   104  			logger.Fatal("%s", err)
   105  		}
   106  
   107  		// Setup the any global configuration options
   108  		HandleGlobalFlags(cfg)
   109  
   110  		var body string
   111  		var err error
   112  
   113  		if cfg.Body != "" {
   114  			body = cfg.Body
   115  		} else if stdin.IsReadable() {
   116  			logger.Info("Reading annotation body from STDIN")
   117  
   118  			// Actually read the file from STDIN
   119  			stdin, err := ioutil.ReadAll(os.Stdin)
   120  			if err != nil {
   121  				logger.Fatal("Failed to read from STDIN: %s", err)
   122  			}
   123  
   124  			body = string(stdin[:])
   125  		}
   126  
   127  		// Create the API client
   128  		client := agent.APIClient{
   129  			Endpoint: cfg.Endpoint,
   130  			Token:    cfg.AgentAccessToken,
   131  		}.Create()
   132  
   133  		// Create the annotation we'll send to the Buildkite API
   134  		annotation := &api.Annotation{
   135  			Body:    body,
   136  			Style:   cfg.Style,
   137  			Context: cfg.Context,
   138  			Append:  cfg.Append,
   139  		}
   140  
   141  		// Retry the annotation a few times before giving up
   142  		err = retry.Do(func(s *retry.Stats) error {
   143  			// Attempt ot create the annotation
   144  			resp, err := client.Annotations.Create(cfg.Job, annotation)
   145  
   146  			// Don't bother retrying if the response was one of these statuses
   147  			if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404 || resp.StatusCode == 400) {
   148  				s.Break()
   149  				return err
   150  			}
   151  
   152  			// Show the unexpected error
   153  			if err != nil {
   154  				logger.Warn("%s (%s)", err, s)
   155  			}
   156  
   157  			return err
   158  		}, &retry.Config{Maximum: 5, Interval: 1 * time.Second, Jitter: true})
   159  
   160  		// Show a fatal error if we gave up trying to create the annotation
   161  		if err != nil {
   162  			logger.Fatal("Failed to annotate build: %s", err)
   163  		}
   164  
   165  		logger.Info("Successfully annotated build")
   166  	},
   167  }