mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-11 14:39:28 +02:00
feat: announce: mattermost (#2536)
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
This commit is contained in:
parent
d2a63c1093
commit
519a4f67c5
@ -8,6 +8,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/middleware/logging"
|
||||
"github.com/goreleaser/goreleaser/internal/middleware/skip"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/discord"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/mattermost"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/reddit"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/slack"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/smtp"
|
||||
@ -27,6 +28,7 @@ type Announcer interface {
|
||||
var announcers = []Announcer{
|
||||
// XXX: keep asc sorting
|
||||
discord.Pipe{},
|
||||
mattermost.Pipe{},
|
||||
reddit.Pipe{},
|
||||
slack.Pipe{},
|
||||
smtp.Pipe{},
|
||||
|
147
internal/pipe/mattermost/mattermost.go
Normal file
147
internal/pipe/mattermost/mattermost.go
Normal file
@ -0,0 +1,147 @@
|
||||
package mattermost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/caarlos0/env/v6"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultColor = "#2D313E"
|
||||
defaultUsername = `GoReleaser`
|
||||
defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .GitURL }}/releases/tag/{{ .Tag }}`
|
||||
defaultMessageTitle = `{{ .ProjectName }} {{ .Tag }} is out!`
|
||||
)
|
||||
|
||||
type Pipe struct{}
|
||||
|
||||
func (Pipe) String() string { return "mattermost" }
|
||||
func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Config.Announce.Mattermost.Enabled }
|
||||
|
||||
type Config struct {
|
||||
Webhook string `env:"MATTERMOST_WEBHOOK,notEmpty"`
|
||||
}
|
||||
|
||||
func (Pipe) Default(ctx *context.Context) error {
|
||||
if ctx.Config.Announce.Mattermost.MessageTemplate == "" {
|
||||
ctx.Config.Announce.Mattermost.MessageTemplate = defaultMessageTemplate
|
||||
}
|
||||
if ctx.Config.Announce.Mattermost.TitleTemplate == "" {
|
||||
ctx.Config.Announce.Mattermost.TitleTemplate = defaultMessageTitle
|
||||
}
|
||||
if ctx.Config.Announce.Mattermost.Username == "" {
|
||||
ctx.Config.Announce.Mattermost.Username = defaultUsername
|
||||
}
|
||||
if ctx.Config.Announce.Teams.Color == "" {
|
||||
ctx.Config.Announce.Teams.Color = defaultColor
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Pipe) Announce(ctx *context.Context) error {
|
||||
msg, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Mattermost.MessageTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("announce: failed to announce to mattermost: %w", err)
|
||||
}
|
||||
|
||||
title, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Mattermost.TitleTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("announce: failed to announce to teams: %w", err)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := env.Parse(&cfg); err != nil {
|
||||
return fmt.Errorf("announce: failed to announce to mattermost: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("posting: %q", msg)
|
||||
|
||||
wm := &incomingWebhookRequest{
|
||||
Username: ctx.Config.Announce.Mattermost.Username,
|
||||
IconEmoji: ctx.Config.Announce.Mattermost.IconEmoji,
|
||||
IconURL: ctx.Config.Announce.Mattermost.IconURL,
|
||||
ChannelName: ctx.Config.Announce.Mattermost.Channel,
|
||||
Attachments: []*mattermostAttachment{
|
||||
{
|
||||
Title: title,
|
||||
Text: msg,
|
||||
Color: ctx.Config.Announce.Teams.Color,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = postWebhook(ctx, cfg.Webhook, wm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("announce: failed to announce to mattermost: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func postWebhook(ctx *context.Context, url string, msg *incomingWebhookRequest) error {
|
||||
payload, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal the message: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed new request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
r, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("announce: failed to announce to mattermost: %w", err)
|
||||
}
|
||||
closeBody(r)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func closeBody(r *http.Response) {
|
||||
if r.Body != nil {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
_ = r.Body.Close()
|
||||
}
|
||||
}
|
||||
|
||||
type incomingWebhookRequest struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
ChannelName string `json:"channel"`
|
||||
Attachments []*mattermostAttachment `json:"attachments"`
|
||||
IconEmoji string `json:"icon_emoji"`
|
||||
}
|
||||
|
||||
type mattermostAttachment struct {
|
||||
Fallback string `json:"fallback"`
|
||||
Color string `json:"color"`
|
||||
Pretext string `json:"pretext"`
|
||||
AuthorName string `json:"author_name"`
|
||||
AuthorLink string `json:"author_link"`
|
||||
AuthorIcon string `json:"author_icon"`
|
||||
Title string `json:"title"`
|
||||
TitleLink string `json:"title_link"`
|
||||
Text string `json:"text"`
|
||||
Fields []*mattermostAttachmentField `json:"fields"`
|
||||
Footer string `json:"footer"`
|
||||
FooterIcon string `json:"footer_icon"`
|
||||
}
|
||||
|
||||
type mattermostAttachmentField struct {
|
||||
Title string `json:"title"`
|
||||
Value interface{} `json:"value"`
|
||||
Short bool `json:"short"`
|
||||
}
|
99
internal/pipe/mattermost/mattermost_test.go
Normal file
99
internal/pipe/mattermost/mattermost_test.go
Normal file
@ -0,0 +1,99 @@
|
||||
package mattermost
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
)
|
||||
|
||||
func TestStringer(t *testing.T) {
|
||||
require.Equal(t, Pipe{}.String(), "mattermost")
|
||||
}
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
ctx := context.New(config.Project{})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.Equal(t, ctx.Config.Announce.Mattermost.MessageTemplate, defaultMessageTemplate)
|
||||
}
|
||||
|
||||
func TestAnnounceInvalidTemplate(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Announce: config.Announce{
|
||||
Mattermost: config.Mattermost{
|
||||
MessageTemplate: "{{ .Foo }",
|
||||
},
|
||||
},
|
||||
})
|
||||
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to mattermost: template: tmpl:1: unexpected "}" in operand`)
|
||||
}
|
||||
|
||||
func TestAnnounceMissingEnv(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Announce: config.Announce{
|
||||
Mattermost: config.Mattermost{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.EqualError(t, Pipe{}.Announce(ctx), `announce: failed to announce to mattermost: env: environment variable "MATTERMOST_WEBHOOK" should not be empty`)
|
||||
}
|
||||
|
||||
func TestSkip(t *testing.T) {
|
||||
t.Run("skip", func(t *testing.T) {
|
||||
require.True(t, Pipe{}.Skip(context.New(config.Project{})))
|
||||
})
|
||||
|
||||
t.Run("dont skip", func(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Announce: config.Announce{
|
||||
Mattermost: config.Mattermost{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
require.False(t, Pipe{}.Skip(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPostWebhook(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rc := &incomingWebhookRequest{}
|
||||
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
err := json.Unmarshal(body, rc)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, defaultColor, rc.Attachments[0].Color)
|
||||
require.Equal(t, "Honk v1.0.0 is out!", rc.Attachments[0].Title)
|
||||
require.Equal(t, "Honk v1.0.0 is out! Check it out at https://github.com/honk/honk/releases/tag/v1.0.0", rc.Attachments[0].Text)
|
||||
|
||||
w.WriteHeader(200)
|
||||
_, err = w.Write([]byte{})
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
ctx := context.New(config.Project{
|
||||
ProjectName: "Honk",
|
||||
Announce: config.Announce{
|
||||
Mattermost: config.Mattermost{
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
ctx.Git.CurrentTag = "v1.0.0"
|
||||
ctx.Git.URL = "https://github.com/honk/honk"
|
||||
|
||||
os.Setenv("MATTERMOST_WEBHOOK", ts.URL)
|
||||
defer os.Unsetenv("MATTERMOST_WEBHOOK")
|
||||
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Announce(ctx))
|
||||
}
|
@ -707,12 +707,13 @@ type GoMod struct {
|
||||
}
|
||||
|
||||
type Announce struct {
|
||||
Twitter Twitter `yaml:"twitter,omitempty"`
|
||||
Reddit Reddit `yaml:"reddit,omitempty"`
|
||||
Slack Slack `yaml:"slack,omitempty"`
|
||||
Discord Discord `yaml:"discord,omitempty"`
|
||||
Teams Teams `yaml:"teams,omitempty"`
|
||||
SMTP SMTP `yaml:"smtp,omitempty"`
|
||||
Twitter Twitter `yaml:"twitter,omitempty"`
|
||||
Reddit Reddit `yaml:"reddit,omitempty"`
|
||||
Slack Slack `yaml:"slack,omitempty"`
|
||||
Discord Discord `yaml:"discord,omitempty"`
|
||||
Teams Teams `yaml:"teams,omitempty"`
|
||||
SMTP SMTP `yaml:"smtp,omitempty"`
|
||||
Mattermost Mattermost `yaml:"mattermost,omitempty"`
|
||||
}
|
||||
|
||||
type Twitter struct {
|
||||
@ -754,6 +755,17 @@ type Teams struct {
|
||||
IconURL string `yaml:"icon_url,omitempty"`
|
||||
}
|
||||
|
||||
type Mattermost struct {
|
||||
Enabled bool `yaml:"enabled,omitempty"`
|
||||
MessageTemplate string `yaml:"message_template,omitempty"`
|
||||
TitleTemplate string `yaml:"title_template,omitempty"`
|
||||
Color string `yaml:"color,omitempty"`
|
||||
Channel string `yaml:"channel,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
IconEmoji string `yaml:"icon_emoji,omitempty"`
|
||||
IconURL string `yaml:"icon_url,omitempty"`
|
||||
}
|
||||
|
||||
type SMTP struct {
|
||||
Enabled bool `yaml:"enabled,omitempty"`
|
||||
Host string `yaml:"host,omitempty"`
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/gofish"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/gomod"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/mattermost"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/milestone"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/nfpm"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/project"
|
||||
@ -69,5 +70,6 @@ var Defaulters = []Defaulter{
|
||||
teams.Pipe{},
|
||||
twitter.Pipe{},
|
||||
smtp.Pipe{},
|
||||
mattermost.Pipe{},
|
||||
milestone.Pipe{},
|
||||
}
|
||||
|
44
www/docs/customization/announce/mattermost.md
Normal file
44
www/docs/customization/announce/mattermost.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Mattermost
|
||||
|
||||
For it to work, you'll need to [create a new Incoming Webhook](https://docs.mattermost.com/developer/webhooks-incoming.html) in your own Mattermost deployment, and set some
|
||||
environment variables on your pipeline:
|
||||
|
||||
- `MATTERMOST_WEBHOOK`
|
||||
|
||||
Then, you can add something like the following to your `.goreleaser.yml` config:
|
||||
|
||||
```yaml
|
||||
# .goreleaser.yml
|
||||
announce:
|
||||
mattermost:
|
||||
# Whether its enabled or not.
|
||||
# Defaults to false.
|
||||
enabled: true
|
||||
|
||||
# Title template to use while publishing.
|
||||
# Defaults to `{{ .ProjectName }} {{ .Tag }} is out!`
|
||||
title_template: 'GoReleaser {{ .Tag }} was just released!'
|
||||
|
||||
# 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!'
|
||||
|
||||
# Color code of the message. You have to use hexadecimal.
|
||||
# Defaults to `#2D313E` - the grey-ish from goreleaser
|
||||
color: ''
|
||||
|
||||
# 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/).
|
Loading…
x
Reference in New Issue
Block a user