Skip to content

Commit

Permalink
feat(gateway): add pull_request:labeled and pull_request:unlabeled
Browse files Browse the repository at this point in the history
This adds two new events to the github gateway.
  • Loading branch information
technosophos committed Dec 6, 2017
1 parent ee14452 commit 029492c
Show file tree
Hide file tree
Showing 4 changed files with 554 additions and 29 deletions.
75 changes: 66 additions & 9 deletions docs/topics/github.md
Expand Up @@ -9,15 +9,10 @@ following events:
- `pull_request`: Fired whenever a pull request's state is changed. See the `action`
value in the payload, which will be one of the following:
- opened
- closed
- assigned
- unassigned
- review_requested
- review_request_removed
- labeled
- unlabled
- edited
- reopened
- synchronize
- `pull_request:labeled`: Fired whenever a label is added to a pull request.
- `pull_request:unlabeled`: Fired whenever a label is removed from a pull request.
- `create`: Fired when a tag, branch, or repo is created.
- `release`: Fired when a new release is created.
- `status`: Fired when a status change happens on a commit.
Expand All @@ -26,7 +21,43 @@ following events:
You must be running `brigade-gateway` in a way that makes
it available to GitHub. (For example, assign it a publicly routable IP and domain name.)

## Configuring
## Configuring Your Project

Brigade uses projects to tie repositories to builds. You may want to check out
the [project documentation](projects.md) if you haven't already. This section
explains what parts of your project are required for GitHub integration to function.

In your project's `values.yaml` file, you should make sure the following are set for
GitHub integration to work:

```yaml
# Set to your github user/project or org/project
project: "technosophos/coffeesnob"

# The full name of the repository
repository: "github.com/technosophos/coffeesnob"

# The URL to use to clone. You can use any supported GitHub URL scheme
cloneURL: "https://github.com/technosophos/coffeesnob.git"

# A long shared secret. In the "Configuring GitHub" section, this is simply called
# Secret.
# You get to make this up. It should be a long string of arbitrary alphanumeric
# characters.
sharedSecret: 09879879879879879879879871

# Your GitHub OAuth2 token.
github:
token: "1111111111111111111112222222222222111111"
```

You can learn more about generating OAuth2 tokens from the
[official GitHub documentation](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/).

Make sure you run `helm upgrade` if you changed any parameters above (or `helm install`
if you are creating a new project).

## Configuring GitHub

To add a Brigade project to GitHub:

Expand Down Expand Up @@ -75,6 +106,32 @@ service.
In this case, once the NGINX proxy is set up with SSL, you can point your
GitHub "Payload URL" to the proxy instead of directly at the `brigade-gw` service.

## Configuring the GitHub Gateway

By default, when you install Brigade, the GitHub gateway is configured to ignore
pull requests that come from forks. More specifically:

If the `pull_request` action is set to `opened`, `reopened`, or `synchronize`, it
is ignored by default.

However, `pull_request:labeled` and `pull_request:unlabeled` are _not_ ignored.

The reasoning behind this is that a public repo can be forked by an untrusted third
party, so we don't want to allow that third party to run Brigade events on our
cluster. However, labels can only be added and removed to PRs by people with write
permissions to the repo. So those actions can be trusted.

If you know what you are doing, and you want to allow any pull requests to build,
even if coming from a forked repository, you may set the following configuration
in your _brigade_ `values.yaml` (NOT the project YAML).

```console
$ helm upgrade brigade brigade/brigade --set gw.buildForkedPullRequests=true --reuse-values
```

Note that this will introduce security risks if you allow public forks of your
repository.

## Connecting to Private GitHub Repositories (or Using SSH)

