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
|
package main
import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"log/syslog"
"net/http"
"os"
"regexp"
"strconv"
"time"
)
// Regular expression to extract change ID out of a URL
var changeIdRegexp = regexp.MustCompile(`^.*/(\d+)$`)
func gerritHookMain(cfg *config, log *syslog.Writer, trigger *buildTrigger) {
if trigger == nil {
os.Exit(0)
}
err := triggerBuild(cfg, log, trigger)
if err != nil {
log.Err(fmt.Sprintf("failed to trigger Buildkite build: %s", err))
os.Exit(1)
}
}
type reviewInput struct {
Message string `json:"message"`
Labels map[string]int `json:"labels,omitempty"`
OmitDuplicateComments bool `json:"omit_duplicate_comments"`
IgnoreDefaultAttentionSetRules bool `json:"ignore_default_attention_set_rules"`
Tag string `json:"tag"`
Notify string `json:"notify,omitempty"`
}
type buildTrigger struct {
project string
change string
kind string
changeUrl string
changeOwner string
changeOwnerUserName string
branch string
topic string
uploader string
uploaderUserName string
commit string
patchset string
changeId string
ref string
}
// https://gerrit.googlesource.com/plugins/hooks/+/HEAD/src/main/resources/Documentation/hooks.md#patchset_created
func triggerForPatchsetCreated() (*buildTrigger, error) {
var trigger buildTrigger
flag.StringVar(&trigger.project, "project", "", "Gerrit project")
flag.StringVar(&trigger.change, "change", "", "Gerrit change")
flag.StringVar(&trigger.kind, "kind", "", "Gerrit kind")
flag.StringVar(&trigger.changeUrl, "change-url", "", "Gerrit URL for the change")
flag.StringVar(&trigger.changeOwner, "change-owner", "", "Gerrit owner")
flag.StringVar(&trigger.changeOwnerUserName, "change-owner-username", "", "Gerrit username")
flag.StringVar(&trigger.branch, "branch", "", "name of the branch")
flag.StringVar(&trigger.topic, "topic", "", "name of the topic")
flag.StringVar(&trigger.uploader, "uploader", "", "name ofthe uploader")
flag.StringVar(&trigger.uploaderUserName, "uploader-username", "", "")
flag.StringVar(&trigger.commit, "commit", "", "")
flag.StringVar(&trigger.patchset, "patchset", "", "")
flag.Parse()
// for now we only care about the project named `world' and the
// branch named 'main'
if trigger.project != "world" || trigger.branch != "main" {
return nil, nil
}
// We only care about patchset that are actually modifying the
// code. See
// https://gerrit-review.googlesource.com/Documentation/config-labels.html
if trigger.kind == "NO_CODE_CHANGE" || trigger.kind == "NO_CHANGE" {
return nil, nil
}
// extract the changeId from the URL
matches := changeIdRegexp.FindStringSubmatch(trigger.changeUrl)
trigger.changeId = matches[1]
// build the ref
changeId, _ := strconv.Atoi(trigger.changeId)
trigger.ref = fmt.Sprintf(
"refs/changes/%02d/%s/%s",
changeId%100, trigger.changeId, trigger.patchset,
)
return &trigger, nil
}
// after triggering a build with buildKite, we update gerrit to add a
// comment that links to the build.
func updateGerrit(cfg *config, review reviewInput, changeId string, patchSet string) {
body, err := json.Marshal(review)
if err != nil {
log.Fatal(fmt.Sprintf("failed to marshal gerrit update: %v", err))
os.Exit(1)
}
reader := ioutil.NopCloser(bytes.NewReader(body))
url := fmt.Sprintf("%s/a/changes/%s/revisions/%s/review", cfg.GerritUrl, changeId, patchSet)
req, err := http.NewRequest("POST", url, reader)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create an HTTP request: %v", err)
os.Exit(1)
}
req.SetBasicAuth(cfg.GerritUser, cfg.GerritPassword)
req.Header.Add("Content-Type", "application/json")
// Let's budget this to 10 seconds maximum, this should be more
// than enough to add a comment to gerrit.
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
if err != nil {
fmt.Fprintf(os.Stderr, "failed to send gerrit request: %v", err)
os.Exit(1)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
respBody, _ := ioutil.ReadAll(resp.Body)
fmt.Fprintf(os.Stderr, "failed to update gerrit: %s: %s ", respBody, resp.Status)
} else {
fmt.Printf("added link to CI build to %s", patchSet)
}
}
|