Skip to content

Commit

Permalink
feat: announce: slack
Browse files Browse the repository at this point in the history
Fixes #2425

Signed-off-by: Furkan <furkan.turkal@trendyol.com>
  • Loading branch information
Dentrax committed Aug 31, 2021
1 parent d580a48 commit 41a9719
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 1 deletion.
3 changes: 3 additions & 0 deletions go.mod
Expand Up @@ -94,6 +94,7 @@ require (
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/slack-go/slack v0.9.4
github.com/spf13/pflag v1.0.5 // indirect
github.com/xanzy/ssh-agent v0.3.0 // indirect
go.opencensus.io v0.23.0 // indirect
Expand All @@ -114,3 +115,5 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

require github.com/gorilla/websocket v1.4.2 // indirect
4 changes: 4 additions & 0 deletions go.sum
Expand Up @@ -244,6 +244,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
Expand Down Expand Up @@ -351,6 +352,7 @@ github.com/goreleaser/fileglob v1.2.0/go.mod h1:rFyb2pXaK3YdnYnSjn6lifw0h2Q6s8Of
github.com/goreleaser/nfpm/v2 v2.6.0 h1:bwDU9o4/CVTSpqASJA7+r+rkqpTGamQKYHMRH3wDlRE=
github.com/goreleaser/nfpm/v2 v2.6.0/go.mod h1:qaMnjBaZz/2vInOIWx0IbuKuaZpaVB6O8oLG0u4qH1Y=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
Expand Down Expand Up @@ -532,6 +534,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/slack-go/slack v0.9.4 h1:C+FC3zLxLxUTQjDy2RZeMHYon005zsCROiZNWVo+opQ=
github.com/slack-go/slack v0.9.4/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
Expand Down
2 changes: 2 additions & 0 deletions internal/pipe/announce/announce.go
Expand Up @@ -3,6 +3,7 @@ package announce

import (
"fmt"
"github.com/goreleaser/goreleaser/internal/pipe/slack"

"github.com/goreleaser/goreleaser/internal/middleware"
"github.com/goreleaser/goreleaser/internal/pipe/twitter"
Expand All @@ -26,6 +27,7 @@ type Announcer interface {
// nolint: gochecknoglobals
var announcers = []Announcer{
twitter.Pipe{}, // announce to twitter
slack.Pipe{}, // announce to slack
}

// Run the pipe.
Expand Down
71 changes: 71 additions & 0 deletions internal/pipe/slack/slack.go
@@ -0,0 +1,71 @@
package slack

import (
"fmt"
"github.com/slack-go/slack"

"github.com/apex/log"
"github.com/caarlos0/env/v6"
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/context"
)

const (
defaultUsername = `GoReleaser`
defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .GitURL }}/releases/tag/{{ .Tag }}`
)

type Pipe struct{}

func (Pipe) String() string { return "slack" }

type Config struct {
Webhook string `env:"SLACK_WEBHOOK,notEmpty"`
}

func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Announce.Slack.MessageTemplate == "" {
ctx.Config.Announce.Slack.MessageTemplate = defaultMessageTemplate
}
if ctx.Config.Announce.Slack.Username == "" {
ctx.Config.Announce.Slack.Username = defaultUsername
}
return nil
}

func (Pipe) Announce(ctx *context.Context) error {
if ctx.SkipAnnounce {
return pipe.ErrSkipAnnounceEnabled
}
if !ctx.Config.Announce.Slack.Enabled {
return pipe.ErrSkipDisabledPipe
}

msg, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Slack.MessageTemplate)
if err != nil {
return fmt.Errorf("announce: failed to announce to slack: %w", err)
}

var cfg Config
if err := env.Parse(&cfg); err != nil {
return fmt.Errorf("announce: failed to announce to slack: %w", err)
}

log.Infof("posting: '%s'", msg)

wm := &slack.WebhookMessage{
Username: ctx.Config.Announce.Slack.Username,
IconEmoji: ctx.Config.Announce.Slack.IconEmoji,
IconURL: ctx.Config.Announce.Slack.IconURL,
Channel: ctx.Config.Announce.Slack.Channel,
Text: msg,
}

err = slack.PostWebhook(cfg.Webhook, wm)
if err != nil {
return fmt.Errorf("announce: failed to announce to slack: %w", err)
}

return nil
}
62 changes: 62 additions & 0 deletions internal/pipe/slack/slack_test.go
@@ -0,0 +1,62 @@
package slack

import (
"testing"

"github.com/goreleaser/goreleaser/internal/testlib"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
"github.com/stretchr/testify/require"
)

func TestStringer(t *testing.T) {
require.Equal(t, Pipe{}.String(), "slack")
}

func TestDefault(t *testing.T) {
ctx := context.New(config.Project{})
require.NoError(t, Pipe{}.Default(ctx))
require.Equal(t, ctx.Config.Announce.Slack.MessageTemplate, defaultMessageTemplate)
}

func TestAnnounceDisabled(t *testing.T) {
ctx := context.New(config.Project{})
require.NoError(t, Pipe{}.Default(ctx))
testlib.AssertSkipped(t, Pipe{}.Announce(ctx))
}

func TestAnnounceInvalidTemplate(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Slack: config.Slack{
Enabled: true,
MessageTemplate: "{{ .Foo }",
},
},
})
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to slack: template: tmpl:1: unexpected "}" in operand`)
}

