diff --git a/README.md b/README.md index 0386d7e..c04070f 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,10 @@ redirect: https://github.com///.DEREK.yaml If this optional field is non-empty, Derek will read it's configuration from another location. This allows multiple projects to use the same configuration. Please note that redirection is only supported for GitHub repository URLs. +* Command triggers + +By default, Derek commands can be called with `Derek `. The prefix `Derek ` is the default trigger, but the bot also supports the `/` trigger which can be enabled by setting the `use_slash_trigger` environment variable to `true`. + ### Examples: * Update the title of a PR or issue diff --git a/commentHandler.go b/commentHandler.go index 6580989..ba2833c 100644 --- a/commentHandler.go +++ b/commentHandler.go @@ -31,8 +31,19 @@ const ( addLabelConstant string = "AddLabel" setMilestoneConstant string = "SetMilestone" removeMilestoneConstant string = "RemoveMilestone" + + commandTriggerDefault string = "Derek " + commandTriggerSlash string = "/" ) +func getCommandTrigger() string { + commandTrigger := commandTriggerDefault + if os.Getenv("use_slash_trigger") == "true" { + commandTrigger = commandTriggerSlash + } + return commandTrigger +} + func makeClient(installation int) (*github.Client, context.Context) { ctx := context.Background() @@ -65,7 +76,7 @@ func handleComment(req types.IssueCommentOuter) { var feedback string var err error - command := parse(req.Comment.Body) + command := parse(req.Comment.Body, getCommandTrigger()) switch command.Type { @@ -319,23 +330,23 @@ func updateMilestone(req types.IssueCommentOuter, cmdType string, cmdValue strin return buffer.String(), nil } -func parse(body string) *types.CommentAction { +func parse(body, commandTrigger string) *types.CommentAction { commentAction := types.CommentAction{} commands := map[string]string{ - "Derek add label: ": addLabelConstant, - "Derek remove label: ": removeLabelConstant, - "Derek assign: ": assignConstant, - "Derek unassign: ": unassignConstant, - "Derek close": closeConstant, - "Derek reopen": reopenConstant, - "Derek set title: ": setTitleConstant, - "Derek edit title: ": setTitleConstant, - "Derek lock": lockConstant, - "Derek unlock": unlockConstant, - "Derek set milestone: ": setMilestoneConstant, - "Derek remove milestone: ": removeMilestoneConstant, + commandTrigger + "add label: ": addLabelConstant, + commandTrigger + "remove label: ": removeLabelConstant, + commandTrigger + "assign: ": assignConstant, + commandTrigger + "unassign: ": unassignConstant, + commandTrigger + "close": closeConstant, + commandTrigger + "reopen": reopenConstant, + commandTrigger + "set title: ": setTitleConstant, + commandTrigger + "edit title: ": setTitleConstant, + commandTrigger + "lock": lockConstant, + commandTrigger + "unlock": unlockConstant, + commandTrigger + "set milestone: ": setMilestoneConstant, + commandTrigger + "remove milestone: ": removeMilestoneConstant, } for trigger, commandType := range commands { diff --git a/commentHandler_test.go b/commentHandler_test.go index f3e7435..3e79c25 100644 --- a/commentHandler_test.go +++ b/commentHandler_test.go @@ -4,54 +4,79 @@ package main import ( + "os" "testing" "github.com/alexellis/derek/types" ) -var actionOptions = []struct { - title string - body string - expectedAction string -}{ - { - title: "Correct reopen command", - body: "Derek reopen", - expectedAction: "reopen", - }, - { //this case replaces Test_Parsing_Close - title: "Correct close command", - body: "Derek close", - expectedAction: "close", - }, - { - title: "invalid command", - body: "Derek dance", - expectedAction: "", - }, - { - title: "Longer reopen command", - body: "Derek reopen: ", - expectedAction: "reopen", - }, - { - title: "Longer close command", - body: "Derek close: ", - expectedAction: "close", - }, +var commandTriggers = []string{commandTriggerDefault, commandTriggerSlash} + +func Test_getCommandTrigger(t *testing.T) { + const ( + envVar = "use_slash_trigger" + errorMessage = "expected trigger to be: %s, got: %s" + ) + var trigger string + + // test default trigger + os.Unsetenv(envVar) + trigger = getCommandTrigger() + if trigger != commandTriggerDefault { + t.Errorf(errorMessage, commandTriggerDefault, trigger) + } + + // test slash trigger + os.Setenv(envVar, "true") + trigger = getCommandTrigger() + if trigger != commandTriggerSlash { + t.Errorf(errorMessage, commandTriggerSlash, trigger) + } } func Test_Parsing_OpenClose(t *testing.T) { + var actionOptions = []struct { + title string + body string + expectedAction string + }{ + { + title: "Correct reopen command", + body: "reopen", + expectedAction: "reopen", + }, + { //this case replaces Test_Parsing_Close + title: "Correct close command", + body: "close", + expectedAction: "close", + }, + { + title: "invalid command", + body: "dance", + expectedAction: "", + }, + { + title: "Longer reopen command", + body: "reopen: ", + expectedAction: "reopen", + }, + { + title: "Longer close command", + body: "close: ", + expectedAction: "close", + }, + } + for _, test := range actionOptions { t.Run(test.title, func(t *testing.T) { - action := parse(test.body) - - if action.Type != test.expectedAction { - t.Errorf("Action - want: %s, got %s", test.expectedAction, action.Type) + for _, trigger := range commandTriggers { + action := parse(trigger+test.body, trigger) + if action.Type != test.expectedAction { + t.Errorf("Action - want: %s, got %s", test.expectedAction, action.Type) + } } - }) } } @@ -66,19 +91,19 @@ func Test_Parsing_Labels(t *testing.T) { }{ { //this case replaces Test_Parsing_AddLabel title: "Add label of demo", - body: "Derek add label: demo", + body: "add label: demo", expectedType: "AddLabel", expectedVal: "demo", }, { title: "Remove label of demo", - body: "Derek remove label: demo", + body: "remove label: demo", expectedType: "RemoveLabel", expectedVal: "demo", }, { title: "Invalid label action", - body: "Derek peel label: demo", + body: "peel label: demo", expectedType: "", expectedVal: "", }, @@ -87,9 +112,11 @@ func Test_Parsing_Labels(t *testing.T) { for _, test := range labelOptions { t.Run(test.title, func(t *testing.T) { - action := parse(test.body) - if action.Type != test.expectedType || action.Value != test.expectedVal { - t.Errorf("Action - wanted: %s, got %s\nLabel - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + for _, trigger := range commandTriggers { + action := parse(trigger+test.body, trigger) + if action.Type != test.expectedType || action.Value != test.expectedVal { + t.Errorf("Action - wanted: %s, got %s\nLabel - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + } } }) } @@ -105,37 +132,37 @@ func Test_Parsing_Assignments(t *testing.T) { }{ { title: "Assign to burt", - body: "Derek assign: burt", + body: "assign: burt", expectedType: assignConstant, expectedVal: "burt", }, { title: "Unassign burt", - body: "Derek unassign: burt", + body: "unassign: burt", expectedType: unassignConstant, expectedVal: "burt", }, { title: "Assign to me", - body: "Derek assign: me", + body: "assign: me", expectedType: assignConstant, expectedVal: "me", }, { title: "Unassign me", - body: "Derek unassign: me", + body: "unassign: me", expectedType: unassignConstant, expectedVal: "me", }, { title: "Invalid assignment action", - body: "Derek consign: burt", + body: "consign: burt", expectedType: "", expectedVal: "", }, { title: "Unassign blank", - body: "Derek unassign: ", + body: "unassign: ", expectedType: "", expectedVal: "", }, @@ -144,9 +171,11 @@ func Test_Parsing_Assignments(t *testing.T) { for _, test := range assignmentOptions { t.Run(test.title, func(t *testing.T) { - action := parse(test.body) - if action.Type != test.expectedType || action.Value != test.expectedVal { - t.Errorf("Action - wanted: %s, got %s\nMaintainer - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + for _, trigger := range commandTriggers { + action := parse(trigger+test.body, trigger) + if action.Type != test.expectedType || action.Value != test.expectedVal { + t.Errorf("Action - wanted: %s, got %s\nMaintainer - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + } } }) } @@ -162,25 +191,25 @@ func Test_Parsing_Titles(t *testing.T) { }{ { title: "Set Title", - body: "Derek set title: This is a really great Title!", + body: "set title: This is a really great Title!", expectedType: setTitleConstant, expectedVal: "This is a really great Title!", }, { title: "Mis-spelling of title", - body: "Derek set titel: This is a really great Title!", + body: "set titel: This is a really great Title!", expectedType: "", expectedVal: "", }, { title: "Empty Title", - body: "Derek set title: ", + body: "set title: ", expectedType: "", //blank because it should fail isValidCommand expectedVal: "", }, { title: "Empty Title (Double Space)", - body: "Derek set title: ", + body: "set title: ", expectedType: setTitleConstant, expectedVal: "", }, @@ -189,9 +218,11 @@ func Test_Parsing_Titles(t *testing.T) { for _, test := range titleOptions { t.Run(test.title, func(t *testing.T) { - action := parse(test.body) - if action.Type != test.expectedType || action.Value != test.expectedVal { - t.Errorf("\nAction - wanted: %s, got %s\nValue - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + for _, trigger := range commandTriggers { + action := parse(trigger+test.body, trigger) + if action.Type != test.expectedType || action.Value != test.expectedVal { + t.Errorf("\nAction - wanted: %s, got %s\nValue - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + } } }) } @@ -411,19 +442,19 @@ func Test_Parsing_Milestones(t *testing.T) { }{ { title: "Right set milestone", - body: "Derek set milestone: demo", + body: "set milestone: demo", expectedType: "SetMilestone", expectedVal: "demo", }, { title: "Right remove milestone", - body: "Derek remove milestone: demo", + body: "remove milestone: demo", expectedType: "RemoveMilestone", expectedVal: "demo", }, { title: "Wrong set milestone", - body: "Derek you ok label: demo", + body: "you ok label: demo", expectedType: "", expectedVal: "", }, @@ -431,9 +462,12 @@ func Test_Parsing_Milestones(t *testing.T) { for _, test := range milestonesOptions { t.Run(test.title, func(t *testing.T) { - action := parse(test.body) - if action.Type != test.expectedType || action.Value != test.expectedVal { - t.Errorf("Action - wanted: %s, got %s\nLabel - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + + for _, trigger := range commandTriggers { + action := parse(trigger+test.body, trigger) + if action.Type != test.expectedType || action.Value != test.expectedVal { + t.Errorf("Action - wanted: %s, got %s\nLabel - wanted: %s, got %s", test.expectedType, action.Type, test.expectedVal, action.Value) + } } }) }