Skip to content

Commit

Permalink
use vcs-specific emoji (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
djeebus authored Jan 22, 2024
1 parent cb4a644 commit af23f47
Show file tree
Hide file tree
Showing 12 changed files with 94 additions and 61 deletions.
36 changes: 0 additions & 36 deletions pkg/commitState.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package pkg

import "fmt"

// CommitState is an enum for represnting the state of a commit for posting via CommitStatus
type CommitState uint8

Expand All @@ -16,15 +14,6 @@ const (
StatePanic
)

// Emoji returns a string representation of this state for use in the request
func (s CommitState) Emoji() string {
if emoji, ok := stateEmoji[s]; ok {
return emoji
} else {
return defaultEmoji
}
}

func (s CommitState) BareString() string {
text, ok := stateString[s]
if !ok {
Expand All @@ -33,31 +22,6 @@ func (s CommitState) BareString() string {
return text
}

func (s CommitState) String() string {
text, ok := stateString[s]
if !ok {
text = defaultString
}

if text == "" {
return ""
}

return fmt.Sprintf("%s %s", text, s.Emoji())
}

var stateEmoji = map[CommitState]string{
StateNone: "",
StateSuccess: ":white_check_mark:",
StateRunning: ":running:",
StateWarning: ":warning:",
StateFailure: ":red_circle:",
StateError: ":heavy_exclamation_mark:",
StatePanic: ":skull:",
}

const defaultEmoji = ":interrobang:"

var stateString = map[CommitState]string{
StateNone: "",
StateSuccess: "Passed",
Expand Down
16 changes: 10 additions & 6 deletions pkg/conftest/conftest.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@ const passedMessage = "\nPassed all policy checks."

var reposCache = local.NewReposDirectory()

type emojiable interface {
ToEmoji(state pkg.CommitState) string
}

// Conftest runs the conftest validation against an application in a given repository
// path. It generates a summary string with the results, which can later be posted
// as a GitLab comment. The validation checks resources against Zapier policies and
// provides feedback for warnings or errors as informational messages. Failure to
// pass a policy check currently does not block deploy.
func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) (pkg.CheckResult, error) {
func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string, vcs emojiable) (pkg.CheckResult, error) {
_, span := otel.Tracer("Kubechecks").Start(ctx, "Conftest")
defer span.End()

Expand Down Expand Up @@ -76,7 +80,7 @@ func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) (
}

var b bytes.Buffer
formatConftestResults(&b, results)
formatConftestResults(&b, results, vcs)
innerStrings = append(innerStrings, fmt.Sprintf("\n\n**%s**\n", app.Spec.Source.Path))
s := b.String()
s = strings.ReplaceAll(s, fmt.Sprintf("%s/", repoPath), "")
Expand Down Expand Up @@ -114,7 +118,7 @@ func Conftest(ctx context.Context, app *v1alpha1.Application, repoPath string) (
// formatConftestResults writes the check results from an array of output.CheckResult objects into a formatted table.
// The table omits success messages to reduce noise and includes file, message, and status (failed, warning, skipped).
// The formatted table is then rendered and written to the given io.Writer 'w'.
func formatConftestResults(w io.Writer, checkResults []output.CheckResult) {
func formatConftestResults(w io.Writer, checkResults []output.CheckResult, vcs emojiable) {
table := tablewriter.NewWriter(w)
table.SetHeader([]string{" ", "file", "message"})
table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false})
Expand All @@ -129,19 +133,19 @@ func formatConftestResults(w io.Writer, checkResults []output.CheckResult) {
}

for _, result := range checkResult.Exceptions {
tableData = append(tableData, []string{pkg.StateError.Emoji(), code(checkResult.FileName), result.Message})
tableData = append(tableData, []string{vcs.ToEmoji(pkg.StateError), code(checkResult.FileName), result.Message})
}

for _, result := range checkResult.Warnings {
tableData = append(tableData, []string{pkg.StateWarning.Emoji(), code(checkResult.FileName), result.Message})
tableData = append(tableData, []string{vcs.ToEmoji(pkg.StateWarning), code(checkResult.FileName), result.Message})
}

for _, result := range checkResult.Skipped {
tableData = append(tableData, []string{" :arrow_right: ", code(checkResult.FileName), result.Message})
}

for _, result := range checkResult.Failures {
tableData = append(tableData, []string{pkg.StateFailure.Emoji(), code(checkResult.FileName), result.Message})
tableData = append(tableData, []string{vcs.ToEmoji(pkg.StateFailure), code(checkResult.FileName), result.Message})
}
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/events/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ func (ce *CheckEvent) createRunner(span trace.Span, grpCtx context.Context, app

ce.vcsNote.AddToAppMessage(grpCtx, app, cr)

ce.logger.Info().Str("app", app).Str("check", desc).Str("result", cr.State.String()).Msgf("check done")
ce.logger.Info().Str("app", app).Str("check", desc).Str("result", cr.State.BareString()).Msgf("check done")
}()
}
}
Expand All @@ -419,7 +419,7 @@ func (ce *CheckEvent) validatePolicy(ctx context.Context, app string) func() (pk
return pkg.CheckResult{}, errors.Wrapf(err, "could not retrieve ArgoCD App data: %q", app)
}

cr, err := conftest.Conftest(ctx, argoApp, ce.TempWorkingDir)
cr, err := conftest.Conftest(ctx, argoApp, ce.TempWorkingDir, ce.client)
if err != nil {
return pkg.CheckResult{}, err
}
Expand Down
16 changes: 11 additions & 5 deletions pkg/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ func (ar *AppResults) AddCheckResult(result CheckResult) {
ar.results = append(ar.results, result)
}

func NewMessage(name string, prId, commentId int) *Message {
func NewMessage(name string, prId, commentId int, vcs toEmoji) *Message {
return &Message{
Name: name,
CheckID: prId,
NoteID: commentId,
apps: make(map[string]*AppResults),
vcs: vcs,
}
}

type toEmoji interface {
ToEmoji(state CommitState) string
}

// Message type that allows concurrent updates
// Has a reference to the owner/repo (ie zapier/kubechecks),
// the PR/MR id, and the actual messsage
Expand All @@ -49,6 +54,7 @@ type Message struct {
apps map[string]*AppResults
footer string
lock sync.Mutex
vcs toEmoji
}

func (m *Message) WorstState() CommitState {
Expand Down Expand Up @@ -92,7 +98,7 @@ func (m *Message) SetFooter(start time.Time, commitSha string) {
}

func (m *Message) BuildComment(ctx context.Context) string {
return buildComment(ctx, m.apps)
return m.buildComment(ctx, m.apps)
}

func buildFooter(start time.Time, commitSHA string) string {
Expand All @@ -112,7 +118,7 @@ func buildFooter(start time.Time, commitSHA string) string {
}

// Iterate the map of all apps in this message, building a final comment from their current state
func buildComment(ctx context.Context, apps map[string]*AppResults) string {
func (m *Message) buildComment(ctx context.Context, apps map[string]*AppResults) string {
_, span := otel.Tracer("Kubechecks").Start(ctx, "buildComment")
defer span.End()

Expand All @@ -131,7 +137,7 @@ func buildComment(ctx context.Context, apps map[string]*AppResults) string {
if check.State == StateNone {
summary = check.Summary
} else {
summary = fmt.Sprintf("%s %s", check.Summary, check.State.String())
summary = fmt.Sprintf("%s %s %s", check.Summary, check.State.BareString(), m.vcs.ToEmoji(check.State))
}

msg := fmt.Sprintf("<details>\n<summary>%s</summary>\n\n%s\n</details>", summary, check.Details)
Expand All @@ -141,7 +147,7 @@ func buildComment(ctx context.Context, apps map[string]*AppResults) string {

sb.WriteString("<details>\n")
sb.WriteString("<summary>\n\n")
sb.WriteString(fmt.Sprintf("## ArgoCD Application Checks: `%s` %s\n", appName, appState.Emoji()))
sb.WriteString(fmt.Sprintf("## ArgoCD Application Checks: `%s` %s\n", appName, m.vcs.ToEmoji(appState)))
sb.WriteString("</summary>\n\n")
sb.WriteString(strings.Join(checkStrings, "\n\n---\n\n"))
sb.WriteString("</details>")
Expand Down
19 changes: 13 additions & 6 deletions pkg/message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import (
"github.com/stretchr/testify/assert"
)

type fakeEmojiable struct {
emoji string
}

func (fe fakeEmojiable) ToEmoji(state CommitState) string { return fe.emoji }

func TestBuildComment(t *testing.T) {
appResults := map[string]*AppResults{
"myapp": {
Expand All @@ -20,16 +26,17 @@ func TestBuildComment(t *testing.T) {
},
},
}
comment := buildComment(context.TODO(), appResults)
m := NewMessage("message", 1, 2, fakeEmojiable{":test:"})
comment := m.buildComment(context.TODO(), appResults)
assert.Equal(t, `# Kubechecks Report
<details>
<summary>
## ArgoCD Application Checks: `+"`myapp`"+` :heavy_exclamation_mark:
## ArgoCD Application Checks: `+"`myapp`"+` :test:
</summary>
<details>
<summary>this failed bigly Error :heavy_exclamation_mark:</summary>
<summary>this failed bigly Error :test:</summary>
should add some important details here
</details></details>`, comment)
Expand All @@ -38,7 +45,7 @@ should add some important details here
func TestMessageIsSuccess(t *testing.T) {
t.Run("logic works", func(t *testing.T) {
var (
message = NewMessage("name", 1, 2)
message = NewMessage("name", 1, 2, fakeEmojiable{":test:"})
ctx = context.TODO()
)

Expand Down Expand Up @@ -84,7 +91,7 @@ func TestMessageIsSuccess(t *testing.T) {
for state := range testcases {
t.Run(state.BareString(), func(t *testing.T) {
var (
message = NewMessage("name", 1, 2)
message = NewMessage("name", 1, 2, fakeEmojiable{":test:"})
ctx = context.TODO()
)
message.AddNewApp(ctx, "some-app")
Expand All @@ -96,7 +103,7 @@ func TestMessageIsSuccess(t *testing.T) {

func TestMultipleItemsWithNewlines(t *testing.T) {
var (
message = NewMessage("name", 1, 2)
message = NewMessage("name", 1, 2, fakeEmojiable{":test:"})
ctx = context.Background()
)
message.AddNewApp(ctx, "first-app")
Expand Down
1 change: 1 addition & 0 deletions pkg/vcs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ type Client interface {

Username() string
Email() string
ToEmoji(pkg.CommitState) string
}
4 changes: 2 additions & 2 deletions pkg/vcs/github_client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,12 @@ func toGithubCommitStatus(state pkg.CommitState) *string {
return pkg.Pointer("success")
}

log.Warn().Str("state", state.String()).Msg("failed to convert to a github commit status")
log.Warn().Str("state", state.BareString()).Msg("failed to convert to a github commit status")
return pkg.Pointer("failure")
}

func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, status pkg.CommitState) error {
log.Info().Str("repo", repo.Name).Str("sha", repo.SHA).Str("status", status.String()).Msg("setting Github commit status")
log.Info().Str("repo", repo.Name).Str("sha", repo.SHA).Str("status", status.BareString()).Msg("setting Github commit status")
repoStatus, _, err := c.Repositories.CreateStatus(ctx, repo.Owner, repo.Name, repo.SHA, &github.RepoStatus{
State: toGithubCommitStatus(status),
Description: pkg.Pointer(status.BareString()),
Expand Down
24 changes: 24 additions & 0 deletions pkg/vcs/github_client/emoji.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package github_client

import "github.com/zapier/kubechecks/pkg"

var stateEmoji = map[pkg.CommitState]string{
pkg.StateNone: "",
pkg.StateSuccess: ":white_check_mark:",
pkg.StateRunning: ":runner:",
pkg.StateWarning: ":warning:",
pkg.StateFailure: ":red_circle:",
pkg.StateError: ":exclamation:",
pkg.StatePanic: ":skull:",
}

const defaultEmoji = ":interrobang:"

// ToEmoji returns a string representation of this state for use in the request
func (c *Client) ToEmoji(s pkg.CommitState) string {
if emoji, ok := stateEmoji[s]; ok {
return emoji
} else {
return defaultEmoji
}
}
2 changes: 1 addition & 1 deletion pkg/vcs/github_client/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, prID int, msg
log.Error().Err(err).Msg("could not post message to PR")
}

return pkg.NewMessage(repo.FullName, prID, int(*comment.ID))
return pkg.NewMessage(repo.FullName, prID, int(*comment.ID), c)
}

func (c *Client) UpdateMessage(ctx context.Context, m *pkg.Message, msg string) error {
Expand Down
24 changes: 24 additions & 0 deletions pkg/vcs/gitlab_client/emoji.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package gitlab_client

import "github.com/zapier/kubechecks/pkg"

var stateEmoji = map[pkg.CommitState]string{
pkg.StateNone: "",
pkg.StateSuccess: ":white_check_mark:",
pkg.StateRunning: ":runner:",
pkg.StateWarning: ":warning:",
pkg.StateFailure: ":red_circle:",
pkg.StateError: ":exclamation:",
pkg.StatePanic: ":skull:",
}

const defaultEmoji = ":interrobang:"

// ToEmoji returns a string representation of this state for use in the request
func (c *Client) ToEmoji(s pkg.CommitState) string {
if emoji, ok := stateEmoji[s]; ok {
return emoji
} else {
return defaultEmoji
}
}
2 changes: 1 addition & 1 deletion pkg/vcs/gitlab_client/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (c *Client) PostMessage(ctx context.Context, repo *repo.Repo, mergeRequestI
log.Error().Err(err).Msg("could not post message to MR")
}

return pkg.NewMessage(repo.FullName, mergeRequestID, n.ID)
return pkg.NewMessage(repo.FullName, mergeRequestID, n.ID, c)
}

func (c *Client) hideOutdatedMessages(ctx context.Context, projectName string, mergeRequestID int, notes []*gitlab.Note) error {
Expand Down
7 changes: 5 additions & 2 deletions pkg/vcs/gitlab_client/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package gitlab_client
import (
"context"
"errors"
"fmt"
"strconv"
"time"

Expand All @@ -19,10 +20,12 @@ const GitlabCommitStatusContext = "kubechecks"
var errNoPipelineStatus = errors.New("nil pipeline status")

func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, state pkg.CommitState) error {
description := fmt.Sprintf("%s %s", state.BareString(), c.ToEmoji(state))

status := &gitlab.SetCommitStatusOptions{
Name: pkg.Pointer(GitlabCommitStatusContext),
Context: pkg.Pointer(GitlabCommitStatusContext),
Description: pkg.Pointer(state.String()),
Description: pkg.Pointer(description),
State: convertState(state),
}
// Get pipelineStatus so we can attach new status to existing pipeline. We
Expand All @@ -49,7 +52,7 @@ func (c *Client) CommitStatus(ctx context.Context, repo *repo.Repo, state pkg.Co
log.Debug().
Str("project", repo.FullName).
Str("commit_sha", repo.SHA).
Str("kubechecks_status", state.String()).
Str("kubechecks_status", description).
Str("gitlab_status", string(status.State)).
Msg("gitlab client: updating commit status")
_, err = c.setCommitStatus(repo.FullName, repo.SHA, status)
Expand Down

0 comments on commit af23f47

Please sign in to comment.