diff --git a/Dockerfile b/Dockerfile index 8458882..6f17a26 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,11 +5,11 @@ WORKDIR /go/src/github.com/alexellis/derek COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o derek . -FROM alpine:3.5 +FROM alpine:3.6 RUN apk --no-cache add curl ca-certificates \ && echo "Pulling watchdog binary from Github." \ - && curl -sSL https://github.com/alexellis/faas/releases/download/0.6.9/fwatchdog > /usr/bin/fwatchdog \ + && curl -sSL https://github.com/alexellis/faas/releases/download/0.6.11/fwatchdog > /usr/bin/fwatchdog \ && chmod +x /usr/bin/fwatchdog \ && apk del curl --no-cache diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..129585d --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +all: test build +build: + go build -o derek + +test: + go test -cover diff --git a/commentHandler.go b/commentHandler.go index cd903a9..bf05fd5 100644 --- a/commentHandler.go +++ b/commentHandler.go @@ -7,9 +7,9 @@ import ( "strings" log "github.com/Sirupsen/logrus" - "github.com/google/go-github/github" "github.com/alexellis/derek/auth" "github.com/alexellis/derek/types" + "github.com/google/go-github/github" ) const open = "open" @@ -20,7 +20,11 @@ func makeClient(installation int) (*github.Client, context.Context) { token := os.Getenv("access_token") if len(token) == 0 { - newToken, tokenErr := auth.MakeAccessTokenForInstallation(os.Getenv("application"), installation, os.Getenv("private_key")) + + applicationID := os.Getenv("application") + privateKeyPath := os.Getenv("private_key") + + newToken, tokenErr := auth.MakeAccessTokenForInstallation(applicationID, installation, privateKeyPath) if tokenErr != nil { log.Fatalln(tokenErr.Error()) } @@ -98,6 +102,7 @@ func handleComment(req types.IssueCommentOuter) { if assignee == "me" { assignee = req.Comment.User.Login } + _, _, err := client.Issues.AddAssignees(ctx, req.Repository.Owner.Login, req.Repository.Name, req.Issue.Number, []string{assignee}) if err != nil { log.Fatalln(err) @@ -121,6 +126,7 @@ func handleComment(req types.IssueCommentOuter) { fmt.Printf("%s unassigned successfully or already unassigned.\n", command.Value) break + case "close", "reopen": fmt.Printf("%s wants to %s issue #%d\n", req.Comment.User.Login, command.Type, req.Issue.Number) @@ -176,7 +182,6 @@ func parse(body string) *types.CommentAction { } func isValidCommand(body string, trigger string) bool { - - return (len(body) > len(trigger) && body[0:len(trigger)] == trigger) || (body == trigger && !strings.HasSuffix(trigger, ": ")) - + return (len(body) > len(trigger) && body[0:len(trigger)] == trigger) || + (body == trigger && !strings.HasSuffix(trigger, ": ")) } diff --git a/commentHandler_test.go b/commentHandler_test.go index 1cb237b..c6b7400 100644 --- a/commentHandler_test.go +++ b/commentHandler_test.go @@ -51,34 +51,34 @@ func Test_Parsing_OpenClose(t *testing.T) { } } -var labelOptions = []struct { - title string - body string - expectedType string - expectedVal string -}{ - { //this case replaces Test_Parsing_AddLabel - title: "Add label of demo", - body: "Derek add label: demo", - expectedType: "AddLabel", - expectedVal: "demo", - }, - { - title: "Remove label of demo", - body: "Derek remove label: demo", - expectedType: "RemoveLabel", - expectedVal: "demo", - }, - { - title: "Invalid label action", - body: "Derek peel label: demo", - expectedType: "", - expectedVal: "", - }, -} - func Test_Parsing_Labels(t *testing.T) { + var labelOptions = []struct { + title string + body string + expectedType string + expectedVal string + }{ + { //this case replaces Test_Parsing_AddLabel + title: "Add label of demo", + body: "Derek add label: demo", + expectedType: "AddLabel", + expectedVal: "demo", + }, + { + title: "Remove label of demo", + body: "Derek remove label: demo", + expectedType: "RemoveLabel", + expectedVal: "demo", + }, + { + title: "Invalid label action", + body: "Derek peel label: demo", + expectedType: "", + expectedVal: "", + }, + } + for _, test := range labelOptions { t.Run(test.title, func(t *testing.T) { @@ -90,52 +90,52 @@ func Test_Parsing_Labels(t *testing.T) { } } -var assignmentOptions = []struct { - title string - body string - expectedType string - expectedVal string -}{ - { - title: "Assign to burt", - body: "Derek assign: burt", - expectedType: "Assign", - expectedVal: "burt", - }, - { - title: "Unassign burt", - body: "Derek unassign: burt", - expectedType: "Unassign", - expectedVal: "burt", - }, - { - title: "Assign to me", - body: "Derek assign: me", - expectedType: "Assign", - expectedVal: "me", - }, - { - title: "Unassign me", - body: "Derek unassign: me", - expectedType: "Unassign", - expectedVal: "me", - }, - { - title: "Invalid assignment action", - body: "Derek consign: burt", - expectedType: "", - expectedVal: "", - }, - { - title: "Unassign blank", - body: "Derek unassign: ", - expectedType: "", - expectedVal: "", - }, -} - func Test_Parsing_Assignments(t *testing.T) { + var assignmentOptions = []struct { + title string + body string + expectedType string + expectedVal string + }{ + { + title: "Assign to burt", + body: "Derek assign: burt", + expectedType: "Assign", + expectedVal: "burt", + }, + { + title: "Unassign burt", + body: "Derek unassign: burt", + expectedType: "Unassign", + expectedVal: "burt", + }, + { + title: "Assign to me", + body: "Derek assign: me", + expectedType: "Assign", + expectedVal: "me", + }, + { + title: "Unassign me", + body: "Derek unassign: me", + expectedType: "Unassign", + expectedVal: "me", + }, + { + title: "Invalid assignment action", + body: "Derek consign: burt", + expectedType: "", + expectedVal: "", + }, + { + title: "Unassign blank", + body: "Derek unassign: ", + expectedType: "", + expectedVal: "", + }, + } + for _, test := range assignmentOptions { t.Run(test.title, func(t *testing.T) { diff --git a/main.go b/main.go index ed5e87d..fff3a24 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io/ioutil" "os" @@ -40,23 +41,30 @@ func main() { // HMAC Validated or not turned on. eventType := os.Getenv("Http_X_Github_Event") + if err := handleEvent(eventType, bytesIn); err != nil { + log.Fatal(err) + } +} + +func handleEvent(eventType string, bytesIn []byte) error { + switch eventType { case "pull_request": req := types.PullRequestOuter{} if err := json.Unmarshal(bytesIn, &req); err != nil { - log.Fatalf("Cannot parse input %s", err.Error()) + return fmt.Errorf("Cannot parse input %s", err.Error()) } customer, err := auth.IsCustomer(req.Repository) if err != nil { - log.Fatalf("Unable to verify customer: %s/%s", req.Repository.Owner.Login, req.Repository.Name) - } else if !customer { - log.Fatalf("No customer found for: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + return fmt.Errorf("Unable to verify customer: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + } else if customer == false { + return fmt.Errorf("No customer found for: %s/%s", req.Repository.Owner.Login, req.Repository.Name) } derekConfig, err := getConfig(req.Repository.Owner.Login, req.Repository.Name) if err != nil { - log.Fatalf("Unable to access maintainers file at: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + return fmt.Errorf("Unable to access maintainers file at: %s/%s", req.Repository.Owner.Login, req.Repository.Name) } if enabledFeature(dcoCheck, derekConfig) { @@ -67,19 +75,19 @@ func main() { case "issue_comment": req := types.IssueCommentOuter{} if err := json.Unmarshal(bytesIn, &req); err != nil { - log.Fatalf("Cannot parse input %s", err.Error()) + return fmt.Errorf("Cannot parse input %s", err.Error()) } customer, err := auth.IsCustomer(req.Repository) if err != nil { - log.Fatalf("Unable to verify customer: %s/%s", req.Repository.Owner.Login, req.Repository.Name) - } else if !customer { - log.Fatalf("No customer found for: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + return fmt.Errorf("Unable to verify customer: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + } else if customer == false { + return fmt.Errorf("No customer found for: %s/%s", req.Repository.Owner.Login, req.Repository.Name) } derekConfig, err := getConfig(req.Repository.Owner.Login, req.Repository.Name) if err != nil { - log.Fatalf("Unable to access maintainers file at: %s/%s", req.Repository.Owner.Login, req.Repository.Name) + return fmt.Errorf("Unable to access maintainers file at: %s/%s", req.Repository.Owner.Login, req.Repository.Name) } if permittedUserFeature(comments, derekConfig, req.Comment.User.Login) { @@ -87,6 +95,8 @@ func main() { } break default: - log.Fatalln("X_Github_Event want: ['pull_request', 'issue_comment'], got: " + eventType) + return fmt.Errorf("X_Github_Event want: ['pull_request', 'issue_comment'], got: " + eventType) } + + return nil } diff --git a/permissionsHandler.go b/permissionsHandler.go index bd966ae..5079514 100644 --- a/permissionsHandler.go +++ b/permissionsHandler.go @@ -13,7 +13,7 @@ import ( "github.com/alexellis/derek/types" ) -const defaultMaintFile = ".DEREK.yml" +const configFile = ".DEREK.yml" func enabledFeature(attemptedFeature string, config *types.DerekConfig) bool { @@ -46,7 +46,7 @@ func permittedUserFeature(attemptedFeature string, config *types.DerekConfig, us func getConfig(owner string, repository string) (*types.DerekConfig, error) { - maintainersFile := fmt.Sprintf("https://github.com/%s/%s/raw/master/%s", owner, repository, defaultMaintFile) + maintainersFile := fmt.Sprintf("https://github.com/%s/%s/raw/master/%s", owner, repository, configFile) client := http.Client{ Timeout: 30 * time.Second, diff --git a/permissionsHandler_test.go b/permissionsHandler_test.go index 62917e8..10a447e 100644 --- a/permissionsHandler_test.go +++ b/permissionsHandler_test.go @@ -1,106 +1,62 @@ package main import ( - "os" "testing" "github.com/alexellis/derek/types" ) -var envVarOpts = []struct { - title string - envName string - envConfigVal string - envExpectedVal string -}{ - { - title: "envvar correctly set", - envName: "maintainers_file", - envConfigVal: "DEREK", - envExpectedVal: "DEREK", - }, - { - title: "Misspelt envVar Name", - envName: "maintainers_fill", - envConfigVal: "DEREK", - envExpectedVal: "MAINTAINERS", - }, - { - title: "envVar doesnt exist", - envName: "", - envConfigVal: "", - envExpectedVal: "MAINTAINERS", - }, -} - -func Test_getEnv(t *testing.T) { - - for _, test := range envVarOpts { - t.Run(test.title, func(t *testing.T) { - - os.Setenv(test.envName, test.envConfigVal) - - envvar := getEnv("maintainers_file", "MAINTAINERS") +func Test_enabledFeature(t *testing.T) { - if envvar != test.envExpectedVal { - t.Errorf("Maintainers File - wanted: %s, found %s", test.envExpectedVal, envvar) - } - os.Unsetenv(test.envName) - }) + var enableFeatureOpts = []struct { + title string + attemptedFeature string + configFeatures []string + expectedVal bool + }{ + { + title: "dco enabled try dco case sensitive", + attemptedFeature: "dco_check", + configFeatures: []string{"dco_check"}, + expectedVal: true, + }, + { + title: "dco enabled try dco case insensitive", + attemptedFeature: "DCO_check", + configFeatures: []string{"dco_check"}, + expectedVal: true, + }, + { + title: "dco enabled try comments", + attemptedFeature: "comments", + configFeatures: []string{"dco_check"}, + expectedVal: false, + }, + { + title: "Comments enabled try comments case insensitive", + attemptedFeature: "Comments", + configFeatures: []string{"comments"}, + expectedVal: true, + }, + { + title: "Comments enabled try comments case sensitive", + attemptedFeature: "comments", + configFeatures: []string{"comments"}, + expectedVal: true, + }, + { + title: "Comments enabled try dco", + attemptedFeature: "dco", + configFeatures: []string{"comments"}, + expectedVal: false, + }, + { + title: "Non-existent feature", + attemptedFeature: "gibberish", + configFeatures: []string{"dco_check", "comments"}, + expectedVal: false, + }, } -} - -var enableFeatureOpts = []struct { - title string - attemptedFeature string - configFeatures []string - expectedVal bool -}{ - { - title: "dco enabled try dco case sensitive", - attemptedFeature: "dco_check", - configFeatures: []string{"dco_check"}, - expectedVal: true, - }, - { - title: "dco enabled try dco case insensitive", - attemptedFeature: "DCO_check", - configFeatures: []string{"dco_check"}, - expectedVal: true, - }, - { - title: "dco enabled try comments", - attemptedFeature: "comments", - configFeatures: []string{"dco_check"}, - expectedVal: false, - }, - { - title: "Comments enabled try comments case insensitive", - attemptedFeature: "Comments", - configFeatures: []string{"comments"}, - expectedVal: true, - }, - { - title: "Comments enabled try comments case sensitive", - attemptedFeature: "comments", - configFeatures: []string{"comments"}, - expectedVal: true, - }, - { - title: "Comments enabled try dco", - attemptedFeature: "dco", - configFeatures: []string{"comments"}, - expectedVal: false, - }, - { - title: "Non-existent feature", - attemptedFeature: "gibberish", - configFeatures: []string{"dco_check", "comments"}, - expectedVal: false, - }, -} - -func Test_enabledFeature(t *testing.T) { for _, test := range enableFeatureOpts { t.Run(test.title, func(t *testing.T) { @@ -115,76 +71,77 @@ func Test_enabledFeature(t *testing.T) { } } -var permittedUserFeatureOpts = []struct { - title string - attemptedFeature string - user string - config types.DerekConfig - expectedVal bool -}{ - { - title: "Valid feature with valid maintainer", - attemptedFeature: "comment", - user: "Burt", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, +func Test_permittedUserFeature(t *testing.T) { + + var permittedUserFeatureOpts = []struct { + title string + attemptedFeature string + user string + config types.DerekConfig + expectedVal bool + }{ + { + title: "Valid feature with valid maintainer", + attemptedFeature: "comment", + user: "Burt", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: true, }, - expectedVal: true, - }, - { - title: "Valid feature with valid maintainer case insensitive", - attemptedFeature: "comment", - user: "burt", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + { + title: "Valid feature with valid maintainer case insensitive", + attemptedFeature: "comment", + user: "burt", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: true, }, - expectedVal: true, - }, - { - title: "Valid feature with invalid maintainer", - attemptedFeature: "comment", - user: "ernie", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + { + title: "Valid feature with invalid maintainer", + attemptedFeature: "comment", + user: "ernie", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: false, }, - expectedVal: false, - }, - { - title: "Valid feature with invalid maintainer case insensitive", - attemptedFeature: "Comment", - user: "ernie", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + { + title: "Valid feature with invalid maintainer case insensitive", + attemptedFeature: "Comment", + user: "ernie", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: false, }, - expectedVal: false, - }, - { - title: "Invalid feature with valid maintainer", - attemptedFeature: "invalid", - user: "Burt", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + { + title: "Invalid feature with valid maintainer", + attemptedFeature: "invalid", + user: "Burt", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: false, }, - expectedVal: false, - }, - { - title: "Invalid feature with valid maintainer case insensitive", - attemptedFeature: "invalid", - user: "burt", - config: types.DerekConfig{ - Features: []string{"comment"}, - Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + { + title: "Invalid feature with valid maintainer case insensitive", + attemptedFeature: "invalid", + user: "burt", + config: types.DerekConfig{ + Features: []string{"comment"}, + Maintainers: []string{"Burt", "Tarquin", "Blanche"}, + }, + expectedVal: false, }, - expectedVal: false, - }, -} + } -func Test_permittedUserFeature(t *testing.T) { for _, test := range permittedUserFeatureOpts { t.Run(test.title, func(t *testing.T) {