The OpenShift Test Platform team at Red Hat maintains and operates the CI system for OpenShift. This includes maintaining and enhancing: the kubernetes/test-infra, all of the openshift/ci-tools that go into running a CI system, documentation for the system in openshift/ci-docs, and certain aspects of the CI configuration in openshift/release.
This necessitates that the team review and approve PRs in the preceding repos from contributors both inside and outside of the team itself. The team wants to provide a quick and easy experience for contributors and strives to provide PR reviews in a timely manner. This lofty goal makes relying on GitHub's chatty notification system difficult and distracting.
I thought a lot about how the team could be responsive to our contributors while not distracting ourselves from our other important duties. What if each member of the team received a slack message each day detailing all of the PRs where their input has been requested? Then they could simply review it at a time that made sense for them, and act on each PR. This would ensure that no contributor would ever have to wait for more than a day for their PR to be seen.
A YAML configuration file could be defined for this tool to: enumerate our team members, team name, and repos that we want to check:
teamMembers: - sgoeddel '' teamName: test-platform repos: - openshift/ci-tools - openshift/ci-docs - openshift/release - kubernetes/test-infra
GitHub provides a REST API that has an endpoint to list PRs for a repo. Utilizing that, we can obtain every open PR for the repos that we maintain:
prs, err := ghClient.GetPullRequests(org, repo) if err != nil { logrus.Errorf("failed to get pull requests: %v", err) }
Then check to see which, if any, of our team members have been requested to review the PR:
for _, pr := range prs { for i, u := range users { if u.requestedToReview(pr) { u.PrRequests = append(u.PrRequests, prRequest{ Repo: orgRepo, Number: pr.Number, Url: pr.HTMLURL, Title: pr.Title, Author: pr.User.Login, Created: pr.CreatedAt, LastUpdated: pr.UpdatedAt, }) users[i] = u } } }
Determining if a given user has been requested to review a PR requires checking if the user themselves have been requested or if the configured team has been requested:
func (u *user) requestedToReview(pr github.PullRequest) bool { // only check PRs that the user is not the author of, as they could have requested their own team if u.GithubId != pr.User.Login { for _, team := range pr.RequestedTeams { if u.TeamName == team.Slug { return true } } for _, reviewer := range pr.RequestedReviewers { if u.GithubId == reviewer.Login { return true } } } return false }
In order for the resulting reminder message to be most useful we need a good ordering of the PRs:
// sort by most recent update first sort.Slice(user.PrRequests, func(i, j int) bool { return user.PrRequests[i].LastUpdated.After(user.PrRequests[j].LastUpdated) })
It is also helpful to have a simple colored emoji next to each PR so that they are easily filtered by date created:
const ( recent = ":large_green_circle:" normal = ":large_orange_circle:" old = ":red_circle:" twoDays = time.Hour * 24 * 2 oneWeek = time.Hour * 24 * 7 ) func (p prRequest) recency() string { now := time.Now() if p.Created.After(now.Add(-twoDays)) { return recent } else if p.Created.After(now.Add(-oneWeek)) { return normal } else { return old } }
Slack also provides an API that allows us to post a message. Using their constructs we can format a nice message to display:
for _, pr := range user.PrRequests { message = append(message, &slack.ContextBlock{ Type: slack.MBTContext, ContextElements: slack.ContextElements{ Elements: []slack.MixedElement{ &slack.TextBlockObject{ Type: slack.MarkdownType, Text: pr.link(), }, &slack.TextBlockObject{ Type: slack.MarkdownType, Text: pr.createdUpdatedMessage(), }, }, }, }) }
We decided that it was most useful if this tool was scheduled to run daily, and since our team is geographically dispersed we chose to run it at 8 am UTC on weekdays. Putting it all together, each member of our team gets a message like the following at the beginning of their work day.
The full source code can be found in ci-tools/cmd/pr-reminder.