github.com/windmeup/goreleaser@v1.21.95/internal/pipe/slack/slack.go (about) 1 package slack 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/caarlos0/env/v9" 8 "github.com/caarlos0/log" 9 "github.com/slack-go/slack" 10 "github.com/windmeup/goreleaser/internal/tmpl" 11 "github.com/windmeup/goreleaser/pkg/context" 12 ) 13 14 const ( 15 defaultUsername = `GoReleaser` 16 defaultMessageTemplate = `{{ .ProjectName }} {{ .Tag }} is out! Check it out at {{ .ReleaseURL }}` 17 ) 18 19 type Pipe struct{} 20 21 func (Pipe) String() string { return "slack" } 22 func (Pipe) Skip(ctx *context.Context) bool { return !ctx.Config.Announce.Slack.Enabled } 23 24 type Config struct { 25 Webhook string `env:"SLACK_WEBHOOK,notEmpty"` 26 } 27 28 func (Pipe) Default(ctx *context.Context) error { 29 if ctx.Config.Announce.Slack.MessageTemplate == "" { 30 ctx.Config.Announce.Slack.MessageTemplate = defaultMessageTemplate 31 } 32 if ctx.Config.Announce.Slack.Username == "" { 33 ctx.Config.Announce.Slack.Username = defaultUsername 34 } 35 return nil 36 } 37 38 func (Pipe) Announce(ctx *context.Context) error { 39 msg, err := tmpl.New(ctx).Apply(ctx.Config.Announce.Slack.MessageTemplate) 40 if err != nil { 41 return fmt.Errorf("slack: %w", err) 42 } 43 44 var cfg Config 45 if err := env.Parse(&cfg); err != nil { 46 return fmt.Errorf("slack: %w", err) 47 } 48 49 log.Infof("posting: '%s'", msg) 50 51 // optional processing of advanced formatting options 52 blocks, attachments, err := parseAdvancedFormatting(ctx) 53 if err != nil { 54 return err 55 } 56 57 wm := &slack.WebhookMessage{ 58 Username: ctx.Config.Announce.Slack.Username, 59 IconEmoji: ctx.Config.Announce.Slack.IconEmoji, 60 IconURL: ctx.Config.Announce.Slack.IconURL, 61 Channel: ctx.Config.Announce.Slack.Channel, 62 Text: msg, 63 64 // optional enrichments 65 Blocks: blocks, 66 Attachments: attachments, 67 } 68 69 err = slack.PostWebhook(cfg.Webhook, wm) 70 if err != nil { 71 return fmt.Errorf("slack: %w", err) 72 } 73 74 return nil 75 } 76 77 func parseAdvancedFormatting(ctx *context.Context) (*slack.Blocks, []slack.Attachment, error) { 78 var blocks *slack.Blocks 79 if in := ctx.Config.Announce.Slack.Blocks; len(in) > 0 { 80 blocks = &slack.Blocks{BlockSet: make([]slack.Block, 0, len(in))} 81 82 if err := unmarshal(ctx, in, blocks); err != nil { 83 return nil, nil, fmt.Errorf("slack blocks: %w", err) 84 } 85 } 86 87 var attachments []slack.Attachment 88 if in := ctx.Config.Announce.Slack.Attachments; len(in) > 0 { 89 attachments = make([]slack.Attachment, 0, len(in)) 90 91 if err := unmarshal(ctx, in, &attachments); err != nil { 92 return nil, nil, fmt.Errorf("slack attachments: %w", err) 93 } 94 } 95 96 return blocks, attachments, nil 97 } 98 99 func unmarshal(ctx *context.Context, in interface{}, target interface{}) error { 100 jazon, err := json.Marshal(in) 101 if err != nil { 102 return fmt.Errorf("failed to marshal input as JSON: %w", err) 103 } 104 105 tplApplied, err := tmpl.New(ctx).Apply(string(jazon)) 106 if err != nil { 107 return fmt.Errorf("failed to evaluate template: %w", err) 108 } 109 110 if err = json.Unmarshal([]byte(tplApplied), target); err != nil { 111 return fmt.Errorf("failed to unmarshal into target: %w", err) 112 } 113 114 return nil 115 }