Skip to content

Commit

Permalink
Add support for Github Checks (#110)
Browse files Browse the repository at this point in the history
Currently only Github status checks are supported. This change adds
support for the newer Github Checks API.

I replaced the CheckSuite handler with a CheckRun one. CheckSuiteEvents
are only sent once per ref, whereas a CheckRun can change after a ref is
created.

This change requires updated permissions to allow us to lookup Checks
for specific refs and to listen to CheckRunEvents.
  • Loading branch information
chdsbd authored and bluekeyes committed Apr 24, 2019
1 parent 08efe68 commit ce2222a
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ Bulldozer requires the following permissions as a GitHub app:
| Permission | Access | Reason |
| ---------- | ------ | ------ |
| Repository administration | Read-only | Determine required status checks |
| Checks | Read-only | Read checks for ref |
| Repository contents | Read & write | Read configuration, perform merges |
| Issues | Read & write | Read comments, close linked issues |
| Repository metadata | Read-only | Basic repository data |
Expand All @@ -282,6 +283,7 @@ Bulldozer requires the following permissions as a GitHub app:

It should be subscribed to the following events:

* Check run
* Commit comment
* Pull request
* Status
Expand Down
19 changes: 19 additions & 0 deletions pull/github_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,25 @@ func (ghc *GithubContext) CurrentSuccessStatuses(ctx context.Context) ([]string,
opts.Page = res.NextPage
}

checkOpts := &github.ListCheckRunsOptions{ListOptions: github.ListOptions{PerPage: 100}}
for {
checkRuns, res, err := ghc.client.Checks.ListCheckRunsForRef(ctx, ghc.owner, ghc.repo, ghc.pr.GetHead().GetSHA(), checkOpts)
if err != nil {
return ghc.successStatuses, errors.Wrapf(err, "cannot get check runs for SHA %s on %s", ghc.pr.GetHead().GetSHA(), ghc.Locator())
}

for _, s := range checkRuns.CheckRuns {
if s.GetConclusion() == "success" {
successStatuses = append(successStatuses, s.GetName())
}
}

if res.NextPage == 0 {
break
}
checkOpts.Page = res.NextPage
}

ghc.successStatuses = successStatuses
}

Expand Down
85 changes: 85 additions & 0 deletions server/handler/check_run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2018 Palantir Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package handler

import (
"context"
"encoding/json"

"github.com/google/go-github/github"
"github.com/palantir/go-githubapp/githubapp"
"github.com/pkg/errors"

"github.com/palantir/bulldozer/pull"
)

type CheckRun struct {
Base
}

func (h *CheckRun) Handles() []string {
return []string{"check_run"}
}

func (h *CheckRun) Handle(ctx context.Context, eventType, deliveryID string, payload []byte) error {
var event github.CheckRunEvent

if err := json.Unmarshal(payload, &event); err != nil {
return errors.Wrap(err, "failed to parse check_run event payload")
}

repo := event.GetRepo()
installationID := githubapp.GetInstallationIDFromEvent(&event)

ctx, logger := githubapp.PrepareRepoContext(ctx, installationID, repo)

if event.GetAction() != "completed" {
logger.Debug().Msgf("Doing nothing since check_run action was %q instead of 'completed'", event.GetAction())
return nil
}

client, err := h.ClientCreator.NewInstallationClient(installationID)
if err != nil {
return errors.Wrap(err, "failed to instantiate github client")
}

prs := event.GetCheckRun().PullRequests
if len(prs) == 0 {
logger.Debug().Msg("Doing nothing since status change event affects no open pull requests")
return nil
}

for _, pr := range prs {
// The PR included in the CheckRun response is very slim on information.
// It does not contain the owner information or label information we
// need to process the pull request.

fullPR, _, err := client.PullRequests.Get(ctx, repo.GetOwner().GetLogin(), repo.GetName(), pr.GetNumber())
if err != nil {
return errors.Wrapf(err, "failed to fetch PR number %q for CheckRun", pr.GetNumber())
}
pullCtx := pull.NewGithubContext(client, fullPR)

logger := logger.With().Int(githubapp.LogKeyPRNum, pr.GetNumber()).Logger()
if err := h.ProcessPullRequest(logger.WithContext(ctx), pullCtx, client, pr); err != nil {
logger.Error().Err(errors.WithStack(err)).Msg("Error processing pull request")
}
}

return nil
}

// type assertion
var _ githubapp.EventHandler = &CheckRun{}
1 change: 1 addition & 0 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func New(c *Config) (*Server, error) {
}

webhookHandler := githubapp.NewDefaultEventDispatcher(c.Github,
&handler.CheckRun{Base: baseHandler},
&handler.IssueComment{Base: baseHandler},
&handler.PullRequest{Base: baseHandler},
&handler.PullRequestReview{Base: baseHandler},
Expand Down

0 comments on commit ce2222a

Please sign in to comment.