github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/slack/slack.go (about)

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