mirror of
https://github.com/goreleaser/goreleaser.git
synced 2026-06-19 23:24:39 +02:00
feat: announce: slack (#2429)
Fixes #2425 Signed-off-by: Furkan <furkan.turkal@trendyol.com>
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
@@ -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=
|
||||
@@ -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=
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
+31
-22
@@ -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"`
|
||||
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.
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -59,5 +60,6 @@ var Defaulters = []Defaulter{
|
||||
scoop.Pipe{},
|
||||
twitter.Pipe{},
|
||||
reddit.Pipe{},
|
||||
slack.Pipe{},
|
||||
milestone.Pipe{},
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: ''
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Learn more about the [name template engine](/customization/templates/).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user