Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: announce: slack #2429

Merged
merged 1 commit into from Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions go.mod
Expand Up @@ -95,6 +95,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 @@ -115,3 +116,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 @@ -6,6 +6,7 @@ import (

"github.com/goreleaser/goreleaser/internal/middleware"
"github.com/goreleaser/goreleaser/internal/pipe/reddit"
"github.com/goreleaser/goreleaser/internal/pipe/slack"
"github.com/goreleaser/goreleaser/internal/pipe/twitter"
"github.com/goreleaser/goreleaser/pkg/context"
)
Expand All @@ -28,6 +29,7 @@ type Announcer interface {
var announcers = []Announcer{
twitter.Pipe{}, // announce to twitter
reddit.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))
}
53 changes: 31 additions & 22 deletions pkg/config/config.go
Expand Up @@ -90,28 +90,27 @@ func (r Repo) String() string {

// Homebrew contains the brew section.
type Homebrew struct {
Name string `yaml:",omitempty"`
Tap RepoRef `yaml:",omitempty"`
CommitAuthor CommitAuthor `yaml:"commit_author,omitempty"`
CommitMessageTemplate string `yaml:"commit_msg_template,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this field needs to stay

Folder string `yaml:",omitempty"`
Caveats string `yaml:",omitempty"`
Plist string `yaml:",omitempty"`
Install string `yaml:",omitempty"`
PostInstall string `yaml:"post_install,omitempty"`
Dependencies []HomebrewDependency `yaml:",omitempty"`
Test string `yaml:",omitempty"`
Conflicts []string `yaml:",omitempty"`
Description string `yaml:",omitempty"`
Homepage string `yaml:",omitempty"`
License string `yaml:",omitempty"`
SkipUpload string `yaml:"skip_upload,omitempty"`
DownloadStrategy string `yaml:"download_strategy,omitempty"`
URLTemplate string `yaml:"url_template,omitempty"`
CustomRequire string `yaml:"custom_require,omitempty"`
CustomBlock string `yaml:"custom_block,omitempty"`
IDs []string `yaml:"ids,omitempty"`
Goarm string `yaml:"goarm,omitempty"`
Name string `yaml:",omitempty"`
Tap RepoRef `yaml:",omitempty"`
CommitAuthor CommitAuthor `yaml:"commit_author,omitempty"`
Folder string `yaml:",omitempty"`
Caveats string `yaml:",omitempty"`
Plist string `yaml:",omitempty"`
Install string `yaml:",omitempty"`
PostInstall string `yaml:"post_install,omitempty"`
Dependencies []HomebrewDependency `yaml:",omitempty"`
Test string `yaml:",omitempty"`
Conflicts []string `yaml:",omitempty"`
Description string `yaml:",omitempty"`
Homepage string `yaml:",omitempty"`
License string `yaml:",omitempty"`
SkipUpload string `yaml:"skip_upload,omitempty"`
DownloadStrategy string `yaml:"download_strategy,omitempty"`
URLTemplate string `yaml:"url_template,omitempty"`
CustomRequire string `yaml:"custom_require,omitempty"`
CustomBlock string `yaml:"custom_block,omitempty"`
IDs []string `yaml:"ids,omitempty"`
Goarm string `yaml:"goarm,omitempty"`
}

// Scoop contains the scoop.sh section.
Expand Down Expand Up @@ -692,6 +691,7 @@ type GoMod struct {
type Announce struct {
Twitter Twitter `yaml:"twitter,omitempty"`
Reddit Reddit `yaml:"reddit,omitempty"`
Slack Slack `yaml:"slack,omitempty"`
}

type Twitter struct {
Expand All @@ -708,6 +708,15 @@ type Reddit struct {
Sub string `yaml:"sub,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 @@ -17,6 +17,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
"github.com/goreleaser/goreleaser/internal/pipe/project"
"github.com/goreleaser/goreleaser/internal/pipe/reddit"
"github.com/goreleaser/goreleaser/internal/pipe/slack"
"github.com/goreleaser/goreleaser/internal/pipe/release"
"github.com/goreleaser/goreleaser/internal/pipe/scoop"
"github.com/goreleaser/goreleaser/internal/pipe/sign"
Expand Down Expand Up @@ -59,5 +60,6 @@ var Defaulters = []Defaulter{
scoop.Pipe{},
twitter.Pipe{},
reddit.Pipe{},
slack.Pipe{},
milestone.Pipe{},
}
35 changes: 34 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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


It runs at the very end of the pipeline.

Expand Down Expand Up @@ -30,6 +30,39 @@ 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`

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!'

# The name of the channel that the user selected as a destination for webhook messages.
channel: '#channel'

# Set your Webhook's user name.
username: ''

# Emoji to use as the icon for this message. Overrides icon_url.
icon_emoji: ''

# URL to an image to use as the icon for this message.
icon_url: ''
caarlos0 marked this conversation as resolved.
Show resolved Hide resolved
```

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

Expand Down