func handle()

in prow/plugins/bugzilla/bugzilla.go [580:792]


func handle(e event, gc githubClient, bc bugzilla.Client, options plugins.BugzillaBranchOptions, log *logrus.Entry, allRepos sets.String) error {
	comment := e.comment(gc)
	// check if bug is part of a restricted group
	if !e.missing {
		bug, err := getBug(bc, e.bugId, log, comment)
		if err != nil || bug == nil {
			return err
		}
		if !isBugAllowed(bug, options.AllowedGroups) {
			// ignore bugs that are in non-allowed groups for this repo
			if e.opened || refreshCommandMatch.MatchString(e.body) {
				response := fmt.Sprintf(bugLink+" is in a bug group that is not in the allowed groups for this repo.", e.bugId, bc.Endpoint(), e.bugId)
				if len(options.AllowedGroups) > 0 {
					response += "\nAllowed groups for this repo are:"
					for _, group := range options.AllowedGroups {
						response += "\n- " + group
					}
				} else {
					response += " There are no allowed bug groups configured for this repo."
				}
				return comment(response)
			}
			return nil
		}
	}
	// merges follow a different pattern from the normal validation
	if e.merged {
		return handleMerge(e, gc, bc, options, log, allRepos)
	}
	// close events follow a different pattern from the normal validation
	if e.closed && !e.merged {
		return handleClose(e, gc, bc, options, log)
	}
	// cherrypicks follow a different pattern than normal validation
	if e.cherrypick {
		return handleCherrypick(e, gc, bc, options, log)
	}

	var needsValidLabel, needsInvalidLabel bool
	var response, severityLabel string
	if e.missing {
		log.WithField("bugMissing", true)
		log.Debug("No bug referenced.")
		needsValidLabel, needsInvalidLabel = false, false
		response = `No Bugzilla bug is referenced in the title of this pull request.
To reference a bug, add 'Bug XXX:' to the title of this pull request and request another bug refresh with <code>/bugzilla refresh</code>.`
	} else {
		log = log.WithField("bugId", e.bugId)

		bug, err := getBug(bc, e.bugId, log, comment)
		if err != nil || bug == nil {
			return err
		}
		severityLabel = getSeverityLabel(bug.Severity)

		var dependents []bugzilla.Bug
		if options.DependentBugStates != nil || options.DependentBugTargetReleases != nil {
			for _, id := range bug.DependsOn {
				dependent, err := bc.GetBug(id)
				if err != nil {
					return comment(formatError(fmt.Sprintf("searching for dependent bug %d", id), bc.Endpoint(), e.bugId, err))
				}
				dependents = append(dependents, *dependent)
			}
		}

		valid, validationsRun, why := validateBug(*bug, dependents, options, bc.Endpoint())
		needsValidLabel, needsInvalidLabel = valid, !valid
		if valid {
			log.Debug("Valid bug found.")
			response = fmt.Sprintf(`This pull request references `+bugLink+`, which is valid.`, e.bugId, bc.Endpoint(), e.bugId)
			// if configured, move the bug to the new state
			if update := options.StateAfterValidation.AsBugUpdate(bug); update != nil {
				if err := bc.UpdateBug(e.bugId, *update); err != nil {
					log.WithError(err).Warn("Unexpected error updating Bugzilla bug.")
					return comment(formatError(fmt.Sprintf("updating to the %s state", options.StateAfterValidation), bc.Endpoint(), e.bugId, err))
				}
				response += fmt.Sprintf(" The bug has been moved to the %s state.", options.StateAfterValidation)
			}
			if options.AddExternalLink != nil && *options.AddExternalLink {
				changed, err := bc.AddPullRequestAsExternalBug(e.bugId, e.org, e.repo, e.number)
				if err != nil {
					log.WithError(err).Warn("Unexpected error adding external tracker bug to Bugzilla bug.")
					return comment(formatError("adding this pull request to the external tracker bugs", bc.Endpoint(), e.bugId, err))
				}
				if changed {
					response += " The bug has been updated to refer to the pull request using the external bug tracker."
				}
			}

			response += "\n\n<details>"
			if len(validationsRun) == 0 {
				response += "<summary>No validations were run on this bug</summary>"
			} else {
				response += fmt.Sprintf("<summary>%d validation(s) were run on this bug</summary>\n", len(validationsRun))
			}
			for _, validation := range validationsRun {
				response += fmt.Sprint("\n* ", validation)
			}
			response += "</details>"

			// identify qa contact via email if possible
			explicitQARequest := e.assign || e.cc
			if bug.QAContactDetail == nil {
				if explicitQARequest {
					response += fmt.Sprintf(bugLink+" does not have a QA contact, skipping assignment", e.bugId, bc.Endpoint(), e.bugId)
				}
			} else if bug.QAContactDetail.Email == "" {
				if explicitQARequest {
					response += fmt.Sprintf("QA contact for "+bugLink+" does not have a listed email, skipping assignment", e.bugId, bc.Endpoint(), e.bugId)
				}
			} else {
				query := &emailToLoginQuery{}
				email := bug.QAContactDetail.Email
				queryVars := map[string]interface{}{
					"email": githubql.String(email),
				}
				err := gc.Query(context.Background(), query, queryVars)
				if err != nil {
					log.WithError(err).Error("Failed to run graphql github query")
					return comment(formatError(fmt.Sprintf("querying GitHub for users with public email (%s)", email), bc.Endpoint(), e.bugId, err))
				}
				response += fmt.Sprint("\n\n", processQuery(query, email, log))
				if e.assign {
					response += "\n\n**DEPRECATION NOTICE**: The command `assign-qa` has been deprecated. Please use the `cc-qa` command instead."
				}
			}
		} else {
			log.Debug("Invalid bug found.")
			var formattedReasons string
			for _, reason := range why {
				formattedReasons += fmt.Sprintf(" - %s\n", reason)
			}
			response = fmt.Sprintf(`This pull request references `+bugLink+`, which is invalid:
%s
Comment <code>/bugzilla refresh</code> to re-evaluate validity if changes to the Bugzilla bug are made, or edit the title of this pull request to link to a different bug.`, e.bugId, bc.Endpoint(), e.bugId, formattedReasons)
		}
	}

	// ensure label state is correct. Do not propagate errors
	// as it is more important to report to the user than to
	// fail early on a label check.
	currentLabels, err := gc.GetIssueLabels(e.org, e.repo, e.number)
	if err != nil {
		log.WithError(err).Warn("Could not list labels on PR")
	}
	var hasValidLabel, hasInvalidLabel bool
	var severityLabelToRemove string
	for _, l := range currentLabels {
		if l.Name == labels.ValidBug {
			hasValidLabel = true
		}
		if l.Name == labels.InvalidBug {
			hasInvalidLabel = true
		}
		if l.Name == labels.BugzillaSeverityHigh ||
			l.Name == labels.BugzillaSeverityUrgent ||
			l.Name == labels.BugzillaSeverityMed ||
			l.Name == labels.BugzillaSeverityLow ||
			l.Name == labels.BugzillaSeverityUnspecified {
			severityLabelToRemove = l.Name
		}
	}

	if severityLabelToRemove != "" && severityLabel != severityLabelToRemove {
		if err := gc.RemoveLabel(e.org, e.repo, e.number, severityLabelToRemove); err != nil {
			log.WithError(err).Error("Failed to remove severity bug label.")
		}
	}
	if severityLabel != "" && severityLabel != severityLabelToRemove {
		if err := gc.AddLabel(e.org, e.repo, e.number, severityLabel); err != nil {
			log.WithError(err).Error("Failed to add severity bug label.")
		}
	}

	if hasValidLabel && !needsValidLabel {
		humanLabelled, err := gc.WasLabelAddedByHuman(e.org, e.repo, e.number, labels.ValidBug)
		if err != nil {
			// Return rather than potentially doing the wrong thing. The user can re-trigger us.
			return fmt.Errorf("failed to check if %s label was added by a human: %w", labels.ValidBug, err)
		}
		if humanLabelled {
			// This will make us remove the invalid label if it exists but saves us another check if it was
			// added by a human. It is reasonable to assume that it should be absent if the valid label was
			// manually added.
			needsInvalidLabel = false
			needsValidLabel = true
			response += fmt.Sprintf("\n\nRetaining the %s label as it was manually added.", labels.ValidBug)
		}
	}

	if needsValidLabel && !hasValidLabel {
		if err := gc.AddLabel(e.org, e.repo, e.number, labels.ValidBug); err != nil {
			log.WithError(err).Error("Failed to add valid bug label.")
		}
	} else if !needsValidLabel && hasValidLabel {
		if err := gc.RemoveLabel(e.org, e.repo, e.number, labels.ValidBug); err != nil {
			log.WithError(err).Error("Failed to remove valid bug label.")
		}
	}

	if needsInvalidLabel && !hasInvalidLabel {
		if err := gc.AddLabel(e.org, e.repo, e.number, labels.InvalidBug); err != nil {
			log.WithError(err).Error("Failed to add invalid bug label.")
		}
	} else if !needsInvalidLabel && hasInvalidLabel {
		if err := gc.RemoveLabel(e.org, e.repo, e.number, labels.InvalidBug); err != nil {
			log.WithError(err).Error("Failed to remove invalid bug label.")
		}
	}

	return comment(response)
}