Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use vcs-specific emoji #118

Merged
merged 1 commit into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading