Skip to content

Commit

Permalink
feat: Add support for GitHub Check Runs
Browse files Browse the repository at this point in the history
Adds support to be able to update GitHub Check Runs, which are more powerful than GitHub Statuses

[Link to issue in `argoproj/argo-cd`](argoproj/argo-cd#19256)
  • Loading branch information
tijmenstor committed Oct 11, 2024
1 parent 2fef5c9 commit 7bef993
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 0 deletions.
166 changes: 166 additions & 0 deletions pkg/services/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type GitHubNotification struct {
repoURL string
revision string
Status *GitHubStatus `json:"status,omitempty"`
CheckRun *GitHubCheckRun `json:"checkRun,omitempty"`
Deployment *GitHubDeployment `json:"deployment,omitempty"`
PullRequestComment *GitHubPullRequestComment `json:"pullRequestComment,omitempty"`
RepoURLPath string `json:"repoURLPath,omitempty"`
Expand All @@ -47,6 +48,20 @@ type GitHubStatus struct {
TargetURL string `json:"targetURL,omitempty"`
}

type GitHubCheckRun struct {
Name string `json:"name,omitempty"`
Status string `json:"status,omitempty"`
Conclusion string `json:"conclusion,omitempty"`
DetailsURL string `json:"detailsURL,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"`
Expand Down Expand Up @@ -85,6 +100,47 @@ func (g *GitHubNotification) GetTemplater(name string, f texttemplate.FuncMap) (
return nil, err
}

var checkRunName, checkRunStatus, conclusion, detailsURL, title, summary, text *texttemplate.Template
if g.CheckRun != nil {
checkRunName, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.Name)
if err != nil {
return nil, err
}

checkRunStatus, 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
}

detailsURL, err = texttemplate.New(name).Funcs(f).Parse(g.CheckRun.DetailsURL)
if err != nil {
return nil, err
}

if g.CheckRun.Output != nil {
title, 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
}
}

}

