Skip to content

Commit

Permalink
sdk/check: helper methods for GitHub CheckRun API
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Hall <[email protected]>
  • Loading branch information
imjasonh committed Dec 12, 2024
1 parent 201e53a commit 37f2f54
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 0 deletions.
93 changes: 93 additions & 0 deletions modules/github-bots/sdk/check/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package check

import (
"fmt"
"strings"

"github.com/google/go-github/v61/github"
)

// Docs for Check Run API: https://docs.github.com/en/rest/checks/runs?apiVersion=2022-11-28

const (
maxCheckOutputLength = 65536
truncationMessage = "\n\n⚠️ _Summary has been truncated_"
)

type Conclusion string

const (
ConclusionActionRequired Conclusion = "action_required"
ConclusionCancelled Conclusion = "cancelled"
ConclusionFailure Conclusion = "failure"
// ConclusionNeutral is the default, and is sufficient to pass a required check.
ConclusionNeutral Conclusion = "neutral"
ConclusionSuccess Conclusion = "success"
ConclusionTimedOut Conclusion = "timed_out"
// ConclusionSkipped is not sufficient to pass a required check.
ConclusionSkipped Conclusion = "skipped"
)

type Builder struct {
md strings.Builder
name, headSHA string
Summary string
Conclusion Conclusion
}

func NewBuilder(name, headSHA string) *Builder {
return &Builder{
name: name,
headSHA: headSHA,
}
}

// Writef appends a formatted string to the CheckRun output.
//
// If the output exceeds the maximum length, it will be truncated and a message will be appended.
func (b *Builder) Writef(format string, args ...any) {
if b.md.Len() <= maxCheckOutputLength {
b.md.WriteString(fmt.Sprintf(format, args...))
b.md.WriteRune('\n')
}

if b.md.Len() > maxCheckOutputLength {
out := b.md.String()
out = out[:maxCheckOutputLength-len(truncationMessage)]
out += truncationMessage
b.md = strings.Builder{}
b.md.WriteString(out)
}
}

// CheckRun returns a GitHub CheckRun object with the current state of the Builder.
//
// If the Summary field is empty, it will be set to the name field.
// If the Conclusion field is set, the CheckRun will be marked as completed.
func (b *Builder) CheckRun() *github.CheckRun {
if b.Summary == "" {
b.Summary = b.name
}
cr := &github.CheckRun{
Name: &b.name,
HeadSHA: &b.headSHA,
Output: &github.CheckRunOutput{
Title: &b.Summary,
Summary: &b.Summary,
Text: github.String(b.md.String()),
},
// Fields we don't set:
// - DetailsURL: sets the URL of the "Details" link at the bottom of the Check Run page. Defaults to the app's installation URL.
// - ExternalID: sets a unique identifier of the check run on the external system. Not used by this SDK.
// - Actions: sets actions that a user can perform on the check run. Not used by this SDK.
// - StartedAt: sets the time that the check run began. Automatically set by GitHub the first time the check run is created if it's in-progress.
// - CompletedAt: sets the time that the check run completed. Automatically set by GitHub the first time the check run is completed.
// - Output.Annotations: sets annotations that are used to provide more information about a line of code. Not used by this SDK.
}
// Providing conclusion will automatically set the status parameter to completed.
if b.Conclusion != "" {
cr.Conclusion = github.String(string(b.Conclusion))
cr.Status = github.String("completed")
}
return cr
}
67 changes: 67 additions & 0 deletions modules/github-bots/sdk/check/check_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package check

import (
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-github/v61/github"
)

func TestCheckRun(t *testing.T) {
b := NewBuilder("name", "headSHA")
b.Writef("test %d", 123)

if diff := cmp.Diff(b.CheckRun(), &github.CheckRun{
Name: github.String("name"),
HeadSHA: github.String("headSHA"),
Output: &github.CheckRunOutput{
Title: github.String("name"),
Summary: github.String("name"),
Text: github.String("test 123\n"),
},
}); diff != "" {
t.Errorf("CheckRun() mismatch (-want +got):\n%s", diff)
}

b.Summary = "summary"
b.Conclusion = ConclusionSuccess
b.Writef("test %t", true)
if diff := cmp.Diff(b.CheckRun(), &github.CheckRun{
Name: github.String("name"),
HeadSHA: github.String("headSHA"),
Status: github.String("completed"),
Conclusion: github.String("success"),
Output: &github.CheckRunOutput{
Title: github.String("summary"),
Summary: github.String("summary"),
Text: github.String("test 123\ntest true\n"),
},
}); diff != "" {
t.Errorf("CheckRun() mismatch (-want +got):\n%s", diff)
}
}

func TestWritef(t *testing.T) {
b := NewBuilder("name", "headSHA")

// append 1 KB 100 times
for i := 0; i < 100; i++ {
b.Writef(strings.Repeat("a", 1024)) //nolint:govet

// The output should never exceed maxCheckOutputLength, even internally.
if b.md.Len() > maxCheckOutputLength {
t.Fatalf("CheckRun().Output.Text length = %d, want <= %d", b.md.Len(), maxCheckOutputLength)
}
}

gotText := b.CheckRun().GetOutput().GetText()
wantLength := maxCheckOutputLength
if len(gotText) != wantLength {
t.Fatalf("CheckRun().Output.Text length = %d, want %d", len(gotText), wantLength)
}
if !strings.HasSuffix(gotText, truncationMessage) {
last100 := gotText[len(gotText)-100:]
t.Errorf("CheckRun().Output.Text does not have truncation message, ends with %q", last100)
}
}

0 comments on commit 37f2f54

Please sign in to comment.