blob: 143422fb3189b4b9e537f0f2afb44b7ef2397984 [file] [log] [blame]
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/golang/protobuf/proto"
"go.chromium.org/luci/cv/api/config/v2"
"go.skia.org/infra/go/common"
"go.skia.org/infra/go/cq"
"go.skia.org/infra/go/git"
"go.skia.org/infra/go/gitiles"
"go.skia.org/infra/go/sklog"
"go.skia.org/infra/task_scheduler/go/specs"
)
var (
configFile = flag.String("cfg-file", "", "commit-queue.cfg file to validate.")
remote = flag.String("remote", git.DefaultRemote, "Name of upstream remote to use.")
repoUrl = flag.String("repo", "", "Repo URL. Required if not currently inside a git checkout.")
)
func main() {
common.Init()
if *configFile == "" {
sklog.Fatal("--cfg-file is required.")
}
ctx := context.TODO()
// Find the branch heads.
cwd, err := os.Getwd()
if err != nil {
sklog.Fatalf("Failed to get current working directory: %s", err)
}
gd := git.GitDir(cwd)
if *repoUrl == "" {
*repoUrl, err = gd.Git(ctx, "remote", "get-url", *remote)
if err != nil {
sklog.Fatalf("--repo not provided and failed to obtain repo url for cwd: %s", err)
}
*repoUrl = strings.TrimSpace(*repoUrl)
}
repo := gitiles.NewRepo(*repoUrl, nil)
branches, err := repo.Branches(ctx)
if err != nil {
sklog.Fatal(err)
}
// For each branch head, read the tasks.json file.
taskCfgs := make(map[string]*specs.TasksCfg, len(branches))
for _, branch := range branches {
contents, err := repo.ReadFileAtRef(ctx, specs.TASKS_CFG_FILE, branch.Head)
if err != nil {
if strings.Contains(err.Error(), "404 Not Found") {
sklog.Warningf("Could not find %s on %s", specs.TASKS_CFG_FILE, branch.Name)
// This is valid; there are no trybots on this branch.
taskCfgs[branch.Name] = &specs.TasksCfg{
Jobs: map[string]*specs.JobSpec{},
}
continue
}
sklog.Fatalf("Failed to read tasks.json for %s; %s", branch.Name, err)
}
cfg, err := specs.ParseTasksCfg(string(contents))
if err != nil {
sklog.Fatalf("Failed to parse tasks.json: %s", err)
}
taskCfgs[branch.Name] = cfg
}
// Read the commit queue config.
cfgBytes, err := ioutil.ReadFile(*configFile)
if err != nil {
sklog.Fatalf("Failed to read %s: %s", *configFile, err)
}
var cfg config.Config
if err := proto.UnmarshalText(string(cfgBytes), &cfg); err != nil {
sklog.Fatalf("Failed to parse config proto: %s", err)
}
// For each branch, find the matching CQ config group. Ensure that the
// tryjobs exist in the TasksCfg for that branch.
badTryjobs := map[string][]string{}
for _, branch := range branches {
cg, _, _, err := cq.MatchConfigGroup(&cfg, fmt.Sprintf("refs/heads/%s", branch.Name))
if err != nil {
sklog.Fatalf("Failed to find matching CQ config for %q: %s", branch.Name, err)
}
taskCfg := taskCfgs[branch.Name]
if cg != nil && cg.Verifiers != nil && cg.Verifiers.Tryjob != nil {
for _, tj := range cg.Verifiers.Tryjob.Builders {
split := strings.Split(tj.Name, "/")
shortName := split[len(split)-1]
if strings.HasPrefix(tj.Name, "chromium") {
if branch.Name != git.MasterBranch {
// Chromium tryjobs only work on the main branch.
badTryjobs[branch.Name] = append(badTryjobs[branch.Name], shortName)
}
} else if _, ok := taskCfg.Jobs[shortName]; !ok {
badTryjobs[branch.Name] = append(badTryjobs[branch.Name], shortName)
}
}
} else if cg == nil {
sklog.Warningf("No matching CQ config for %s", branch.Name)
}
}
// Report the results.
if len(badTryjobs) > 0 {
fmt.Println("Found try jobs which don't exist on their respective branches:")
for branch, tryjobs := range badTryjobs {
fmt.Println(fmt.Sprintf("%s:", branch))
for _, tj := range tryjobs {
fmt.Println(fmt.Sprintf("\t%s", tj))
}
}
os.Exit(1)
}
}