blob: b0e47b69f16f48245df85ee7f74e225670fd72e5 [file] [log] [blame]
// Copyright 2016 The Chromium 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 (
"flag"
"fmt"
"os"
"strings"
"presubmit"
"fuchsia.googlesource.com/jiri/gerrit"
)
var (
repoList string
logFilePath string
forceSend bool
dryRun bool
testName string
)
func init() {
flag.StringVar(&repoList, "repo", "", "Comma separated list of repos to query")
flag.StringVar(&logFilePath, "logfile", "/tmp/fuchsia-presubmit-log.json", "Full path of log file to use")
flag.BoolVar(&forceSend, "f", false, "Send all changes, even if they've already been sent")
flag.BoolVar(&dryRun, "n", false, "Query gerrit, log normally, but don't actually send to CI")
flag.StringVar(&testName, "test", "presubmit-test", "The name of the presubmit test job")
}
// sendNewChangesForTesting queries gerrit for new changes, where changes may be grouped into related
// sets that must be tested together (i.e. MultiPart changes.), then sends them for testing.
func sendNewChangesForTesting() error {
numberOfSentCLs := 0
defer func() {
fmt.Printf("Sent %d CLs for testing\n", numberOfSentCLs)
}()
// Grab handle to Gerrit.
gerritHandle, err := presubmit.CreateGerrit()
if err != nil {
return err
}
// Pick a CI worker.
var worker presubmit.Workflow
if dryRun {
fmt.Println("DRY RUN (will not send anything to CI, or write to Gerrit)")
worker = &DryRunCIWorker{}
} else {
worker = &JenkinsGerritCIWorker{}
}
// Don't send any changes if the presubmit test job is not configured properly. This is
// nice because `query` will keep track of the CLs it sends to presubmit and will not send
// them more than once. So this makes sure we're sending to a job that at least looks ready.
if err := worker.CheckPresubmitBuildConfig(); err != nil {
return fmt.Errorf("Refusing to test new CLs because of existing failures\n%v", err)
}
// Read previously found CLs.
var prevCLsMap gerrit.CLRefMap
if !forceSend {
fmt.Println("Using CL log:", logFilePath)
prevCLsMap, err = gerrit.ReadLog(logFilePath)
if err != nil {
return err
}
} else {
fmt.Println("Force sending all pending changes")
}
// Fetch pending CLs from Gerrit.
pendingCLs, err := gerritHandle.Query(presubmit.GerritQuery)
if err != nil {
return err
}
// Filter the list of CLs by repo.
var filteredCLs gerrit.CLList
if len(repoList) != 0 {
repoFilterList := map[string]bool{}
for _, repo := range strings.Split(repoList, ",") {
repoFilterList[repo] = true
}
for _, cl := range pendingCLs {
if repoFilterList[cl.Project] {
filteredCLs = append(filteredCLs, cl)
}
}
} else {
filteredCLs = pendingCLs
}
// Write the current list of pending CLs to a file.
err = gerrit.WriteLog(logFilePath, filteredCLs)
if err != nil {
return err
}
// Compare the previous CLs to the current list to determine which new CLs we must
// send for testing.
newCLs, errList := gerrit.NewOpenCLs(prevCLsMap, filteredCLs)
errMsg := ""
for _, e := range errList {
// NewOpenCLs may return errors detected when parsing MultiPart CL metadata.
errMsg += fmt.Sprintf("NewOpenCLs error: %v\n", e)
}
if len(errMsg) > 0 {
return fmt.Errorf(errMsg)
}
// Send the CLs for testing.
sender := presubmit.CLsSender{
CLLists: newCLs,
Worker: worker,
}
if err := sender.SendCLsToPresubmitTest(); err != nil {
return err
}
numberOfSentCLs = sender.CLsSent
return nil
}
// JenkinsGerritCIWorker implements a workflow for clsSender with jenkins as CI and gerrit for code review.
type JenkinsGerritCIWorker struct{}
func (jg *JenkinsGerritCIWorker) RemoveOutdatedBuilds(outdatedCLs map[presubmit.CLNumber]presubmit.Patchset) []error {
return presubmit.RemoveOutdatedBuilds(outdatedCLs)
}
func (jg *JenkinsGerritCIWorker) AddPresubmitTestBuild(cls gerrit.CLList) error {
return presubmit.AddPresubmitTestBuild(testName, cls)
}
func (jg *JenkinsGerritCIWorker) CheckPresubmitBuildConfig() error {
return presubmit.CheckPresubmitBuildConfig(testName)
}
func (jg *JenkinsGerritCIWorker) PostResults(message string, changes gerrit.CLList, score presubmit.VerifiedScore) error {
return presubmit.PostMessageToGerrit(message, changes, score)
}
// DryRunCIWorker implements a workflow for clsSender that does not try to do any CI. Useful if
// you're not running a Jenkins instance on your localhost.
type DryRunCIWorker struct{}
func (w *DryRunCIWorker) RemoveOutdatedBuilds(outdatedCLs map[presubmit.CLNumber]presubmit.Patchset) []error {
return nil
}
func (w *DryRunCIWorker) AddPresubmitTestBuild(cls gerrit.CLList) error {
return nil
}
func (w *DryRunCIWorker) CheckPresubmitBuildConfig() error {
return nil
}
func (w *DryRunCIWorker) PostResults(message string, changes gerrit.CLList, score presubmit.VerifiedScore) error {
return presubmit.InternalPostMessageToGerrit(message, changes, score,
func(ref string, msg string, labels map[string]string) error {
return nil
})
}
func main() {
flag.Parse()
if err := sendNewChangesForTesting(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}