Skip to content

Commit 0371f70

Browse files
chmouelShivam Mukhade
authored andcommitted
[gh app] Update exiting check run if there is one
When there was an already exiting check_run we discover it. It fixes the issue when we : - Create a commit - There is a failure before we have checked out the pipelinerun match (ie: hub is down and fail to get the taskRef remotely) - We do a /retest and the error is fixed. - We would still have the status of the first failure as well a second successfull one. We now list the check_runs attached to the ref created by our ApplicationID and re-use that ID instead of creating a new one. There is a slight bug in github interface, if we update the check run and set as pending, it doesn't reflect it in the UI. It does change it when the check_run went from failure to successfull so that's fine. Signed-off-by: Chmouel Boudjnah <[email protected]>
1 parent db8f587 commit 0371f70

File tree

4 files changed

+141
-18
lines changed

4 files changed

+141
-18
lines changed

pkg/provider/github/events.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func (v *Provider) getAppToken(ctx context.Context, info *info.PacOpts) error {
4949
if err != nil {
5050
return fmt.Errorf("could not parse installation_id: %w", err)
5151
}
52+
v.ApplicationID = &applicationID
5253

5354
// check if the path exists
5455
if _, err := os.Stat(workspacePath); os.IsNotExist(err) {

pkg/provider/github/github.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const apiPublicURL = "https://api.github.com/"
1717
type Provider struct {
1818
Client *github.Client
1919
Token, APIURL *string
20+
ApplicationID *int64
2021
}
2122

2223
func (v *Provider) GetConfig() *info.ProviderConfig {

pkg/provider/github/status.go

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,53 @@ func getCheckName(status provider.StatusOpts, pacopts *info.PacOpts) string {
3535
return status.OriginalPipelineRunName
3636
}
3737

38-
// createCheckRunStatus create a status via the checkRun API, which is only
38+
func (v *Provider) getExistingCheckRunID(ctx context.Context, runevent *info.Event) (*int64, error) {
39+
res, _, err := v.Client.Checks.ListCheckRunsForRef(ctx, runevent.Organization, runevent.Repository,
40+
runevent.SHA, &github.ListCheckRunsOptions{
41+
AppID: v.ApplicationID,
42+
})
43+
if err != nil {
44+
return nil, err
45+
}
46+
if *res.Total > 0 {
47+
// Their should be only one, since we CRUD it.. maybe one day we
48+
// will offer the ability to do multiple pipelineruns per commit then
49+
// things will get more complicated here.
50+
return res.CheckRuns[0].ID, nil
51+
}
52+
return nil, nil
53+
}
54+
55+
func (v *Provider) createCheckRunStatus(ctx context.Context, runevent *info.Event, pacopts *info.PacOpts, status provider.StatusOpts) (*int64, error) {
56+
now := github.Timestamp{Time: time.Now()}
57+
checkrunoption := github.CreateCheckRunOptions{
58+
Name: getCheckName(status, pacopts),
59+
HeadSHA: runevent.SHA,
60+
Status: github.String("in_progress"),
61+
DetailsURL: github.String(pacopts.LogURL),
62+
StartedAt: &now,
63+
}
64+
65+
checkRun, _, err := v.Client.Checks.CreateCheckRun(ctx, runevent.Organization, runevent.Repository, checkrunoption)
66+
if err != nil {
67+
return nil, err
68+
}
69+
return checkRun.ID, nil
70+
}
71+
72+
// getOrUpdateCheckRunStatus create a status via the checkRun API, which is only
3973
// available with Github apps tokens.
40-
func (v *Provider) createCheckRunStatus(ctx context.Context, runevent *info.Event, pacopts *info.PacOpts, status provider.StatusOpts) error {
74+
func (v *Provider) getOrUpdateCheckRunStatus(ctx context.Context, runevent *info.Event, pacopts *info.PacOpts, status provider.StatusOpts) error {
75+
var err error
76+
4177
now := github.Timestamp{Time: time.Now()}
4278
if runevent.CheckRunID == nil {
43-
now := github.Timestamp{Time: time.Now()}
44-
checkrunoption := github.CreateCheckRunOptions{
45-
Name: getCheckName(status, pacopts),
46-
HeadSHA: runevent.SHA,
47-
Status: github.String("in_progress"),
48-
DetailsURL: github.String(pacopts.LogURL),
49-
StartedAt: &now,
50-
}
51-
52-
checkRun, _, err := v.Client.Checks.CreateCheckRun(ctx, runevent.Organization, runevent.Repository, checkrunoption)
53-
if err != nil {
54-
return err
79+
if runevent.CheckRunID, _ = v.getExistingCheckRunID(ctx, runevent); runevent.CheckRunID == nil {
80+
runevent.CheckRunID, err = v.createCheckRunStatus(ctx, runevent, pacopts, status)
81+
if err != nil {
82+
return err
83+
}
5584
}
56-
runevent.CheckRunID = checkRun.ID
5785
}
5886

5987
checkRunOutput := &github.CheckRunOutput{
@@ -78,7 +106,7 @@ func (v *Provider) createCheckRunStatus(ctx context.Context, runevent *info.Even
78106
opts.Conclusion = &status.Conclusion
79107
}
80108

81-
_, _, err := v.Client.Checks.UpdateCheckRun(ctx, runevent.Organization, runevent.Repository, *runevent.CheckRunID, opts)
109+
_, _, err = v.Client.Checks.UpdateCheckRun(ctx, runevent.Organization, runevent.Repository, *runevent.CheckRunID, opts)
82110
return err
83111
}
84112

@@ -156,5 +184,5 @@ func (v *Provider) CreateStatus(ctx context.Context, runevent *info.Event, pacop
156184
if pacopts.ProviderInfoFromRepo {
157185
return v.createStatusCommit(ctx, runevent, pacopts, status)
158186
}
159-
return v.createCheckRunStatus(ctx, runevent, pacopts, status)
187+
return v.getOrUpdateCheckRunStatus(ctx, runevent, pacopts, status)
160188
}

pkg/provider/github/status_test.go

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"io/ioutil"
77
"net/http"
8+
"reflect"
89
"strings"
910
"testing"
1011

@@ -23,13 +24,45 @@ func TestGithubProviderCreateCheckRun(t *testing.T) {
2324
event := &info.Event{
2425
Organization: "check",
2526
Repository: "info",
27+
SHA: "createCheckRunSHA",
2628
}
2729

28-
err := gcvs.createCheckRunStatus(ctx, event, &info.PacOpts{LogURL: "http://nowhere"}, provider.StatusOpts{Status: "hello moto"})
30+
err := gcvs.getOrUpdateCheckRunStatus(ctx, event, &info.PacOpts{LogURL: "http://nowhere"}, provider.StatusOpts{Status: "hello moto"})
2931
assert.NilError(t, err)
3032
assert.Equal(t, *event.CheckRunID, int64(555))
3133
}
3234

35+
func TestGetExistingCheckRunID(t *testing.T) {
36+
ctx, _ := rtesting.SetupFakeContext(t)
37+
client, mux, _, teardown := ghtesthelper.SetupGH()
38+
defer teardown()
39+
40+
gvcs := &Provider{
41+
Client: client,
42+
}
43+
event := &info.Event{
44+
Organization: "owner",
45+
Repository: "repository",
46+
SHA: "sha",
47+
}
48+
49+
checkRunID := int64(55555)
50+
mux.HandleFunc(fmt.Sprintf("/repos/%v/%v/commits/%v/check-runs", event.Organization, event.Repository, event.SHA), func(w http.ResponseWriter, r *http.Request) {
51+
_, _ = fmt.Fprintf(w, `{
52+
"total_count": 1,
53+
"check_runs": [
54+
{
55+
"id": %v
56+
}
57+
]
58+
}`, checkRunID)
59+
})
60+
61+
id, err := gvcs.getExistingCheckRunID(ctx, event)
62+
assert.NilError(t, err)
63+
assert.Equal(t, *id, checkRunID)
64+
}
65+
3366
func TestGithubProviderCreateStatus(t *testing.T) {
3467
checkrunid := int64(2026)
3568
resultid := int64(666)
@@ -304,3 +337,63 @@ func TestGetCheckName(t *testing.T) {
304337
})
305338
}
306339
}
340+
341+
func TestProviderGetExistingCheckRunID(t *testing.T) {
342+
tests := []struct {
343+
name string
344+
jsonret string
345+
expectedID *int64
346+
wantErr bool
347+
}{
348+
{
349+
name: "has check runs",
350+
jsonret: `{
351+
"total_count": 1,
352+
"check_runs": [
353+
{
354+
"id": 55555
355+
}
356+
]
357+
}`,
358+
expectedID: github.Int64(55555),
359+
},
360+
{
361+
name: "no check runs",
362+
jsonret: `{"total_count": 0,"check_runs": []}`,
363+
expectedID: nil,
364+
},
365+
{
366+
name: "error it",
367+
jsonret: `BLAHALALACLCALWA`,
368+
expectedID: nil,
369+
wantErr: true,
370+
},
371+
}
372+
for _, tt := range tests {
373+
t.Run(tt.name, func(t *testing.T) {
374+
ctx, _ := rtesting.SetupFakeContext(t)
375+
client, mux, _, teardown := ghtesthelper.SetupGH()
376+
defer teardown()
377+
event := &info.Event{
378+
Organization: "owner",
379+
Repository: "repository",
380+
SHA: "sha",
381+
}
382+
v := &Provider{
383+
Client: client,
384+
}
385+
mux.HandleFunc(fmt.Sprintf("/repos/%v/%v/commits/%v/check-runs", event.Organization, event.Repository, event.SHA), func(w http.ResponseWriter, r *http.Request) {
386+
_, _ = fmt.Fprintf(w, tt.jsonret)
387+
})
388+
389+
got, err := v.getExistingCheckRunID(ctx, event)
390+
if (err != nil) != tt.wantErr {
391+
t.Errorf("getExistingCheckRunID() error = %v, wantErr %v", err, tt.wantErr)
392+
return
393+
}
394+
if !reflect.DeepEqual(got, tt.expectedID) {
395+
t.Errorf("getExistingCheckRunID() got = %v, want %v", got, tt.expectedID)
396+
}
397+
})
398+
}
399+
}

0 commit comments

Comments
 (0)