mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-04 03:11:55 +02:00
905a1640f1
* feat(announce): added Slack notification options This feature adds support for specifying a richer content in Slack announcements. We may now specify "blocks" and "attachments" to produce better-looking announcement messages. * fixes #2986 The goreleaser configuration only exposes the top-level structures and does not check the validity of the Slack API internal structures. This way, we do not inject hard dependencies on changes in the Slack API. Notice: untyped config parsing introduces a little hack to have yaml and JSON marshaling work together properly. This hack won't be necessary with yaml.v3. How this has been tested? ------------------------- * Added unit tests for the config parsing * Added a (skipped) e2e test. For now, this requires a valid Slack webhook, so I've been able to test this manually. Signed-off-by: Frederic BIDON <fredbi@yahoo.com> * added more unit tests Signed-off-by: Frederic BIDON <fredbi@yahoo.com> * removed yaml.v2 hack Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
247 lines
5.2 KiB
Go
247 lines
5.2 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestUnmarshalSlackBlocks(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("valid blocks", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
prop, err := LoadReader(goodBlocksSlackConf())
|
|
require.NoError(t, err)
|
|
|
|
expectedBlocks := []SlackBlock{
|
|
{
|
|
Internal: map[string]interface{}{
|
|
"type": "header",
|
|
"text": map[string]interface{}{
|
|
"type": "plain_text",
|
|
"text": "{{ .Version }}",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Internal: map[string]interface{}{
|
|
"text": map[string]interface{}{
|
|
"type": "mrkdwn",
|
|
"text": "Heading\n=======\n\n**Bold**\n",
|
|
},
|
|
"type": "section",
|
|
},
|
|
},
|
|
}
|
|
// assert Unmarshal from YAML
|
|
require.Equal(t, expectedBlocks, prop.Announce.Slack.Blocks)
|
|
|
|
jazon, err := json.Marshal(prop.Announce.Slack.Blocks)
|
|
require.NoError(t, err)
|
|
|
|
var untyped []SlackBlock
|
|
require.NoError(t, json.Unmarshal(jazon, &untyped))
|
|
|
|
// assert that JSON Marshal didn't alter the struct
|
|
require.Equal(t, expectedBlocks, prop.Announce.Slack.Blocks)
|
|
})
|
|
|
|
t.Run("invalid blocks", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := LoadReader(badBlocksSlackConf())
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestUnmarshalSlackAttachments(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
t.Run("valid attachments", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
prop, err := LoadReader(goodAttachmentsSlackConf())
|
|
require.NoError(t, err)
|
|
|
|
expectedAttachments := []SlackAttachment{
|
|
{
|
|
Internal: map[string]interface{}{
|
|
"color": "#46a64f",
|
|
"fields": []interface{}{
|
|
map[string]interface{}{
|
|
"short": false,
|
|
"title": "field 1",
|
|
"value": "value 1",
|
|
},
|
|
},
|
|
"footer": "a footer",
|
|
"mrkdwn_in": []interface{}{
|
|
"text",
|
|
},
|
|
"pretext": "optional",
|
|
"text": "another",
|
|
"title": "my_title",
|
|
},
|
|
},
|
|
}
|
|
// assert Unmarshal from YAML
|
|
require.Equal(t, expectedAttachments, prop.Announce.Slack.Attachments)
|
|
|
|
jazon, err := json.Marshal(prop.Announce.Slack.Attachments)
|
|
require.NoError(t, err)
|
|
|
|
var untyped []SlackAttachment
|
|
require.NoError(t, json.Unmarshal(jazon, &untyped))
|
|
|
|
// assert that JSON Marshal didn't alter the struct
|
|
require.Equal(t, expectedAttachments, prop.Announce.Slack.Attachments)
|
|
})
|
|
|
|
t.Run("invalid attachments", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
_, err := LoadReader(badAttachmentsSlackConf())
|
|
require.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestUnmarshalYAMLSlackBlocks(t *testing.T) {
|
|
// func (a *SlackAttachment) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
t.Parallel()
|
|
|
|
const testError = "testError"
|
|
erf := func(_ interface{}) error {
|
|
return errors.New(testError)
|
|
}
|
|
|
|
t.Run("SlackBlock.UnmarshalYAML error case", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var block SlackBlock
|
|
err := block.UnmarshalYAML(erf)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, testError)
|
|
})
|
|
|
|
t.Run("SlackAttachment.UnmarshalYAML error case", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var attachment SlackAttachment
|
|
err := attachment.UnmarshalYAML(erf)
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, testError)
|
|
})
|
|
}
|
|
|
|
func goodBlocksSlackConf() io.Reader {
|
|
const conf = `
|
|
announce:
|
|
slack:
|
|
enabled: true
|
|
username: my_user
|
|
message_template: fallback
|
|
channel: my_channel
|
|
blocks:
|
|
- type: header
|
|
text:
|
|
type: plain_text
|
|
text: '{{ .Version }}'
|
|
- type: section
|
|
text:
|
|
type: mrkdwn
|
|
text: |
|
|
Heading
|
|
=======
|
|
|
|
**Bold**
|
|
`
|
|
|
|
buf := bytes.NewBufferString(conf)
|
|
|
|
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
|
|
}
|
|
|
|
func badBlocksSlackConf() io.Reader {
|
|
const conf = `
|
|
announce:
|
|
slack:
|
|
enabled: true
|
|
username: my_user
|
|
message_template: fallback
|
|
channel: my_channel
|
|
blocks:
|
|
type: header
|
|
text:
|
|
type: plain_text
|
|
text: '{{ .Version }}'
|
|
type: section
|
|
text:
|
|
type: mrkdwn
|
|
text: |
|
|
**Bold**
|
|
`
|
|
|
|
buf := bytes.NewBufferString(conf)
|
|
|
|
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
|
|
}
|
|
|
|
func goodAttachmentsSlackConf() io.Reader {
|
|
const conf = `
|
|
announce:
|
|
slack:
|
|
enabled: true
|
|
username: my_user
|
|
message_template: fallback
|
|
channel: my_channel
|
|
attachments:
|
|
- mrkdwn_in: ["text"]
|
|
color: '#46a64f'
|
|
pretext: optional
|
|
title: my_title
|
|
text: another
|
|
fields:
|
|
- title: field 1
|
|
value: value 1
|
|
short: false
|
|
footer: a footer
|
|
`
|
|
|
|
buf := bytes.NewBufferString(conf)
|
|
|
|
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
|
|
}
|
|
|
|
func badAttachmentsSlackConf() io.Reader {
|
|
const conf = `
|
|
announce:
|
|
slack:
|
|
enabled: true
|
|
username: my_user
|
|
message_template: fallback
|
|
channel: my_channel
|
|
attachments:
|
|
key:
|
|
mrkdwn_in: ["text"]
|
|
color: #46a64f
|
|
pretext: optional
|
|
title: my_title
|
|
text: another
|
|
fields:
|
|
- title: field 1
|
|
value: value 1
|
|
short: false
|
|
footer: a footer
|
|
`
|
|
|
|
buf := bytes.NewBufferString(conf)
|
|
|
|
return bytes.NewReader(bytes.ReplaceAll(buf.Bytes(), []byte("\t"), []byte(" ")))
|
|
}
|