var statusState, label, targetURL *texttemplate.Template
if g.Status != nil {
statusState, err = texttemplate.New(name).Funcs(f).Parse(g.Status.State)
Expand Down Expand Up @@ -159,6 +215,60 @@ func (g *GitHubNotification) GetTemplater(name string, f texttemplate.FuncMap) (
}
notification.GitHub.revision = revisionData.String()

if g.CheckRun != nil {
if notification.GitHub.CheckRun == nil {
notification.GitHub.CheckRun = &GitHubCheckRun{}
}

var nameData bytes.Buffer
if err := checkRunName.Execute(&nameData, vars); err != nil {
return err
}
notification.GitHub.CheckRun.Name = nameData.String()

var statusData bytes.Buffer
if err := checkRunStatus.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 detailsURLData bytes.Buffer
if err := detailsURL.Execute(&detailsURLData, vars); err != nil {
return err
}
notification.GitHub.CheckRun.DetailsURL = detailsURLData.String()

if g.CheckRun.Output != nil {
if notification.GitHub.CheckRun.Output == nil {
notification.GitHub.CheckRun.Output = &GitHubCheckRunOutput{}
}

var titleData bytes.Buffer
if err := title.Execute(&titleData, vars); err != nil {
return err
}
notification.GitHub.CheckRun.Output.Title = titleData.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()
}
}

if g.Status != nil {
if notification.GitHub.Status == nil {
notification.GitHub.Status = &GitHubStatus{}
Expand Down Expand Up @@ -346,6 +456,62 @@ func (g gitHubService) Send(notification Notification, _ Destination) error {
}
}

if notification.GitHub.CheckRun != nil {
checkRuns, _, err := g.client.Checks.ListCheckRunsForRef(
context.Background(),
u[0],
u[1],
notification.GitHub.revision,
&github.ListCheckRunsOptions{
CheckName: &notification.GitHub.CheckRun.Name,
},
)

if err != nil {
return err
}

var checkRun *github.CheckRun
if len(checkRuns.CheckRuns) != 0 {
checkRun = checkRuns.CheckRuns[0]
} else {
checkRun, _, err = g.client.Checks.CreateCheckRun(
context.Background(),
u[0],
u[1],
github.CreateCheckRunOptions{
Name: notification.GitHub.CheckRun.Name,
HeadSHA: notification.GitHub.revision,
},
)
if err != nil {
return err
}
}

g.client.Checks.UpdateCheckRun(

Check failure on line 492 in pkg/services/github.go

View workflow job for this annotation

GitHub Actions / Lint Go code

Error return value of `g.client.Checks.UpdateCheckRun` is not checked (errcheck)
context.Background(),
u[0],
u[1],
*checkRun.ID,
github.UpdateCheckRunOptions{
Name: notification.GitHub.CheckRun.Name,
DetailsURL: &notification.GitHub.CheckRun.DetailsURL,
Status: &notification.GitHub.CheckRun.Status,
Conclusion: &notification.GitHub.CheckRun.Conclusion,
Output: &github.CheckRunOutput{
Title: &notification.GitHub.CheckRun.Output.Title,
Summary: &notification.GitHub.CheckRun.Output.Summary,
Text: &notification.GitHub.CheckRun.Output.Text,
},
},
)

if err != nil {
return err
}
}

if notification.GitHub.Deployment != nil {
// maximum is 140 characters
description := trunc(notification.Message, 140)
Expand Down
60 changes: 60 additions & 0 deletions pkg/services/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,66 @@ func TestGetTemplater_GitHub_Deployment(t *testing.T) {
assert.Equal(t, "v0.0.1", notification.GitHub.Deployment.Reference)
}

func TestGetTemplater_GitHub_CheckRun(t *testing.T) {
n := Notification{
GitHub: &GitHubNotification{
RepoURLPath: "{{.sync.spec.git.repo}}",
RevisionPath: "{{.sync.status.lastSyncedCommit}}",
CheckRun: &GitHubCheckRun{
Name: "test-check-run",
Status: "completed",
Conclusion: "success",
DetailsURL: "https://example.com/details",
Output: &GitHubCheckRunOutput{
Title: "Check Run Title",
Summary: "Check Run Summary",
Text: "Check Run Text",
},
},
},
}
templater, err := n.GetTemplater("", template.FuncMap{})

if !assert.NoError(t, err) {
t.Fatalf("Failed to get templater: %v", err)
return
}

var notification Notification
err = templater(&notification, map[string]interface{}{
"sync": map[string]interface{}{
"metadata": map[string]interface{}{
"name": "root-sync-test",
},
"spec": map[string]interface{}{
"git": map[string]interface{}{
"repo": "https://github.com/argoproj-labs/argocd-notifications.git",
},
},
"status": map[string]interface{}{
"lastSyncedCommit": "0123456789",
},
},
})

if !assert.NoError(t, err) {
t.Fatalf("Failed to execute templater: %v", err)
return
}

assert.Equal(t, "{{.sync.spec.git.repo}}", notification.GitHub.RepoURLPath)
assert.Equal(t, "{{.sync.status.lastSyncedCommit}}", notification.GitHub.RevisionPath)
assert.Equal(t, "https://github.com/argoproj-labs/argocd-notifications.git", notification.GitHub.repoURL)
assert.Equal(t, "0123456789", notification.GitHub.revision)
assert.Equal(t, "test-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, "https://example.com/details", notification.GitHub.CheckRun.DetailsURL)
assert.Equal(t, "Check Run Title", notification.GitHub.CheckRun.Output.Title)
assert.Equal(t, "Check Run Summary", notification.GitHub.CheckRun.Output.Summary)
assert.Equal(t, "Check Run Text", notification.GitHub.CheckRun.Output.Text)
}

func TestNewGitHubService_GitHubOptions(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit 7bef993

Please sign in to comment.