grafana/pkg/registry/apis/provisioning/webhooks/pullrequest/comment.go

128 lines
3.6 KiB
Go

package pullrequest
import (
"bytes"
"context"
"fmt"
"html/template"
"path/filepath"
"strings"
)
type commenter struct {
templateDashboard *template.Template
templateTable *template.Template
templateRenderInfo *template.Template
}
func NewCommenter() Commenter {
return &commenter{
templateDashboard: template.Must(template.New("dashboard").Parse(commentTemplateSingleDashboard)),
templateTable: template.Must(template.New("table").Parse(commentTemplateTable)),
templateRenderInfo: template.Must(template.New("setup").Parse(commentTemplateMissingImageRenderer)),
}
}
func (c *commenter) Comment(ctx context.Context, prRepo PullRequestRepo, pr int, info changeInfo) error {
comment, err := c.generateComment(ctx, info)
if err != nil {
return fmt.Errorf("unable to generate comment text: %w", err)
}
if err := prRepo.CommentPullRequest(ctx, pr, comment); err != nil {
return fmt.Errorf("comment pull request: %w", err)
}
return nil
}
func (c *commenter) generateComment(_ context.Context, info changeInfo) (string, error) {
// TODO: should we comment even if there are no changes?
if len(info.Changes) == 0 {
return "Grafana didn't find any changes in this pull request.", nil
}
var buf bytes.Buffer
if len(info.Changes) == 1 && info.Changes[0].Parsed.GVK.Kind == dashboardKind {
if err := c.templateDashboard.Execute(&buf, info.Changes[0]); err != nil {
return "", fmt.Errorf("unable to execute template: %w", err)
}
} else {
if err := c.templateTable.Execute(&buf, info); err != nil {
return "", fmt.Errorf("unable to execute template: %w", err)
}
}
if info.MissingImageRenderer {
if err := c.templateRenderInfo.Execute(&buf, info); err != nil {
return "", fmt.Errorf("unable to execute template: %w", err)
}
}
return strings.TrimSpace(buf.String()), nil
}
const commentTemplateSingleDashboard = `Hey there! 🎉
Grafana spotted some changes to your dashboard.
{{- if and .GrafanaScreenshotURL .PreviewScreenshotURL}}
### Side by Side Comparison of {{.Parsed.Info.Path}}
| Before | After |
|----------|---------|
| ![Before]({{.GrafanaScreenshotURL}}) | ![Preview]({{.PreviewScreenshotURL}}) |
{{- else if .GrafanaScreenshotURL}}
### Original of {{.Title}}
![Original]({{.GrafanaScreenshotURL}})
{{- else if .PreviewScreenshotURL}}
### Preview of {{.Parsed.Info.Path}}
![Preview]({{.PreviewScreenshotURL}})
{{ end}}
{{ if and .GrafanaURL .PreviewURL}}
See the [original]({{.GrafanaURL}}) and [preview]({{.PreviewURL}}) of {{.Parsed.Info.Path}}.
{{- else if .GrafanaURL}}
See the [original]({{.GrafanaURL}}) of {{.Title}}.
{{- else if .PreviewURL}}
See the [preview]({{.PreviewURL}}) of {{.Parsed.Info.Path}}.
{{- end}}
`
const commentTemplateTable = `Hey there! 🎉
Grafana spotted some changes.
| Action | Kind | Resource | Preview |
|--------|------|----------|---------|
{{- range .Changes}}
| {{.Parsed.Action}} | {{.Kind}} | {{.ExistingLink}} | {{ if .PreviewURL}}[preview]({{.PreviewURL}}){{ end }} |
{{- end}}
{{ if .SkippedFiles }}
and {{ .SkippedFiles }} more files.
{{ end}}
`
// TODO: this should expand and show links to setup docs
const commentTemplateMissingImageRenderer = `
NOTE: The image renderer is not configured
`
// TODO: does this have some value?
func (f *fileChangeInfo) Kind() string {
if f.Parsed == nil {
return filepath.Ext(f.Change.Path)
}
v := f.Parsed.GVK.Kind
if v == "" {
return filepath.Ext(f.Parsed.Info.Path)
}
return f.Parsed.GVK.Kind
}
// TODO: does this have some value?
func (f *fileChangeInfo) ExistingLink() string {
if f.GrafanaURL != "" {
return fmt.Sprintf("[%s](%s)", f.Title, f.GrafanaURL)
}
return f.Title
}