func TestAnnounceMissingEnv(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Slack: config.Slack{
Enabled: true,
},
},
})
require.NoError(t, Pipe{}.Default(ctx))
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to slack: env: environment variable "SLACK_WEBHOOK" should not be empty`)
}

func TestAnnounceSkipAnnounce(t *testing.T) {
ctx := context.New(config.Project{
Announce: config.Announce{
Slack: config.Slack{
Enabled: true,
},
},
})
ctx.SkipAnnounce = true
testlib.AssertSkipped(t, Pipe{}.Announce(ctx))
}
10 changes: 10 additions & 0 deletions pkg/config/config.go
Expand Up @@ -690,13 +690,23 @@ type GoMod struct {

type Announce struct {
Twitter Twitter `yaml:"twitter,omitempty"`
Slack Slack `yaml:"slack,omitempty"`
}

type Twitter struct {
Enabled bool `yaml:"enabled,omitempty"`
MessageTemplate string `yaml:"message_template,omitempty"`
}

type Slack struct {
Enabled bool `yaml:"enabled,omitempty"`
MessageTemplate string `yaml:"message_template,omitempty"`
Channel string `yaml:"channel,omitempty"`
Username string `yaml:"username,omitempty"`
IconEmoji string `yaml:"icon_emoji,omitempty"`
IconURL string `yaml:"icon_url,omitempty"`
}

// Load config file.
func Load(file string) (config Project, err error) {
f, err := os.Open(file) // #nosec
Expand Down
2 changes: 2 additions & 0 deletions pkg/defaults/defaults.go
Expand Up @@ -4,6 +4,7 @@ package defaults

import (
"fmt"
"github.com/goreleaser/goreleaser/internal/pipe/slack"

"github.com/goreleaser/goreleaser/internal/pipe/archive"
"github.com/goreleaser/goreleaser/internal/pipe/artifactory"
Expand Down Expand Up @@ -57,5 +58,6 @@ var Defaulters = []Defaulter{
brew.Pipe{},
scoop.Pipe{},
twitter.Pipe{},
slack.Pipe{},
milestone.Pipe{},
}
27 changes: 26 additions & 1 deletion www/docs/customization/announce.md
Expand Up @@ -2,7 +2,7 @@
title: Announce
---

GoReleaser can also announce new releases, currently, to Twitter only.
GoReleaser can also announce new releases, currently, to Twitter and Slack only.

It runs at the very end of the pipeline.

Expand Down Expand Up @@ -30,5 +30,30 @@ announce:
message_template: 'Awesome project {{.Tag}} is out!'
```

## Slack

For it to work, you'll need to [create a new Incoming Webhook](https://api.slack.com/messaging/webhooks), and set some environment variables on your pipeline:

- `SLACK_WEBHOOK`
- `SLACK_CHANNEL`
- `SLACK_USERNAME`
- `SLACK_ICON_EMOJI`
- `SLACK_ICON_URL`

Then, you can add something like the following to your `.goreleaser.yml` config:

```yaml
# .goreleaser.yml
announce:
slack:
# Wether its enabled or not.
# Defaults to false.
enabled: true

# Message template to use while publishing.
# Defaults to `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .GitURL }}/releases/tag/{{ .Tag }}`
message_template: 'Awesome project {{.Tag}} is out!'
```

!!! tip
Learn more about the [name template engine](/customization/templates/).

0 comments on commit 41a9719

Please sign in to comment.