Skip to content

Commit

Permalink
feat(slack): add basic integration with Slack
Browse files Browse the repository at this point in the history
If SLACK_WEBHOOK is set then Release Notary will send a simple Slack markdown compliant message to it.
  • Loading branch information
fallion committed Nov 20, 2019
1 parent 54a2ed5 commit a60201f
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .drone.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ steps:
GITHUB_TOKEN:
from_secret: github_token
GITHUB_REPOSITORY: commitsar-app/release-notary
SLACK_WEBHOOK:
from_secret: slack_webhook
commands:
- go run main.go publish
when:
Expand Down
13 changes: 13 additions & 0 deletions cmd/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

history "github.com/commitsar-app/git/pkg"
"github.com/commitsar-app/release-notary/internal/releaser"
"github.com/commitsar-app/release-notary/internal/slack"
"github.com/commitsar-app/release-notary/internal/text"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -118,6 +119,18 @@ var publishCmd = &cobra.Command{
return err
}

if viper.IsSet("SLACK_WEBHOOK") {
slack := &slack.Slack{
WebHookURL: viper.GetString("SLACK_WEBHOOK"),
}

err = slack.Publish(sections)

if err != nil {
return err
}
}

return nil
},
}
39 changes: 39 additions & 0 deletions internal/slack/publish.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package slack

import (
"bytes"
"net/http"
"time"

"github.com/commitsar-app/release-notary/internal/text"
jsoniter "github.com/json-iterator/go"
)

var json = jsoniter.ConfigCompatibleWithStandardLibrary

type request struct {
Text string `json:"text"`
}

// Publish pushes the release notes to Slack via provided Webhook. https://api.slack.com/reference/messaging/payload
func (s *Slack) Publish(commits map[string][]text.Commit) error {
releaseNotes := GenerateReleaseNotes(commits)

client := http.Client{
Timeout: time.Second * 5,
}

jsonBody, err := json.Marshal(request{Text: releaseNotes})

if err != nil {
return err
}

_, err = client.Post(s.WebHookURL, "application/json", bytes.NewBuffer(jsonBody))

if err != nil {
return err
}

return nil
}
47 changes: 47 additions & 0 deletions internal/slack/publish_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package slack

import (
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"

"github.com/commitsar-app/release-notary/internal/text"
"github.com/stretchr/testify/assert"
)

func TestPublish(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "/webhook", req.URL.String())
assert.Equal(t, "application/json", req.Header["Content-Type"][0])

body, err := ioutil.ReadAll(req.Body)

assert.NoError(t, err)

expectedBody := "{\"text\":\"*Features*\\r\\nci test\\r\\n\\r\\n*Bug fixes*\\r\\nhuge bug\\r\\nbug fix\\r\\n\\r\\n*Chores and Improvements*\\r\\ntesting\\r\\nthis should end up in chores\\r\\n\\r\\n*Other*\\r\\nmerge master in something\\r\\nrandom\\r\\n\\r\\n\"}"

assert.Equal(t, expectedBody, string(body))

_, err = rw.Write([]byte(`ok`))

assert.NoError(t, err)
}))

defer server.Close()

slack := &Slack{
WebHookURL: server.URL + "/webhook",
}

testData := map[string][]text.Commit{
"features": []text.Commit{text.Commit{Category: "feat", Scope: "ci", Heading: "ci test"}},
"chores": []text.Commit{text.Commit{Category: "chore", Scope: "", Heading: "testing"}, text.Commit{Category: "improvement", Scope: "", Heading: "this should end up in chores"}},
"bugs": []text.Commit{text.Commit{Category: "bug", Scope: "", Heading: "huge bug"}, text.Commit{Category: "fix", Scope: "", Heading: "bug fix"}},
"others": []text.Commit{text.Commit{Category: "other", Scope: "", Heading: "merge master in something"}, text.Commit{Category: "bs", Scope: "", Heading: "random"}},
}

err := slack.Publish(testData)

assert.NoError(t, err)
}
49 changes: 49 additions & 0 deletions internal/slack/release_notes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package slack

import (
"strings"

"github.com/commitsar-app/release-notary/internal/text"
)

// GenerateReleaseNotes creates a string from release notes that conforms with the Slack formatting. Expected format can be found in testdata.
func GenerateReleaseNotes(sections map[string][]text.Commit) string {
builder := strings.Builder{}

if len(sections["features"]) > 0 {
builder.WriteString("*Features*\r\n")
builder.WriteString(buildSection(sections["features"]))
builder.WriteString("\r\n")
}

if len(sections["bugs"]) > 0 {
builder.WriteString("*Bug fixes*\r\n")
builder.WriteString(buildSection(sections["bugs"]))
builder.WriteString("\r\n")
}

if len(sections["chores"]) > 0 {
builder.WriteString("*Chores and Improvements*\r\n")
builder.WriteString(buildSection(sections["chores"]))
builder.WriteString("\r\n")
}

if len(sections["others"]) > 0 {
builder.WriteString("*Other*\r\n")
builder.WriteString(buildSection(sections["others"]))
builder.WriteString("\r\n")
}

return builder.String()
}

func buildSection(commits []text.Commit) string {
builder := strings.Builder{}

for _, commit := range commits {
builder.WriteString(commit.Heading)
builder.WriteString("\r\n")
}

return builder.String()
}
19 changes: 19 additions & 0 deletions internal/slack/release_notes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package slack

import (
"testing"

"github.com/commitsar-app/release-notary/internal/text"
"github.com/stretchr/testify/assert"
)

func TestGenerateReleaseNotes(t *testing.T) {
testData := map[string][]text.Commit{
"features": []text.Commit{text.Commit{Category: "feat", Scope: "ci", Heading: "ci test"}},
"chores": []text.Commit{text.Commit{Category: "chore", Scope: "", Heading: "testing"}, text.Commit{Category: "improvement", Scope: "", Heading: "this should end up in chores"}},
"bugs": []text.Commit{text.Commit{Category: "bug", Scope: "", Heading: "huge bug"}, text.Commit{Category: "fix", Scope: "", Heading: "bug fix"}},
"others": []text.Commit{text.Commit{Category: "other", Scope: "", Heading: "merge master in something"}, text.Commit{Category: "bs", Scope: "", Heading: "random"}},
}

assert.Equal(t, "*Features*\r\nci test\r\n\r\n*Bug fixes*\r\nhuge bug\r\nbug fix\r\n\r\n*Chores and Improvements*\r\ntesting\r\nthis should end up in chores\r\n\r\n*Other*\r\nmerge master in something\r\nrandom\r\n\r\n", GenerateReleaseNotes(testData))
}
6 changes: 6 additions & 0 deletions internal/slack/slack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package slack

// Slack is the struct holding all the methods to work with the Slack integration.
type Slack struct {
WebHookURL string
}
15 changes: 15 additions & 0 deletions internal/slack/testdata/expected_format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*Features*
ci test

*Bug fixes*
huge bug
bug fix

*Chores and Improvements*
testing
this should end up in chores

*Other*
merge master in something
random

0 comments on commit a60201f

Please sign in to comment.