func()

in cmd/gopherbot/gopherbot.go [1983:2091]


func (b *gopherbot) assignReviewersToCLs(ctx context.Context) error {
	const tagNoOwners = "no-owners"
	b.corpus.Gerrit().ForeachProjectUnsorted(func(gp *maintner.GerritProject) error {
		if gp.Project() == "scratch" || gp.Server() != "go.googlesource.com" {
			return nil
		}
		gp.ForeachOpenCL(func(cl *maintner.GerritCL) error {
			if cl.Private || cl.WorkInProgress() || time.Since(cl.Created) < 10*time.Minute {
				return nil
			}
			if assignReviewersOptOut[cl.Owner().Email()] {
				return nil
			}

			// Don't auto-assign reviewers to CLs on shared branches;
			// the presumption is that developers there will know which
			// reviewers to assign.
			if strings.HasPrefix(cl.Branch(), "dev.") {
				return nil
			}

			tags := cl.Meta.Hashtags()
			if tags.Contains(tagNoOwners) {
				return nil
			}

			gc := gerritChange{gp.Project(), cl.Number}
			if b.deletedChanges[gc] {
				return nil
			}
			if strutil.ContainsFold(cl.Commit.Msg, "do not submit") || strutil.ContainsFold(cl.Commit.Msg, "do not review") {
				return nil
			}

			currentReviewers, ok := b.humanReviewersOnChange(ctx, gc, cl)
			if ok {
				return nil
			}

			changeURL := fmt.Sprintf("https://go-review.googlesource.com/c/%s/+/%d", gp.Project(), cl.Number)
			log.Printf("No reviewers or cc: %s", changeURL)
			files, err := b.gerrit.ListFiles(ctx, gc.ID(), cl.Commit.Hash.String())
			if err != nil {
				log.Printf("Could not get change %+v: %v", gc, err)
				if httpErr, ok := err.(*gerrit.HTTPError); ok && httpErr.Res.StatusCode == http.StatusNotFound {
					b.deletedChanges[gc] = true
				}
				return nil
			}

			var paths []string
			for f := range files {
				if f == "/COMMIT_MSG" {
					continue
				}
				paths = append(paths, gp.Project()+"/"+f)
			}

			entries, err := getCodeOwners(ctx, paths)
			if err != nil {
				log.Printf("Could not get owners for change %s: %v", changeURL, err)
				return nil
			}

			authorEmail := cl.Commit.Author.Email()
			merged := mergeOwnersEntries(entries, authorEmail)
			if len(merged.Primary) == 0 && len(merged.Secondary) == 0 {
				// No owners found for the change. Add the #no-owners tag.
				log.Printf("Adding no-owners tag to change %s...", changeURL)
				if *dryRun {
					return nil
				}
				if _, err := b.gerrit.AddHashtags(ctx, gc.ID(), tagNoOwners); err != nil {
					log.Printf("Could not add hashtag to change %q: %v", gc.ID(), err)
					return nil
				}
				return nil
			}

			// Assign reviewers.
			var review gerrit.ReviewInput
			for _, owner := range merged.Primary {
				review.Reviewers = append(review.Reviewers, gerrit.ReviewerInput{Reviewer: owner.GerritEmail})
			}
			for _, owner := range merged.Secondary {
				review.Reviewers = append(review.Reviewers, gerrit.ReviewerInput{Reviewer: owner.GerritEmail, State: "CC"})
			}

			// If the reviewers that would be set are the same as the existing
			// reviewers (minus the bots), there is no work to be done.
			if sameReviewers(currentReviewers, review) {
				log.Printf("Setting review %+v on %s would have no effect, continuing", review, changeURL)
				return nil
			}
			if *dryRun {
				log.Printf("[dry run] Would set review on %s: %+v", changeURL, review)
				return nil
			}
			log.Printf("Setting review on %s: %+v", changeURL, review)
			if err := b.gerrit.SetReview(ctx, gc.ID(), "current", review); err != nil {
				log.Printf("Could not set review for change %q: %v", gc.ID(), err)
				return nil
			}
			return nil
		})
		return nil
	})
	return nil
}