about summary refs log tree commit diff
path: root/tools/gerrit-hook/buildkite.go
blob: 07d2cfd673e813fa3837a55a0f669a97885cfbcb (plain) (blame)
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
package main

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log/syslog"
	"net/http"
	"os"
	"strings"
	"time"
)

// https://buildkite.com/docs/apis/rest-api/builds#create-a-Build
type Build struct {
	Commit string            `json:"commit"`
	Branch string            `json:"branch"`
	Env    map[string]string `json:"env"`
}

type buildResponse struct {
	WebUrl string `json:"web_url"`
}

func triggerBuild(cfg *config, log *syslog.Writer, trigger *buildTrigger) error {
	env := make(map[string]string)
	branch := trigger.ref

	if trigger.changeId != "" && trigger.patchset != "" {
		env["GERRIT_CHANGE_ID"] = trigger.changeId
		env["GERRIT_CHANGE_URL"] = trigger.changeUrl
		env["GERRIT_PATCHSET"] = trigger.patchset

		branch = fmt.Sprintf("cl/%v", strings.Split(trigger.ref, "/")[3])
	}

	b := Build{
		Commit: trigger.commit,
		Branch: branch,
		Env:    env,
	}

	body, _ := json.Marshal(b)
	reader := ioutil.NopCloser(bytes.NewReader(body))

	bkUrl := fmt.Sprintf("https://api.buildkite.com/v2/organizations/%s/pipelines/%s/builds", cfg.BuildKiteOrganization, trigger.project)
	req, err := http.NewRequest("POST", bkUrl, reader)
	if err != nil {
		return fmt.Errorf("failed to create an HTTP request: %v", err)
	}

	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", cfg.BuildKiteToken))
	req.Header.Add("Content-Type", "application/json")

	// Let's budget this to 10 seconds maximum, this should be more
	// than enough, as we're only triggering the build, we're not
	// waiting on the status of the build
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	resp, err := http.DefaultClient.Do(req.WithContext(ctx))
	if err != nil {
		return fmt.Errorf("failed to send buildKite request: %v", err)
	}
	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("failed to parse buildKite response: %v", err)
	}

	if resp.StatusCode != http.StatusCreated {
		return fmt.Errorf("received a non-success response from buildKite: %s (%v)", respBody, resp.Status)
	}

	var buildResp buildResponse
	err = json.Unmarshal(respBody, &buildResp)
	if err != nil {
		return fmt.Errorf("failed to unmarshal build response: %v", err)
	}

	// Report the status back to the Gerrit CL so that users can click
	// through to the running build.
	msg := fmt.Sprintf("started build for patchset #%s on: %s", trigger.patchset, buildResp.WebUrl)
	review := reviewInput{
		Message:                        msg,
		OmitDuplicateComments:          true,
		Tag:                            "autogenerated:buildkite~trigger",
		IgnoreDefaultAttentionSetRules: true,
		Notify:                         "NONE",
	}
	updateGerrit(cfg, review, trigger.changeId, trigger.patchset)
	return nil
}

func postCommand(cfg *config) {
	changeId := os.Getenv("GERRIT_CHANGE_ID")
	patchSet := os.Getenv("GERRIT_PATCHSET")

	if changeId == "" || patchSet == "" {
		fmt.Println("nothing to do")
		return
	}

	// our build stage has the label :hammer:
	if os.Getenv("BUILDKITE_LABEL") != ":hammer:" {
		return
	}

	var vote int
	var verb string
	var notify string

	if os.Getenv("BUILDKITE_COMMAND_EXIT_STATUS") == "0" {
		vote = 1
		verb = "passed"
		notify = "NONE"
	} else {
		vote = -1
		verb = "failed"
		notify = "OWNER"
	}

	msg := fmt.Sprintf("Build of patchset %s %s: %s", patchSet, verb, os.Getenv("BUILDKITE_BUILD_URL"))
	review := reviewInput{
		Message:                        msg,
		OmitDuplicateComments:          true,
		IgnoreDefaultAttentionSetRules: vote == 1,
		Tag:                            "autogenerated:buildkite~result",
		Notify:                         notify,
		Labels: map[string]int{
			"Verified": vote,
		},
	}

	updateGerrit(cfg, review, changeId, patchSet)
}