diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 0ca983eb..6ded8527 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -174,6 +174,10 @@ "Comment": "1.5.0-168-g2abea07", "Rev": "2abea075ec076abf0572d29bdb28ae7da64cfb7a" }, + { + "ImportPath": "github.com/stvp/flowdock", + "Rev": "50362abeabebf40b0f56a326d62f17e61a59302b" + }, { "ImportPath": "launchpad.net/goyaml", "Comment": "51", diff --git a/Godeps/_workspace/src/github.com/stvp/flowdock/README.md b/Godeps/_workspace/src/github.com/stvp/flowdock/README.md new file mode 100644 index 00000000..16da2b06 --- /dev/null +++ b/Godeps/_workspace/src/github.com/stvp/flowdock/README.md @@ -0,0 +1,53 @@ +flowdock +======== + +A Flowdock client for Golang. See the [Flowdock API docs][api_docs] for more +information on the individual attributes. + +[Go API Documentation][godocs] + +Examples +-------- + +You can set global defaults and use `flowdock.Inbox()` directly: + +```go +import ( + "github.com/stvp/flowdock" +) + +func main() { + flowdock.Token = "732da505d0284d5b1d909b1a32426345" + flowdock.Source = "My Cool App" + flowdock.FromAddress = "cool@dudes.com" + // See API docs for more variables + + go flowdock.Inbox("My subject", "My content goes here.") +} +``` + +Or you can create a `flowdock.Client` to use different Flowdock message +settings in the same app: + +```go +import ( + "github.com/stvp/flowdock" +) + +func main() { + client := flowdock.Client{ + Token: "732da505d0284d5b1d909b1a32426345", + Source: "App A", + FromAddress: "email@stovepipestudios.com", + FromName: "Client A", + ReplyTo: "app_a@stovepipestudios.com", + Tags: []string{"app_a"}, + } + + go client.Inbox("Subject", "Content") +} +``` + +[api_docs]: https://www.flowdock.com/api/team-inbox +[godocs]: http://godoc.org/github.com/stvp/flowdock + diff --git a/Godeps/_workspace/src/github.com/stvp/flowdock/flowdock.go b/Godeps/_workspace/src/github.com/stvp/flowdock/flowdock.go new file mode 100644 index 00000000..ba0c7835 --- /dev/null +++ b/Godeps/_workspace/src/github.com/stvp/flowdock/flowdock.go @@ -0,0 +1,105 @@ +package flowdock + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +const ( + ENDPOINT = "https://api.flowdock.com/v1/messages/team_inbox/" +) + +var ( + // Required default client settings + Token = "" + Source = "" + FromAddress = "" + + // Optional default client settings + FromName = "" + ReplyTo = "" + Project = "" + Link = "" + Tags = []string{} +) + +type Client struct { + // Required + Token string + Source string + FromAddress string + Subject string + Content string + + // Optional + FromName string + ReplyTo string + Project string + Link string + Tags []string +} + +func (c *Client) Inbox(subject, content string) error { + return send(c.Token, c.Source, c.FromAddress, subject, content, c.FromName, c.ReplyTo, c.Project, c.Link, c.Tags) +} + +func Inbox(subject, content string) error { + return send(Token, Source, FromAddress, subject, content, FromName, ReplyTo, Project, Link, Tags) +} + +func send(token, source, fromAddress, subject, content, fromName, replyTo, project, link string, tags []string) error { + // Required validation + if len(token) == 0 { + return fmt.Errorf(`"Token" is required`) + } + if len(source) == 0 { + return fmt.Errorf(`"Source" is required`) + } + if len(fromAddress) == 0 { + return fmt.Errorf(`"FromAddress" is required`) + } + if len(subject) == 0 { + return fmt.Errorf(`"Subject" is required`) + } + + // Build payload + payload := map[string]interface{}{ + "source": source, + "from_address": fromAddress, + "subject": subject, + "content": content, + } + if len(fromName) > 0 { + payload["from_name"] = fromName + } + if len(replyTo) > 0 { + payload["reply_to"] = replyTo + } + if len(project) > 0 { + payload["project"] = project + } + if len(link) > 0 { + payload["link"] = link + } + if len(tags) > 0 { + payload["tags"] = tags + } + jsonPayload, err := json.Marshal(payload) + if err != nil { + return err + } + + // Send to Flowdock + resp, err := http.Post(ENDPOINT+token, "application/json", bytes.NewReader(jsonPayload)) + defer resp.Body.Close() + + if resp.StatusCode == 200 { + return nil + } else { + bodyBytes, _ := ioutil.ReadAll(resp.Body) + return fmt.Errorf("Unexpected response from Flowdock: %s %s", resp.Status, string(bodyBytes)) + } +} diff --git a/pkg/plugin/notify/flowdock.go b/pkg/plugin/notify/flowdock.go new file mode 100644 index 00000000..5837fc3b --- /dev/null +++ b/pkg/plugin/notify/flowdock.go @@ -0,0 +1,90 @@ +package notify + +import ( + "fmt" + "strings" + "net/url" + "github.com/stvp/flowdock" +) + +const ( + flowdockStartedSubject = "Building %s (%s)" + flowdockSuccessSubject = "Build: %s (%s) is SUCCESS" + flowdockFailureSubject = "Build: %s (%s) is FAILED" + flowdockMessage = "

%s

\nBuild: %s
\nResult: %s
\nAuthor: %s
Commit: %s
\nRepository Url: %s" + flowdockBuildOkEmail = "build+ok@flowdock.com" + flowdockBuildFailEmail = "build+fail@flowdock.com"; +) + +type Flowdock struct { + Token string `yaml:"token,omitempty"` + Source string `yaml:"source,omitempty"` + Tags string `yaml:"tags,omitempty"` + Started bool `yaml:"on_started,omitempty"` + Success bool `yaml:"on_success,omitempty"` + Failure bool `yaml:"on_failure,omitempty"` +} + +func (f *Flowdock) Send(context *Context) error { + switch { + case context.Commit.Status == "Started" && f.Started: + return f.sendStarted(context) + case context.Commit.Status == "Success" && f.Success: + return f.sendSuccess(context) + case context.Commit.Status == "Failure" && f.Failure: + return f.sendFailure(context) + } + + return nil +} + +func (f *Flowdock) getBuildUrl(context *Context) string { + branchQuery := url.Values{} + if context.Commit.Branch != "" { + branchQuery.Set("branch", context.Commit.Branch) + } + + return fmt.Sprintf("%s/%s/commit/%s?%s", context.Host, context.Repo.Slug, context.Commit.Hash, branchQuery.Encode()) +} + +func (f *Flowdock) getRepoUrl(context *Context) string { + return fmt.Sprintf("%s/%s", context.Host, context.Repo.Slug) +} + +func (f *Flowdock) getMessage(context *Context) string { + buildUrl := fmt.Sprintf("%s", f.getBuildUrl(context), context.Commit.HashShort()) + return fmt.Sprintf(flowdockMessage, context.Repo.Name, buildUrl, context.Commit.Status, context.Commit.Author, context.Commit.Message, f.getRepoUrl(context)) +} + +func (f *Flowdock) sendStarted(context *Context) error { + fromAddress := context.Commit.Author + subject := fmt.Sprintf(flowdockStartedSubject, context.Repo.Name, context.Commit.Branch) + msg := f.getMessage(context) + tags := strings.Split(f.Tags, ",") + return f.send(fromAddress, subject, msg, tags) +} + +func (f *Flowdock) sendFailure(context *Context) error { + fromAddress := flowdockBuildFailEmail + tags := strings.Split(f.Tags, ",") + subject := fmt.Sprintf(flowdockFailureSubject, context.Repo.Name, context.Commit.Branch) + msg := f.getMessage(context) + return f.send(fromAddress, subject, msg, tags) +} + +func (f *Flowdock) sendSuccess(context *Context) error { + fromAddress := flowdockBuildOkEmail + tags := strings.Split(f.Tags, ",") + subject := fmt.Sprintf(flowdockSuccessSubject, context.Repo.Name, context.Commit.Branch) + msg := f.getMessage(context) + return f.send(fromAddress, subject, msg, tags) +} + +// helper function to send Flowdock requests +func (f *Flowdock) send(fromAddress, subject, message string, tags []string) error { + + c := flowdock.Client{Token: f.Token, Source: f.Source, FromName: "drone.io", FromAddress: fromAddress, Tags: tags} + + go c.Inbox(subject, message) + return nil +} diff --git a/pkg/plugin/notify/notification.go b/pkg/plugin/notify/notification.go index 31b2ce3e..c0df619e 100644 --- a/pkg/plugin/notify/notification.go +++ b/pkg/plugin/notify/notification.go @@ -28,11 +28,12 @@ type Sender interface { // for notifying a user, or group of users, // when their Build has completed. type Notification struct { - Email *Email `yaml:"email,omitempty"` - Webhook *Webhook `yaml:"webhook,omitempty"` - Hipchat *Hipchat `yaml:"hipchat,omitempty"` - Irc *IRC `yaml:"irc,omitempty"` - Slack *Slack `yaml:"slack,omitempty"` + Email *Email `yaml:"email,omitempty"` + Webhook *Webhook `yaml:"webhook,omitempty"` + Hipchat *Hipchat `yaml:"hipchat,omitempty"` + Irc *IRC `yaml:"irc,omitempty"` + Slack *Slack `yaml:"slack,omitempty"` + Flowdock *Flowdock `yaml:"flowdock,omitempty"` } func (n *Notification) Send(context *Context) error { @@ -61,5 +62,10 @@ func (n *Notification) Send(context *Context) error { n.Slack.Send(context) } + // send flowdock notifications + if n.Flowdock != nil { + n.Flowdock.Send(context) + } + return nil }