mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-07-28 23:08:26 +00:00
feat: Slack notifications matched on base branch name (#3644)
* Add ability to send slack notification matched on base branch name * Update BranchRegex usage doc * Add test with invalid branch and workspace regex
This commit is contained in:
@@ -41,6 +41,7 @@ In your Atlantis configuration you can now add the following:
|
||||
webhooks:
|
||||
- event: apply
|
||||
workspace-regex: .*
|
||||
branch-regex: .*
|
||||
kind: slack
|
||||
channel: my-channel
|
||||
```
|
||||
@@ -56,6 +57,7 @@ config: |
|
||||
webhooks:
|
||||
- event: apply
|
||||
workspace-regex: .*
|
||||
branch-regex: .*
|
||||
kind: slack
|
||||
channel: my-channel
|
||||
```
|
||||
|
||||
@@ -25,24 +25,26 @@ import (
|
||||
type SlackWebhook struct {
|
||||
Client SlackClient
|
||||
WorkspaceRegex *regexp.Regexp
|
||||
BranchRegex *regexp.Regexp
|
||||
Channel string
|
||||
}
|
||||
|
||||
func NewSlack(r *regexp.Regexp, channel string, client SlackClient) (*SlackWebhook, error) {
|
||||
func NewSlack(wr *regexp.Regexp, br *regexp.Regexp, channel string, client SlackClient) (*SlackWebhook, error) {
|
||||
if err := client.AuthTest(); err != nil {
|
||||
return nil, fmt.Errorf("testing slack authentication: %s. Verify your slack-token is valid", err)
|
||||
}
|
||||
|
||||
return &SlackWebhook{
|
||||
Client: client,
|
||||
WorkspaceRegex: r,
|
||||
WorkspaceRegex: wr,
|
||||
BranchRegex: br,
|
||||
Channel: channel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Send sends the webhook to Slack if the workspace matches the regex.
|
||||
// Send sends the webhook to Slack if workspace and branch matches their respective regex.
|
||||
func (s *SlackWebhook) Send(log logging.SimpleLogging, applyResult ApplyResult) error {
|
||||
if !s.WorkspaceRegex.MatchString(applyResult.Workspace) {
|
||||
if !s.WorkspaceRegex.MatchString(applyResult.Workspace) || !s.BranchRegex.MatchString(applyResult.Pull.BaseBranch) {
|
||||
return nil
|
||||
}
|
||||
return s.Client.PostMessage(s.Channel, applyResult)
|
||||
|
||||
@@ -102,6 +102,11 @@ func (d *DefaultSlackClient) createAttachments(applyResult ApplyResult) []slack.
|
||||
Value: applyResult.Workspace,
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "Branch",
|
||||
Value: applyResult.Pull.BaseBranch,
|
||||
Short: true,
|
||||
},
|
||||
{
|
||||
Title: "User",
|
||||
Value: applyResult.User.Username,
|
||||
|
||||
@@ -163,8 +163,9 @@ func setup(t *testing.T) {
|
||||
FullName: "runatlantis/atlantis",
|
||||
},
|
||||
Pull: models.PullRequest{
|
||||
Num: 1,
|
||||
URL: "url",
|
||||
Num: 1,
|
||||
URL: "url",
|
||||
BaseBranch: "main",
|
||||
},
|
||||
User: models.User{
|
||||
Username: "lkysow",
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/petergtz/pegomock/v4"
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"github.com/runatlantis/atlantis/server/events/webhooks"
|
||||
"github.com/runatlantis/atlantis/server/events/webhooks/mocks"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
@@ -35,10 +36,14 @@ func TestSend_PostMessage(t *testing.T) {
|
||||
hook := webhooks.SlackWebhook{
|
||||
Client: client,
|
||||
WorkspaceRegex: regex,
|
||||
BranchRegex: regex,
|
||||
Channel: channel,
|
||||
}
|
||||
result := webhooks.ApplyResult{
|
||||
Workspace: "production",
|
||||
Pull: models.PullRequest{
|
||||
BaseBranch: "main",
|
||||
},
|
||||
}
|
||||
|
||||
t.Log("PostMessage should be called, doesn't matter if it errors or not")
|
||||
@@ -57,10 +62,14 @@ func TestSend_NoopSuccess(t *testing.T) {
|
||||
hook := webhooks.SlackWebhook{
|
||||
Client: client,
|
||||
WorkspaceRegex: regex,
|
||||
BranchRegex: regex,
|
||||
Channel: channel,
|
||||
}
|
||||
result := webhooks.ApplyResult{
|
||||
Workspace: "production",
|
||||
Pull: models.PullRequest{
|
||||
BaseBranch: "main",
|
||||
},
|
||||
}
|
||||
err = hook.Send(logging.NewNoopLogger(t), result)
|
||||
Ok(t, err)
|
||||
|
||||
@@ -52,6 +52,7 @@ type MultiWebhookSender struct {
|
||||
type Config struct {
|
||||
Event string
|
||||
WorkspaceRegex string
|
||||
BranchRegex string
|
||||
Kind string
|
||||
Channel string
|
||||
}
|
||||
@@ -59,7 +60,11 @@ type Config struct {
|
||||
func NewMultiWebhookSender(configs []Config, client SlackClient) (*MultiWebhookSender, error) {
|
||||
var webhooks []Sender
|
||||
for _, c := range configs {
|
||||
r, err := regexp.Compile(c.WorkspaceRegex)
|
||||
wr, err := regexp.Compile(c.WorkspaceRegex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
br, err := regexp.Compile(c.BranchRegex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,7 +82,7 @@ func NewMultiWebhookSender(configs []Config, client SlackClient) (*MultiWebhookS
|
||||
if c.Channel == "" {
|
||||
return nil, errors.New("must specify \"channel\" if using a webhook of \"kind: slack\"")
|
||||
}
|
||||
slack, err := NewSlack(r, c.Channel, client)
|
||||
slack, err := NewSlack(wr, br, c.Channel, client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ const (
|
||||
var validConfig = webhooks.Config{
|
||||
Event: validEvent,
|
||||
WorkspaceRegex: validRegex,
|
||||
BranchRegex: validRegex,
|
||||
Kind: validKind,
|
||||
Channel: validChannel,
|
||||
}
|
||||
@@ -42,8 +43,8 @@ func validConfigs() []webhooks.Config {
|
||||
return []webhooks.Config{validConfig}
|
||||
}
|
||||
|
||||
func TestNewWebhooksManager_InvalidRegex(t *testing.T) {
|
||||
t.Log("When given an invalid regex in a config, an error is returned")
|
||||
func TestNewWebhooksManager_InvalidWorkspaceRegex(t *testing.T) {
|
||||
t.Log("When given an invalid workspace regex in a config, an error is returned")
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockSlackClient()
|
||||
|
||||
@@ -55,6 +56,33 @@ func TestNewWebhooksManager_InvalidRegex(t *testing.T) {
|
||||
Assert(t, strings.Contains(err.Error(), "error parsing regexp"), "expected regex error")
|
||||
}
|
||||
|
||||
func TestNewWebhooksManager_InvalidBranchRegex(t *testing.T) {
|
||||
t.Log("When given an invalid branch regex in a config, an error is returned")
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockSlackClient()
|
||||
|
||||
invalidRegex := "("
|
||||
configs := validConfigs()
|
||||
configs[0].BranchRegex = invalidRegex
|
||||
_, err := webhooks.NewMultiWebhookSender(configs, client)
|
||||
Assert(t, err != nil, "expected error")
|
||||
Assert(t, strings.Contains(err.Error(), "error parsing regexp"), "expected regex error")
|
||||
}
|
||||
|
||||
func TestNewWebhooksManager_InvalidBranchAndWorkspaceRegex(t *testing.T) {
|
||||
t.Log("When given an invalid branch and invalid workspace regex in a config, an error is returned")
|
||||
RegisterMockTestingT(t)
|
||||
client := mocks.NewMockSlackClient()
|
||||
|
||||
invalidRegex := "("
|
||||
configs := validConfigs()
|
||||
configs[0].WorkspaceRegex = invalidRegex
|
||||
configs[0].BranchRegex = invalidRegex
|
||||
_, err := webhooks.NewMultiWebhookSender(configs, client)
|
||||
Assert(t, err != nil, "expected error")
|
||||
Assert(t, strings.Contains(err.Error(), "error parsing regexp"), "expected regex error")
|
||||
}
|
||||
|
||||
func TestNewWebhooksManager_NoEvent(t *testing.T) {
|
||||
t.Log("When the event key is not specified in a config, an error is returned")
|
||||
RegisterMockTestingT(t)
|
||||
|
||||
@@ -141,6 +141,10 @@ type WebhookConfig struct {
|
||||
// that is being modified for this event. If the regex matches, we'll
|
||||
// send the webhook, ex. "production.*".
|
||||
WorkspaceRegex string `mapstructure:"workspace-regex"`
|
||||
// BranchRegex is a regex that is used to match against the base branch
|
||||
// that is being modified for this event. If the regex matches, we'll
|
||||
// send the webhook, ex. "main.*".
|
||||
BranchRegex string `mapstructure:"branch-regex"`
|
||||
// Kind is the type of webhook we should send, ex. slack.
|
||||
Kind string `mapstructure:"kind"`
|
||||
// Channel is the channel to send this webhook to. It only applies to
|
||||
@@ -344,6 +348,7 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
for _, c := range userConfig.Webhooks {
|
||||
config := webhooks.Config{
|
||||
Channel: c.Channel,
|
||||
BranchRegex: c.BranchRegex,
|
||||
Event: c.Event,
|
||||
Kind: c.Kind,
|
||||
WorkspaceRegex: c.WorkspaceRegex,
|
||||
|
||||
Reference in New Issue
Block a user