Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Commit

Permalink
Merge pull request #47 from heiko-braun/boards
Browse files Browse the repository at this point in the history
  • Loading branch information
pure-bot[bot] authored Jul 4, 2018
2 parents dc68eab + 1c6863c commit 6752494
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 8 deletions.
29 changes: 25 additions & 4 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,9 @@
version = "1.4.1"

[[constraint]]
revision = "8ea2e2657df890db8fb434a9274799d641bd698c"
name = "github.com/google/go-github"
branch = "master"

[[constraint]]
revision = "master"
name = "github.com/go-resty/resty"
4 changes: 4 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ var RootCmd = &cobra.Command{
Long: `PuRe Bot - pull request bot.`,
}

func GetConfig() config.Config {
return botConfig
}

func Execute() {
if err := RootCmd.Execute(); err != nil {
if logger != nil {
Expand Down
24 changes: 24 additions & 0 deletions config-sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,27 @@ repos:
- "notif/triage"
pure-bot-sandbox:
disabled: true
board:
zenhub_token: "<zenhub token>"
github_repo: "<github repository id>"
columns:
- name: "backlog"
id: "5b33b25ab0388a502999f52b"
events:
- "issues_unassigned"
- "issues_opened"
- "issues_reopened"
- name: "progress"
id: "5b33b25ab0388a502999f52c"
events:
- "issues_assigned"
- "pull_request_closed"
- name: "review"
id: "5b33b25ab0388a502999f52d"
events:
- "pull_request_opened"
- "pull_request_reopened"
- name: "closed"
id: "5b33b25ab0388a502999f52e"
events:
- "issues_closed"
16 changes: 16 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ func NewWithDefaults() Config {
Labels: LabelConfig{
Approved: "approved",
},
Board: Board{
"<token>", "<repo>", []Column{},
},
},
nil,
}
Expand Down Expand Up @@ -59,6 +62,7 @@ type RepoConfig struct {
Disabled bool `mapstructure:"disabled"`
Labels LabelConfig `mapstructure:"labels"`
WipPatterns []string `mapstructure:"wipPatterns"`
Board Board `mapstructure:"board"`
}

type LabelConfig struct {
Expand All @@ -67,3 +71,15 @@ type LabelConfig struct {
ReviewRequested string `mapstructure:"reviewRequested"`
Approved string `mapstructure:"approved"`
}

type Board struct {
ZenhubToken string `mapstructure:"zenhub_token"`
GithubRepo string `mapstructure:"github_repo"`
Columns []Column `mapstructure:"columns"`
}

type Column struct {
Name string `mapstructure:"name"`
Id string `mapstructure:"id"`
Events []string `mapstructure:"events"`
}
151 changes: 151 additions & 0 deletions pkg/webhook/board_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package webhook

import (
"context"
"fmt"
"github.com/go-resty/resty"
"github.com/google/go-github/github"
"github.com/syndesisio/pure-bot/pkg/config"
"go.uber.org/zap"
"regexp"
"strconv"
)

type boardUpdate struct{}

func (h *boardUpdate) EventTypesHandled() []string {
return []string{"issues", "pull_request"}
}

type column struct {
name string
id string
}

var stateMapping = map[string]column{}

var zenHubApi = "https://api.zenhub.io"

var regex = regexp.MustCompile("(?mi)(?:clos(?:e[sd]?|ing)|fix(?:e[sd]|ing))[^\\s]*\\s+#(?P<issue>[0-9]+)")

func (h *boardUpdate) HandleEvent(eventObject interface{}, gh *github.Client, config config.RepoConfig, logger *zap.Logger) error {

// initialise from config if needed
if len(stateMapping) == 0 {
for _, col := range config.Board.Columns {
c := column{col.Name, col.Id}
for _, event := range col.Events {
logger.Info("Mapping " + event + " to " + col.Name)
stateMapping[event] = c
}
}
}

switch event := eventObject.(type) {
case *github.IssuesEvent:
return h.handleIssuesEvent(event, gh, config, logger)
case *github.PullRequestEvent:
return h.handlePullRequestEvent(event, gh, config, logger)
default:
return nil
}
}

func (h *boardUpdate) handleIssuesEvent(event *github.IssuesEvent, gh *github.Client, config config.RepoConfig, logger *zap.Logger) error {

var messageType = "issues"

number := strconv.Itoa(*event.Issue.Number)
logger.Debug("Handling issuesEvent for issue " + number)
logger.Debug("Issue Action: " + *event.Action)

eventKey := messageType + "_" + *event.Action
col, ok := stateMapping[eventKey]
if ok {
return moveIssueOnBoard(config, number, col, logger)
} else {
logger.Debug("Ignore unmapped event: " + eventKey)
}

return nil
}

func (h *boardUpdate) handlePullRequestEvent(event *github.PullRequestEvent, gh *github.Client, config config.RepoConfig, logger *zap.Logger) error {

var messageType = "pull_request"

prNumber := strconv.Itoa(*event.PullRequest.Number)
logger.Info("Handling pullReqestEvent for PR " + prNumber)
logger.Info("PR Action: " + *event.Action)

commits, _, err := gh.PullRequests.ListCommits(context.Background(), event.Repo.Owner.GetLogin(), event.Repo.GetName(),
*event.PullRequest.Number, nil)

if err != nil {
logger.Error("Failed to retrieve commits")
return nil
}

for _, commit := range commits {

message := *commit.Commit.Message
logger.Debug("Processing commit message: " + message)

// get issue id from commit message

match := regex.Match([]byte(message))
logger.Debug("regex matches: " + strconv.FormatBool(match))

issues := extractIssueNumbers(message)

for _, issue := range issues {
eventKey := messageType + "_" + *event.Action
col, ok := stateMapping[eventKey]
if ok {
return moveIssueOnBoard(config, issue, col, logger)
} else {
logger.Debug("Ignore ummapped event: " + eventKey)
}
}
}

return nil
}

func extractIssueNumbers(commitMessage string) []string {
groupNames := regex.SubexpNames()
issues := []string{}
for _, match := range regex.FindAllStringSubmatch(commitMessage, -1) {
for groupIdx, _ := range match {
name := groupNames[groupIdx]

if name == "issue" {
issues = append(issues, match[1])
}
}
}

return issues
}

func moveIssueOnBoard(config config.RepoConfig, issue string, col column, logger *zap.Logger) error {

fmt.Println("Moving #" + issue + " to `" + col.name + "`")

url := zenHubApi + "/p1/repositories/" + config.Board.GithubRepo + "/issues/" + issue + "/moves"
response, err := resty.R().
SetHeader("X-Authentication-Token", config.Board.ZenhubToken).
SetHeader("Content-Type", "application/json").
SetBody(`{"pipeline_id":"` + col.id + `", "position": "top"}`).
Post(url)

if err != nil {
return err
}

if response.StatusCode() > 400 {
logger.Warn("API call unsuccessful: HTTP " + strconv.Itoa(response.StatusCode()) + " from " + url)
}

return nil
}
7 changes: 4 additions & 3 deletions pkg/webhook/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var (
&autoMerger{},
&wip{},
&newIssueLabel{},
&boardUpdate{},
// &dismissReview{},
// &failedStatusCheckAddComment{},
}
Expand Down Expand Up @@ -126,10 +127,10 @@ func NewHTTPHandler(cfg config.WebhookConfig, config config.Config, logger *zap.
repo := extractRepository(event)
repoConfig := extractRepoConfigWithDefaults(repo, config)
if repo != nil {
logger.Debug("Processing event", zap.String("repo", *repo.Name))
logger.Debug("Processing event ", zap.String("messageType", messageType), zap.String("repo", *repo.Name))
}
if repoConfig.Disabled {
logger.Debug("Disabled by configuration", zap.String("repo", *repo.Name))
logger.Info("Disabled by configuration", zap.String("repo", *repo.Name))
return
}

Expand All @@ -143,7 +144,7 @@ func NewHTTPHandler(cfg config.WebhookConfig, config config.Config, logger *zap.
// ========================================================================
// Call all handlers
for _, wh := range handlerMap[messageType] {
logger.Debug("call handler", zap.String("type", messageType), zap.String("handler", reflect.TypeOf(wh).String()))
logger.Info("call handler", zap.String("type", messageType), zap.String("handler", reflect.TypeOf(wh).String()))
err = multierr.Combine(err, wh.HandleEvent(event, client, *repoConfig, logger))
}

Expand Down
47 changes: 47 additions & 0 deletions pkg/webhook/webhook_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package webhook

import (
"strings"
"testing"
)

func TestIssueRegex(t *testing.T) {

// key words

issues := extractIssueNumbers("Fixes #19, Fixed #19, Fixing #19")
if len(issues) != 3 {
t.Error("Invalid number of matches")
}

issues = extractIssueNumbers("Closed #19, Closed #19, Closing #19")
if len(issues) != 3 {
t.Error("Invalid number of matches")
}

// single matches
issues = extractIssueNumbers("Fixes #19")
if len(issues) != 1 {
t.Error("Invalid number of matches")
}

if strings.Compare("19", issues[0]) != 0 {
t.Error("Invalid value " + issues[0])
}

// multiple matches
issues = extractIssueNumbers("Fixes #19, Closes #20")

if len(issues) != 2 {
t.Error("Invalid number of matches")
}

if strings.Compare("19", issues[0]) != 0 {
t.Error("Invalid value " + issues[0])
}

if strings.Compare("20", issues[1]) != 0 {
t.Error("Invalid value " + issues[1])
}

}

0 comments on commit 6752494

Please sign in to comment.