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  }