From 6a0957af60248b469c143c55bcd92468a1a2dbc1 Mon Sep 17 00:00:00 2001 From: Yash Talele Date: Sun, 20 Oct 2024 10:52:58 +0530 Subject: [PATCH 1/2] feat: Add Support for GitHub Check Runs Signed-off-by: Yash Talele --- pkg/services/github.go | 158 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/pkg/services/github.go b/pkg/services/github.go index e25af5f2..b004ed15 100644 --- a/pkg/services/github.go +++ b/pkg/services/github.go @@ -8,6 +8,7 @@ import ( "regexp" "strings" texttemplate "text/template" + "time" "unicode/utf8" "github.com/bradleyfalzon/ghinstallation/v2" @@ -39,6 +40,7 @@ type GitHubNotification struct { PullRequestComment *GitHubPullRequestComment `json:"pullRequestComment,omitempty"` RepoURLPath string `json:"repoURLPath,omitempty"` RevisionPath string `json:"revisionPath,omitempty"` + CheckRun *GitHubCheckRun `json:"checkRun,omitempty"` } type GitHubStatus struct { @@ -47,6 +49,23 @@ type GitHubStatus struct { TargetURL string `json:"targetURL,omitempty"` } +type GitHubCheckRun struct { + // head_sha - this will be the revision path + // external_id - this should have the details of argocd server + Name string `json:"name,omitempty"` + DetailsURL string `json:"details_url,omitempty"` + Status string `json:"status,omitempty"` + Conclusion string `json:"conclusion,omitempty"` + StartedAt string `json:"started_at,omitempty"` + CompletedAt string `json:"completed_at,omitempty"` + Output *GitHubCheckRunOutput `json:"output,omitempty"` +} +type GitHubCheckRunOutput struct { + Title string `json:"title,omitempty"` + Summary string `json:"summary,omitempty"` + Text string `json:"text,omitempty"` +} + type GitHubDeployment struct { State string `json:"state,omitempty"` Environment string `json:"environment,omitempty"` @@ -139,6 +158,49 @@ func (g *GitHubNotification) GetTemplater(name string, f texttemplate.FuncMap) ( } } + var checkRunName, detailsURL, status, conclusion, startedAt, completedAt *texttemplate.Template + if g.CheckRun != nil { + checkRunName, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Name) + if err != nil { + return nil, err + } + detailsURL, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.DetailsURL) + if err != nil { + return nil, err + } + status, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Status) + if err != nil { + return nil, err + } + conclusion, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Conclusion) + if err != nil { + return nil, err + } + startedAt, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.StartedAt) + if err != nil { + return nil, err + } + completedAt, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.CompletedAt) + if err != nil { + return nil, err + } + } + var checkRunTitle, summary, text *texttemplate.Template + if g.CheckRun != nil && g.CheckRun.Output != nil { + checkRunTitle, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Output.Title) + if err != nil { + return nil, err + } + summary, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Output.Summary) + if err != nil { + return nil, err + } + text, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Output.Text) + if err != nil { + return nil, err + } + } + return func(notification *Notification, vars map[string]interface{}) error { if notification.GitHub == nil { notification.GitHub = &GitHubNotification{ @@ -246,6 +308,63 @@ func (g *GitHubNotification) GetTemplater(name string, f texttemplate.FuncMap) ( notification.GitHub.PullRequestComment.Content = contentData.String() } + if g.CheckRun != nil { + if notification.GitHub.CheckRun == nil { + notification.GitHub.CheckRun = &GitHubCheckRun{} + } + var checkRunNameData bytes.Buffer + if err := checkRunName.Execute(&checkRunNameData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Name = checkRunNameData.String() + var detailsURLData bytes.Buffer + if err := detailsURL.Execute(&detailsURLData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.DetailsURL = detailsURLData.String() + + var statusData bytes.Buffer + if err := status.Execute(&statusData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Status = statusData.String() + var conclusionData bytes.Buffer + if err := conclusion.Execute(&conclusionData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Conclusion = conclusionData.String() + var startedAtData bytes.Buffer + if err := startedAt.Execute(&startedAtData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.StartedAt = startedAtData.String() + var completedAtData bytes.Buffer + if err := completedAt.Execute(&completedAtData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.CompletedAt = completedAtData.String() + } + if g.CheckRun != nil && g.CheckRun.Output != nil { + if notification.GitHub.CheckRun.Output == nil { + notification.GitHub.CheckRun.Output = &GitHubCheckRunOutput{} + } + var checkRunTitleData bytes.Buffer + if err := checkRunTitle.Execute(&checkRunTitleData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Output.Title = checkRunTitleData.String() + var summaryData bytes.Buffer + if err := summary.Execute(&summaryData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Output.Summary = summaryData.String() + var textData bytes.Buffer + if err := text.Execute(&textData, vars); err != nil { + return err + } + notification.GitHub.CheckRun.Output.Text = textData.String() + } + return nil }, nil } @@ -438,5 +557,44 @@ func (g gitHubService) Send(notification Notification, _ Destination) error { } } + if notification.GitHub.CheckRun != nil { + startedTime, err := time.Parse("YYYY-MM-DDTHH:MM:SSZ", notification.GitHub.CheckRun.StartedAt) + if err != nil { + return err + } + completedTime, err := time.Parse("YYYY-MM-DDTHH:MM:SSZ", notification.GitHub.CheckRun.CompletedAt) + if err != nil { + return err + } + externalID := "argocd-notifications" + checkRunOutput := &github.CheckRunOutput{} + if notification.GitHub.CheckRun.Output != nil { + checkRunOutput = &github.CheckRunOutput{ + Title: ¬ification.GitHub.CheckRun.Output.Title, + Text: ¬ification.GitHub.CheckRun.Output.Text, + Summary: ¬ification.GitHub.CheckRun.Output.Summary, + } + } + + _, _, err = g.client.Checks.CreateCheckRun( + context.Background(), + u[0], + u[1], + github.CreateCheckRunOptions{ + HeadSHA: notification.GitHub.revision, + ExternalID: &externalID, + Name: notification.GitHub.CheckRun.Name, + DetailsURL: ¬ification.GitHub.CheckRun.DetailsURL, + StartedAt: &github.Timestamp{Time: startedTime}, + CompletedAt: &github.Timestamp{Time: completedTime}, + Output: checkRunOutput, + }, + ) + + if err != nil { + return err + } + } + return nil } From 9a3a421d754b25dfed92dea39ecc5d10a755a8fd Mon Sep 17 00:00:00 2001 From: Yash Talele Date: Sun, 20 Oct 2024 10:54:15 +0530 Subject: [PATCH 2/2] Added test cases for github check run Signed-off-by: Yash Talele --- pkg/services/github_test.go | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pkg/services/github_test.go b/pkg/services/github_test.go index c9649974..ea0c2904 100644 --- a/pkg/services/github_test.go +++ b/pkg/services/github_test.go @@ -3,6 +3,7 @@ package services import ( "testing" "text/template" + "time" "github.com/stretchr/testify/assert" ) @@ -261,3 +262,55 @@ func TestGetTemplater_Github_PullRequestComment(t *testing.T) { assert.Equal(t, "0123456789", notification.GitHub.revision) assert.Equal(t, "This is a comment", notification.GitHub.PullRequestComment.Content) } + +func TestGitHubCheckRunNotification(t *testing.T) { + checkRun := &GitHubCheckRun{ + Name: "ArgoCD GitHub Check Run", + DetailsURL: "http://example.com/build/status", + Status: "completed", + Conclusion: "success", + StartedAt: time.Now().Format(time.RFC3339), + CompletedAt: time.Now().Add(5 * time.Minute).Format(time.RFC3339), + Output: &GitHubCheckRunOutput{ + Title: "Test Check Run", + Summary: "All tests passed.", + Text: "All unit tests and integration tests passed successfully.", + }, + } + + githubNotification := &GitHubNotification{ + CheckRun: checkRun, + } + + vars := map[string]interface{}{ + "app": map[string]interface{}{ + "spec": map[string]interface{}{ + "source": map[string]interface{}{ + "repoURL": "https://github.com/argoproj/argo-cd.git", + }, + }, + "status": map[string]interface{}{ + "operationState": map[string]interface{}{ + "syncResult": map[string]interface{}{ + "revision": "abc123", + }, + }, + }, + }, + } + + templater, err := githubNotification.GetTemplater("checkRun", nil) + assert.NoError(t, err) + + notification := &Notification{} + + err = templater(notification, vars) + assert.NoError(t, err) + + assert.NotNil(t, notification.GitHub) + assert.NotNil(t, notification.GitHub.CheckRun) + assert.Equal(t, "ArgoCD GitHub Check Run", notification.GitHub.CheckRun.Name) + assert.Equal(t, "completed", notification.GitHub.CheckRun.Status) + assert.Equal(t, "success", notification.GitHub.CheckRun.Conclusion) + assert.Equal(t, "All tests passed.", notification.GitHub.CheckRun.Output.Summary) +}