-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
sdk/check: helper methods for GitHub CheckRun API
Signed-off-by: Jason Hall <[email protected]>
- Loading branch information
Showing
2 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |