Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
TestLabel:
title: ".*"
TestLabel:
title: ".*"
TestFileMatch:
files:
- "cmd/.*.go"
- "pkg/.*.go"

15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ This condition is satisfied when the PR title matches on the given regex.
WIP:
title: "^WIP:.*"

### Regex on branch

This condition is satisfied when the PR branch matches on the given regex.

Feature:
branch: "^feature/.*"

### Regex on PR files

This condition is satisfied when any of the PR files matches on the given regexs.

Tests:
files:
- "cmd/.*_tests.go"

### Mergeable status

This condition is satisfied when the PR is in a [mergeable state](https://developer.github.com/v3/pulls/#response-1).
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ require (
github.com/kr/pretty v0.1.0 // indirect
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
Expand All @@ -13,6 +15,11 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d h1:xQcF7b7cZLWZG/+7A4G7un1qmEDYHIvId9qxRS1mZMs=
github.com/waigani/diffparser v0.0.0-20190828052634-7391f219313d/go.mod h1:BzSc3WEF8R+lCaP5iGFRxd5kIXy4JKOZAwNe1w0cdc0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
Expand Down
87 changes: 86 additions & 1 deletion pkg/labeler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,24 @@ package labeler

import (
"fmt"
"io/ioutil"
"log"
"math"
"net/http"
"os"
"regexp"
"strconv"
"strings"

gh "github.com/google/go-github/v27/github"
"github.com/waigani/diffparser"
)

type LabelerConfig map[string]LabelMatcher
type LabelMatcher struct {
Title string
Branch string
Files []string
Mergeable string
SizeBelow string `yaml:"size-below"`
SizeAbove string `yaml:"size-above"`
Expand Down Expand Up @@ -62,12 +68,47 @@ func NewBranchCondition() Condition {
}
prBranchName := pr.Head.GetRef()
log.Printf("Matching `%s` against: `%s`", matcher.Branch, prBranchName)
isMatched, _ := regexp.Match(matcher.Title, []byte(prBranchName))
isMatched, _ := regexp.Match(matcher.Branch, []byte(prBranchName))
return isMatched, nil
},
}
}

func NewFilesCondition() Condition {
prFiles := []string{}

return Condition{
GetName: func() string {
return "File matches regex"
},
Evaluate: func(pr *gh.PullRequest, matcher LabelMatcher) (bool, error) {
if len(matcher.Files) <= 0 {
return false, fmt.Errorf("Files are not set in config")
}

if len(prFiles) == 0 {
var err error
prFiles, err = getPrFileNames(pr)
if err != nil {
return false, err
}
}

log.Printf("Matching `%s` against: %s", strings.Join(matcher.Files, ", "), strings.Join(prFiles, ", "))
for _, fileMatcher := range matcher.Files {
for _, prFile := range prFiles {
isMatched, _ := regexp.Match(fileMatcher, []byte(prFile))
if isMatched {
log.Printf("Matched `%s` against: `%s`", prFile, fileMatcher)
return isMatched, nil
}
}
}
return false, nil
},
}
}

func NewIsMergeableCondition() Condition {
return Condition{
GetName: func() string {
Expand Down Expand Up @@ -184,6 +225,7 @@ func (l *Labeler) findMatches(pr *gh.PullRequest, config *LabelerConfig) (LabelU
NewBranchCondition(),
NewIsMergeableCondition(),
NewSizeCondition(),
NewFilesCondition(),
}

for label, matcher := range *config {
Expand All @@ -204,3 +246,46 @@ func (l *Labeler) findMatches(pr *gh.PullRequest, config *LabelerConfig) (LabelU

return labelUpdates, nil
}

// getPrFileNames returns all of the file names (old and new) of files changed in the given PR
func getPrFileNames(pr *gh.PullRequest) ([]string, error) {
ghToken := os.Getenv("GITHUB_TOKEN")
diffReq, err := http.NewRequest("GET", pr.GetDiffURL(), nil)

if err != nil {
return nil, err
}

diffReq.Header.Add("Authorization", "Bearer "+ghToken)
diffRes, err := http.DefaultClient.Do(diffReq)

if err != nil {
return nil, err
}

defer diffRes.Body.Close()

var diffRaw []byte
prFiles := make([]string, 0)
if diffRes.StatusCode == http.StatusOK {
diffRaw, err = ioutil.ReadAll(diffRes.Body)

if err != nil {
return nil, err
}

diff, _ := diffparser.Parse(string(diffRaw))
prFilesSet := map[string]struct{}{}
// Place in a set to remove duplicates
for _, file := range diff.Files {
prFilesSet[file.OrigName] = struct{}{}
prFilesSet[file.NewName] = struct{}{}
}
// Convert to list to make it easier to consume
for k := range prFilesSet {
prFiles = append(prFiles, k)
}
}

return prFiles, nil
}
26 changes: 25 additions & 1 deletion pkg/labeler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func TestHandleEvent(t *testing.T) {
},
TestCase{
payloads: []string{"small_pr"},
name: "Test the branch rule",
name: "Test the branch rule (matching)",
config: LabelerConfig{
"Branch": LabelMatcher{
Branch: "^srvaroa-patch.*",
Expand All @@ -161,6 +161,30 @@ func TestHandleEvent(t *testing.T) {
initialLabels: []string{},
expectedLabels: []string{"Branch"},
},
TestCase{
payloads: []string{"small_pr"},
name: "Test the branch rule (not matching)",
config: LabelerConfig{
"Branch": LabelMatcher{
Branch: "^does/not-match/*",
},
},
initialLabels: []string{},
expectedLabels: []string{},
},
TestCase{
payloads: []string{"diff_pr"},
name: "Test the branch rule",
config: LabelerConfig{
"Files": LabelMatcher{
Files: []string{
"^pkg/.*_test.go",
},
},
},
initialLabels: []string{},
expectedLabels: []string{"Files"},
},
}

for _, tc := range testCases {
Expand Down
Loading