blob: 96f7e31a76fce185272c3fd2c2a717ea08d73858 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"net/url"
"sort"
"v.io/jiri"
"v.io/jiri/gerrit"
"v.io/x/devtools/internal/test"
)
func gerritBaseUrl() (*url.URL, error) {
u, err := url.Parse(gerritBaseUrlFlag)
if err != nil {
return nil, fmt.Errorf("invalid gerrit url %q: %v", gerritBaseUrlFlag, err)
}
return u, nil
}
func genStartPresubmitBuildLink(strRefs, strProjects, strTests string) string {
return fmt.Sprintf("%s/%s/buildWithParameters?REFS=%s&PROJECTS=%s&TESTS=%s",
jenkinsBaseJobUrl,
presubmitTestJobFlag,
url.QueryEscape(strRefs),
url.QueryEscape(strProjects),
url.QueryEscape(strTests))
}
// postMessage posts the given message to Gerrit.
func postMessage(jirix *jiri.X, message string, refs []string, success bool) error {
refsUsingVerifiedLabel, err := getRefsUsingVerifiedLabel(jirix)
if err != nil {
return err
}
value := "1"
if !success {
value = "-" + value
}
for _, ref := range refs {
labels := map[string]string{}
if _, ok := refsUsingVerifiedLabel[ref]; ok {
labels["Verified"] = value
}
gUrl, err := gerritBaseUrl()
if err != nil {
return err
}
if err := jirix.Gerrit(gUrl).PostReview(ref, message, labels); err != nil {
return err
}
test.Pass(jirix.Context, "review posted for %q with labels %v.\n", ref, labels)
}
return nil
}
func getRefsUsingVerifiedLabel(jirix *jiri.X) (map[string]struct{}, error) {
// Query all open CLs.
gUrl, err := gerritBaseUrl()
if err != nil {
return nil, err
}
cls, err := jirix.Gerrit(gUrl).Query(defaultQueryString)
if err != nil {
return nil, err
}
// Identify the refs that use the "Verified" label.
ret := map[string]struct{}{}
for _, cl := range cls {
if _, ok := cl.Labels["Verified"]; ok {
ret[cl.Reference()] = struct{}{}
}
}
return ret, nil
}
// getSubmittableCLs extracts CLs that have the AutoSubmit label in the commit
// message and satisfy all the submit rules. If a CL is part of a multi-part CLs
// set, all the CLs in that set need to be submittable. It returns a list of
// CLLists each of which is either a single CL or a multi-part CLs set.
func getSubmittableCLs(jirix *jiri.X, cls gerrit.CLList) []gerrit.CLList {
submittableCLs := []gerrit.CLList{}
multiPartCLs := map[string]*gerrit.MultiPartCLSet{}
for _, cl := range cls {
// Check whether a CL satisfies all the submit rules. We do this by checking
// the states of all its labels.
//
// cl.Labels has the following data structure:
// {
// "Code-Review": {
// "approved": struct{}{},
// },
// "Verified": {
// "rejected": struct{}{},
// }
// ...
// }
// A label is satisfied/green when it has an "approved" entry.
allLabelsApproved := true
for label, labelData := range cl.Labels {
// We only check the following labels which might not exist
// at the same time.
switch label {
case "Code-Review", "Verified", "Non-Author-Code-Review", "To-Be-Reviewed":
isApproved := false
for state := range labelData {
if state == "approved" {
isApproved = true
break
}
}
if !isApproved {
allLabelsApproved = false
break
}
}
}
if cl.AutoSubmit && allLabelsApproved {
if cl.MultiPart != nil {
topic := cl.MultiPart.Topic
if _, ok := multiPartCLs[topic]; !ok {
multiPartCLs[topic] = gerrit.NewMultiPartCLSet()
}
multiPartCLs[topic].AddCL(cl)
} else {
submittableCLs = append(submittableCLs, gerrit.CLList{cl})
}
}
}
// This is to make sure we have consistent results order.
sortedTopics := []string{}
for topic := range multiPartCLs {
sortedTopics = append(sortedTopics, topic)
}
sort.Strings(sortedTopics)
// Find complete multi part cl sets.
for _, topic := range sortedTopics {
set := multiPartCLs[topic]
if set.Complete() {
submittableCLs = append(submittableCLs, set.CLs())
}
}
return submittableCLs
}
// submitCLs submits the given CLs.
func submitCLs(jirix *jiri.X, cls gerrit.CLList) error {
for _, cl := range cls {
curRef := cl.Reference()
msg := fmt.Sprintf("submit CL: %s\n", curRef)
gUrl, err := gerritBaseUrl()
if err != nil {
return err
}
if err := jirix.Gerrit(gUrl).Submit(cl.Change_id); err != nil {
test.Fail(jirix.Context, msg)
fmt.Fprintf(jirix.Stderr(), "%v\n", err)
if err := postMessage(jirix, fmt.Sprintf("Failed to submit CL:\n%v\n", err), []string{curRef}, true); err != nil {
return err
}
} else {
test.Pass(jirix.Context, msg)
}
}
return nil
}
// testNameWithPartSuffix generates a new name from the given test name and part index.
func testNameWithPartSuffix(testName string, partIndex int) string {
if partIndex < 0 {
return testName
}
return fmt.Sprintf("%s-part%d", testName, partIndex)
}