Skip to content

Commit 94d159d

Browse files
committed
pr: Fail when the CI status fails
In this case the PR will probably not be merged, but we can fail waiting for it if its combined status is failed.
1 parent 76fae26 commit 94d159d

File tree

4 files changed

+128
-22
lines changed

4 files changed

+128
-22
lines changed

cmd/wait-for-github/pr.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func parsePRArguments(c *cli.Context) (prConfig, error) {
111111
repo: repo,
112112
pr: n,
113113
commitInfoFile: c.String("commit-info-file"),
114-
writer: osFileWriter{},
114+
writer: osFileWriter{},
115115
}, nil
116116
}
117117

@@ -122,10 +122,16 @@ type commitInfo struct {
122122
MergedAt int64 `json:"mergedAt"`
123123
}
124124

125+
type checkMergedAndOverallCI interface {
126+
github.CheckPRMerged
127+
github.GetPRHeadSHA
128+
github.CheckOverallCIStatus
129+
}
130+
125131
type prCheck struct {
126132
prConfig
127133

128-
githubClient github.CheckPRMerged
134+
githubClient checkMergedAndOverallCI
129135
}
130136

131137
func (pr prCheck) Check(ctx context.Context, recheckInterval time.Duration) error {
@@ -161,11 +167,28 @@ func (pr prCheck) Check(ctx context.Context, recheckInterval time.Duration) erro
161167
return cli.Exit("PR is closed", 1)
162168
}
163169

170+
// not merged, not closed, let's see what the CI status is. If that's bad,
171+
// we can exit early.
172+
sha, err := pr.githubClient.GetPRHeadSHA(ctx, pr.owner, pr.repo, pr.pr)
173+
if err != nil {
174+
return err
175+
}
176+
177+
status, err := pr.githubClient.GetCIStatus(ctx, pr.owner, pr.repo, sha)
178+
if err != nil {
179+
return err
180+
}
181+
182+
if status == github.CIStatusFailed {
183+
log.Info("CI failed, exiting")
184+
return cli.Exit("CI failed", 1)
185+
}
186+
164187
log.Infof("PR is not closed yet, rechecking in %s", recheckInterval)
165188
return nil
166189
}
167190