Sometimes it is better to configure Brigade to interact with GitHub via SSH. For example, if
Expand Down
44 changes: 30 additions & 14 deletions pkg/webhook/github.go
Expand Up @@ -84,15 +84,19 @@ func (s *githubHook) handleEvent(c *gin.Context, eventType string) {
repo = e.Repo.GetFullName()
commit = e.HeadCommit.GetID()
case *github.PullRequestEvent:
if isIgnoredPullRequestAction(e) {
c.JSON(http.StatusOK, gin.H{"status": "Action skipped"})
if !s.isAllowedPullRequest(e) {
c.JSON(http.StatusOK, gin.H{"status": "build skipped"})
return
}
if !s.buildForkedPullRequests && e.PullRequest.Head.Repo.GetFork() {
log.Println("skipping forked pull request")
c.JSON(http.StatusOK, gin.H{"status": "Skipped forked pull request"})
return

// EXPERIMENTAL: Since labeling and unlabeling PRs doesn't really have a
// code impact, we don't really want to fire off the same event (or require
// the user to know the event details). So we add a pseudo-event for labeling
// actions.
if a := e.GetAction(); a == "labeled" || a == "unlabeled" {
eventType = "pull_request:" + a
}

repo = e.Repo.GetFullName()
commit = e.PullRequest.Head.GetSHA()
case *github.CommitCommentEvent:
Expand Down Expand Up @@ -166,6 +170,26 @@ func (s *githubHook) buildStatus(eventType, commit string, payload []byte, proj
}
}

// isAllowedPullRequest returns true if this particular pull request is allowed
// to produce an event.
func (s *githubHook) isAllowedPullRequest(e *github.PullRequestEvent) bool {
switch e.GetAction() {
case "opened", "synchronize", "reopened":
if !s.buildForkedPullRequests && e.PullRequest.Head.Repo.GetFork() {
// Log this case for debugging.
log.Println("skipping forked pull request")
return false
}
return true
case "labeled", "unlabeled":
// We let these through because they have project write/admin perms as
// a precondition.
return true
}
log.Println("unsupported pull_request action:", e.GetAction())
return false
}

func truncAt(str string, max int) string {
if len(str) > max {
short := str[0 : max-3]
Expand All @@ -174,14 +198,6 @@ func truncAt(str string, max int) string {
return str
}

func isIgnoredPullRequestAction(event *github.PullRequestEvent) bool {
switch event.GetAction() {
case "opened", "synchronize", "reopened":
return false
}
return true
}

func getFileFromGithub(commit, path string, proj *brigade.Project) ([]byte, error) {
return GetFileContents(proj, commit, path)
}
Expand Down
24 changes: 18 additions & 6 deletions pkg/webhook/github_test.go
Expand Up @@ -54,10 +54,11 @@ func newTestGithubHandler(store storage.Store, t *testing.T) *githubHook {
func TestGithubHandler(t *testing.T) {

tests := []struct {
event string
commit string
payloadFile string
checkStatus bool
event string
commit string
payloadFile string
checkStatus bool
renamedEvent string
}{
{
event: "push",
Expand All @@ -77,6 +78,13 @@ func TestGithubHandler(t *testing.T) {
payloadFile: "testdata/github-pull_request_review-payload.json",
checkStatus: false,
},
{
event: "pull_request",
commit: "ad0703ac08e80448764b34dc089d0f73a1242ae9",
payloadFile: "testdata/github-pull_request-labeled-payload.json",
checkStatus: true,
renamedEvent: "pull_request:labeled",
},
{
event: "status",
commit: "9049f1265b7d61be4a8904a9a27120d2064dab3b",
Expand Down Expand Up @@ -145,8 +153,12 @@ func TestGithubHandler(t *testing.T) {
if len(store.builds) != 1 {
t.Fatal("expected a build created")
}
if store.builds[0].Type != tt.event {
t.Error("Build.Type is not correct")
if ee := store.builds[0].Type; tt.renamedEvent != "" {
if ee != tt.renamedEvent {
t.Errorf("Build.Type is not correct. Expected renamed event %q, got %q", tt.renamedEvent, ee)
}
} else if ee != tt.event {
t.Errorf("Build.Type is not correct. Expected event %q, got %q", tt.event, ee)
}
if store.builds[0].Provider != "github" {
t.Error("Build.Provider is not correct")
Expand Down

0 comments on commit 029492c

Please sign in to comment.