slack/main.go (100 lines of code) (raw):

// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "bytes" "context" "fmt" "text/template" "strings" cbpb "cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb" "github.com/GoogleCloudPlatform/cloud-build-notifiers/lib/notifiers" log "github.com/golang/glog" "github.com/slack-go/slack" ) const ( webhookURLSecretName = "webhookUrl" ) func main() { if err := notifiers.Main(new(slackNotifier)); err != nil { log.Fatalf("fatal error: %v", err) } } type slackNotifier struct { filter notifiers.EventFilter tmpl *template.Template webhookURL string br notifiers.BindingResolver tmplView *notifiers.TemplateView } func (s *slackNotifier) SetUp(ctx context.Context, cfg *notifiers.Config, blockKitTemplate string, sg notifiers.SecretGetter, br notifiers.BindingResolver) error { prd, err := notifiers.MakeCELPredicate(cfg.Spec.Notification.Filter) if err != nil { return fmt.Errorf("failed to make a CEL predicate: %w", err) } s.filter = prd wuRef, err := notifiers.GetSecretRef(cfg.Spec.Notification.Delivery, webhookURLSecretName) if err != nil { return fmt.Errorf("failed to get Secret ref from delivery config (%v) field %q: %w", cfg.Spec.Notification.Delivery, webhookURLSecretName, err) } wuResource, err := notifiers.FindSecretResourceName(cfg.Spec.Secrets, wuRef) if err != nil { return fmt.Errorf("failed to find Secret for ref %q: %w", wuRef, err) } wu, err := sg.GetSecret(ctx, wuResource) if err != nil { return fmt.Errorf("failed to get token secret: %w", err) } s.webhookURL = wu tmpl, err := template.New("blockkit_template").Funcs(template.FuncMap{ "replace": func(s, old, new string) string { return strings.ReplaceAll(s, old, new) }, }).Parse(blockKitTemplate) s.tmpl = tmpl s.br = br return nil } func (s *slackNotifier) SendNotification(ctx context.Context, build *cbpb.Build) error { if !s.filter.Apply(ctx, build) { return nil } log.Infof("sending Slack webhook for Build %q (status: %q)", build.Id, build.Status) bindings, err := s.br.Resolve(ctx, nil, build) if err != nil { return fmt.Errorf("failed to resolve bindings: %w", err) } s.tmplView = &notifiers.TemplateView{ Build: &notifiers.BuildView{Build: build}, Params: bindings, } msg, err := s.writeMessage() if err != nil { return fmt.Errorf("failed to write Slack message: %w", err) } return slack.PostWebhook(s.webhookURL, msg) } func (s *slackNotifier) writeMessage() (*slack.WebhookMessage, error) { build := s.tmplView.Build _, err := notifiers.AddUTMParams(build.LogUrl, notifiers.ChatMedium) if err != nil { return nil, fmt.Errorf("failed to add UTM params: %w", err) } var clr string switch build.Status { case cbpb.Build_SUCCESS: clr = "#22bb33" case cbpb.Build_FAILURE, cbpb.Build_INTERNAL_ERROR, cbpb.Build_TIMEOUT: clr = "#bb2124" default: clr = "#f0ad4e" } var buf bytes.Buffer if err := s.tmpl.Execute(&buf, s.tmplView); err != nil { return nil, err } var blocks slack.Blocks err = blocks.UnmarshalJSON(buf.Bytes()) if err != nil { return nil, fmt.Errorf("failed to unmarshal templating JSON: %w", err) } return &slack.WebhookMessage{Attachments: []slack.Attachment{{Color: clr, Blocks: blocks}}}, nil }