168-
func checkPRMerged(timeoutCtx context.Context, githubClient github.CheckPRMerged, cfg *config, prConf *prConfig) error {
191+
func checkPRMerged(timeoutCtx context.Context, githubClient checkMergedAndOverallCI, cfg *config, prConf *prConfig) error {
169192
checkPRMergedOrClosed := prCheck{
170193
githubClient: githubClient,
171194
prConfig: *prConf,

cmd/wait-for-github/pr_test.go

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,60 +24,90 @@ import (
2424
"io/fs"
2525
"testing"
2626

27+
"github.com/grafana/wait-for-github/internal/github"
2728
"github.com/stretchr/testify/require"
2829
"github.com/urfave/cli/v2"
2930
)
3031

31-
// FakeGithubClientPRCheck implements CheckPRMerged
32-
type FakeGithubClientPRCheck struct {
32+
// fakeGithubClientPRCheck implements the checkMergedAndOverallCI interface
33+
type fakeGithubClientPRCheck struct {
3334
MergedCommit string
3435
Closed bool
3536
MergedAt int64
36-
Error error
37+
38+
isPRMergedError error
39+
getPRHeadSHAError error
40+
getCIStatusError error
41+
42+
CIStatus github.CIStatus
43+
}
44+
45+
func (fg *fakeGithubClientPRCheck) IsPRMergedOrClosed(ctx context.Context, owner, repo string, pr int) (string, bool, int64, error) {
46+
return fg.MergedCommit, fg.Closed, fg.MergedAt, fg.isPRMergedError
3747
}
3848

39-
func (fg *FakeGithubClientPRCheck) IsPRMergedOrClosed(ctx context.Context, owner, repo string, pr int) (string, bool, int64, error) {
40-
return fg.MergedCommit, fg.Closed, fg.MergedAt, fg.Error
49+
func (fg *fakeGithubClientPRCheck) GetPRHeadSHA(ctx context.Context, owner, repo string, pr int) (string, error) {
50+
return fg.MergedCommit, fg.getPRHeadSHAError
51+
}
52+
53+
func (fg *fakeGithubClientPRCheck) GetCIStatus(ctx context.Context, owner, repo string, commitHash string) (github.CIStatus, error) {
54+
return fg.CIStatus, fg.getCIStatusError
4155
}
4256

4357
func TestPRCheck(t *testing.T) {
4458
t.Parallel()
4559

4660
tests := []struct {
4761
name string
48-
fakeClient FakeGithubClientPRCheck
49-
err error
62+
fakeClient fakeGithubClientPRCheck
5063
expectedExitCode *int
5164
}{
5265
{
5366
name: "PR is merged",
54-
fakeClient: FakeGithubClientPRCheck{
67+
fakeClient: fakeGithubClientPRCheck{
5568
MergedCommit: "abc123",
5669
MergedAt: 1234567890,
5770
},
5871
expectedExitCode: &zero,
5972
},
6073
{
6174
name: "PR is closed",
62-
fakeClient: FakeGithubClientPRCheck{
75+
fakeClient: fakeGithubClientPRCheck{
6376
Closed: true,
6477
},
6578
expectedExitCode: &one,
6679
},
6780
{
6881
name: "PR is open",
69-
fakeClient: FakeGithubClientPRCheck{
70-
MergedCommit: "",
71-
Closed: false,
82+
fakeClient: fakeGithubClientPRCheck{
83+
Closed: false,
7284
},
7385
expectedExitCode: &one,
7486
},
7587
{
7688
name: "Error from IsPRMergedOrClosed",
77-
fakeClient: FakeGithubClientPRCheck{
78-
Error: fmt.Errorf("an error occurred"),
89+
fakeClient: fakeGithubClientPRCheck{
90+
isPRMergedError: fmt.Errorf("an error occurred"),
91+
},
92+
},
93+
{
94+
name: "Not merged, but CI failed",
95+
fakeClient: fakeGithubClientPRCheck{
96+
CIStatus: github.CIStatusFailed,
97+
},
98+
expectedExitCode: &one,
99+
},
100+
{
101+
name: "Not merged, getting PR head SHA failed",
102+
fakeClient: fakeGithubClientPRCheck{
103+
getPRHeadSHAError: fmt.Errorf("an error occurred"),
104+
},
105+
},
106+
{
107+
name: "Not merged, getting CI status failed",
108+
fakeClient: fakeGithubClientPRCheck{
109+
getCIStatusError: fmt.Errorf("an error occurred"),
79110
},
80-
err: nil,
81111
},
82112
}
83113

@@ -103,7 +133,7 @@ func TestPRCheck(t *testing.T) {
103133
require.ErrorAs(t, err, &exitErr)
104134
require.Equal(t, *tt.expectedExitCode, exitErr.ExitCode())
105135
} else if err != nil {
106-
require.ErrorAs(t, err, &tt.err)
136+
require.NotNil(t, err)
107137
}
108138
})
109139
}
@@ -138,7 +168,7 @@ func TestWriteCommitInfoFile(t *testing.T) {
138168
prCheck := &prCheck{
139169
prConfig: prConfig,
140170

141-
githubClient: &FakeGithubClientPRCheck{
171+
githubClient: &fakeGithubClientPRCheck{
142172
MergedCommit: "abc123",
143173
MergedAt: 1234567890,
144174
},
@@ -182,7 +212,7 @@ func TestWriteCommitInfoFileError(t *testing.T) {
182212
prCheck := &prCheck{
183213
prConfig: prConfig,
184214

185-
githubClient: &FakeGithubClientPRCheck{
215+
githubClient: &fakeGithubClientPRCheck{
186216
MergedCommit: "abc123",
187217
MergedAt: 1234567890,
188218
},

internal/github/github.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,23 @@ type CheckPRMerged interface {
3434
IsPRMergedOrClosed(ctx context.Context, owner, repo string, pr int) (string, bool, int64, error)
3535
}
3636

37-
type CheckCIStatus interface {
37+
type GetPRHeadSHA interface {
38+
GetPRHeadSHA(ctx context.Context, owner, repo string, pr int) (string, error)
39+
}
40+
41+
type CheckOverallCIStatus interface {
3842
GetCIStatus(ctx context.Context, owner, repo string, commitHash string) (CIStatus, error)
43+
}
44+
45+
type CheckCIStatusForChecks interface {
3946
GetCIStatusForChecks(ctx context.Context, owner, repo string, commitHash string, checkNames []string) (CIStatus, []string, error)
4047
}
4148

49+
type CheckCIStatus interface {
50+
CheckOverallCIStatus
51+
CheckCIStatusForChecks
52+
}
53+
4254
type AuthInfo struct {
4355
InstallationID int64
4456
AppID int64
@@ -117,6 +129,15 @@ func (c GHClient) IsPRMergedOrClosed(ctx context.Context, owner, repo string, pr
117129
return sha, pr.GetState() == "closed", mergedAt, nil
118130
}
119131

132+
func (c GHClient) GetPRHeadSHA(ctx context.Context, owner, repo string, prNumber int) (string, error) {
133+
pr, _, err := c.client.PullRequests.Get(ctx, owner, repo, prNumber)
134+
if err != nil {
135+
return "", fmt.Errorf("failed to query GitHub for PR HEAD SHA: %w", err)
136+
}
137+
138+
return pr.GetHead().GetSHA(), nil
139+
}
140+
120141
type CIStatus uint
121142

122143
const (

internal/github/github_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,3 +644,35 @@ func TestGetCIStatusForChecks_ErrorListStatuses(t *testing.T) {
644644
require.Error(t, err)
645645
require.ErrorContains(t, err, "failed to query GitHub")
646646
}
647+
648+
func TestGetPRHeadSHA(t *testing.T) {
649+
t.Parallel()
650+
651+
ctx := context.Background()
652+
653+
mockedHTTPClient := mock.NewMockedHTTPClient(
654+
mock.WithRequestMatch(
655+
mock.GetReposPullsByOwnerByRepoByPullNumber,
656+
github.PullRequest{
657+
Head: &github.PullRequestBranch{
658+
SHA: github.String("abcdef12345"),
659+
},
660+
},
661+
),
662+
)
663+
664+
ghClient := newClientFromMock(t, mockedHTTPClient)
665+
666+
sha, err := ghClient.GetPRHeadSHA(ctx, "owner", "repo", 1)
667+
require.NoError(t, err)
668+
require.Equal(t, "abcdef12345", sha)
669+
}
670+
671+
func TestGetPRHeadSHA_Error(t *testing.T) {
672+
t.Parallel()
673+
674+
ctx := context.Background()
675+
ghClient := newErrorReturningClient(t)
676+
_, err := ghClient.GetPRHeadSHA(ctx, "owner", "repo", 1)
677+
require.Error(t, err)
678+
}

0 commit comments

Comments
 (0)