in prow/tide/status.go [119:254]
func requirementDiff(pr *PullRequest, q *config.TideQuery, cc contextChecker) (string, int) {
const maxLabelChars = 50
var desc string
var diff int
// Drops labels if needed to fit the description text area, but keep at least 1.
truncate := func(labels []string) []string {
i := 1
chars := len(labels[0])
for ; i < len(labels); i++ {
if chars+len(labels[i]) > maxLabelChars {
break
}
chars += len(labels[i]) + 2 // ", "
}
return labels[:i]
}
// Weight incorrect branches with very high diff so that we select the query
// for the correct branch.
targetBranchDenied := false
for _, excludedBranch := range q.ExcludedBranches {
if string(pr.BaseRef.Name) == excludedBranch {
targetBranchDenied = true
break
}
}
// if no allowlist is configured, the target is OK by default
targetBranchAllowed := len(q.IncludedBranches) == 0
for _, includedBranch := range q.IncludedBranches {
if string(pr.BaseRef.Name) == includedBranch {
targetBranchAllowed = true
break
}
}
if targetBranchDenied || !targetBranchAllowed {
diff += 2000
if desc == "" {
desc = fmt.Sprintf(" Merging to branch %s is forbidden.", pr.BaseRef.Name)
}
}
qAuthor := github.NormLogin(q.Author)
prAuthor := github.NormLogin(string(pr.Author.Login))
// Weight incorrect author with very high diff so that we select the query
// for the correct author.
if qAuthor != "" && prAuthor != qAuthor {
diff += 1000
if desc == "" {
desc = fmt.Sprintf(" Must be by author %s.", qAuthor)
}
}
// Weight incorrect milestone with relatively high diff so that we select the
// query for the correct milestone (but choose favor query for correct branch).
if q.Milestone != "" && (pr.Milestone == nil || string(pr.Milestone.Title) != q.Milestone) {
diff += 100
if desc == "" {
desc = fmt.Sprintf(" Must be in milestone %s.", q.Milestone)
}
}
// Weight incorrect labels and statues with low (normal) diff values.
var missingLabels []string
for _, l1 := range q.Labels {
var found bool
for _, l2 := range pr.Labels.Nodes {
if string(l2.Name) == l1 {
found = true
break
}
}
if !found {
missingLabels = append(missingLabels, l1)
}
}
diff += len(missingLabels)
if desc == "" && len(missingLabels) > 0 {
sort.Strings(missingLabels)
trunced := truncate(missingLabels)
if len(trunced) == 1 {
desc = fmt.Sprintf(" Needs %s label.", trunced[0])
} else {
desc = fmt.Sprintf(" Needs %s labels.", strings.Join(trunced, ", "))
}
}
var presentLabels []string
for _, l1 := range q.MissingLabels {
for _, l2 := range pr.Labels.Nodes {
if string(l2.Name) == l1 {
presentLabels = append(presentLabels, l1)
break
}
}
}
diff += len(presentLabels)
if desc == "" && len(presentLabels) > 0 {
sort.Strings(presentLabels)
trunced := truncate(presentLabels)
if len(trunced) == 1 {
desc = fmt.Sprintf(" Should not have %s label.", trunced[0])
} else {
desc = fmt.Sprintf(" Should not have %s labels.", strings.Join(trunced, ", "))
}
}
// fixing label issues takes precedence over status contexts
var contexts []string
log := logrus.WithFields(pr.logFields())
for _, commit := range pr.Commits.Nodes {
if commit.Commit.OID == pr.HeadRefOID {
for _, ctx := range unsuccessfulContexts(append(commit.Commit.Status.Contexts, checkRunNodesToContexts(log, commit.Commit.StatusCheckRollup.Contexts.Nodes)...), cc, log) {
contexts = append(contexts, string(ctx.Context))
}
}
}
diff += len(contexts)
if desc == "" && len(contexts) > 0 {
sort.Strings(contexts)
trunced := truncate(contexts)
if len(trunced) == 1 {
desc = fmt.Sprintf(" Job %s has not succeeded.", trunced[0])
} else {
desc = fmt.Sprintf(" Jobs %s have not succeeded.", strings.Join(trunced, ", "))
}
}
if q.ReviewApprovedRequired && pr.ReviewDecision != githubql.PullRequestReviewDecisionApproved {
diff += 50
if desc == "" {
desc = " PullRequest is missing sufficient approving GitHub review(s)"
}
}
return desc, diff
}