aboutsummaryrefslogtreecommitdiff
path: root/tools/gerrit-hook/gerrit.go
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gerrit-hook/gerrit.go')
-rw-r--r--tools/gerrit-hook/gerrit.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/tools/gerrit-hook/gerrit.go b/tools/gerrit-hook/gerrit.go
new file mode 100644
index 0000000..6a23527
--- /dev/null
+++ b/tools/gerrit-hook/gerrit.go
@@ -0,0 +1,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)
+ }
+}