Skip to content

Commit 7da22a4

Browse files
authored
DEVPROD-29991 Ensure git_ref_not_found is set properly (evergreen-ci#9968)
1 parent 3f2da99 commit 7da22a4

5 files changed

Lines changed: 84 additions & 8 deletions

File tree

agent/command/git.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ const (
3636
gitGetProjectAttribute = "evergreen.command.git_get_project"
3737

3838
generatedTokenKey = "EVERGREEN_GENERATED_GITHUB_TOKEN"
39-
40-
// githubMergeQueueInvalidRefError is the error message returned by Git when it fails to find
41-
// a GitHub merge queue reference.
42-
githubMergeQueueInvalidRefError = "couldn't find remote ref gh-readonly-queue"
4339
)
4440

4541
var (
@@ -74,6 +70,8 @@ type gitFetchProject struct {
7470

7571
CommitterEmail string `mapstructure:"committer_email"`
7672

73+
refNotFound bool
74+
7775
base
7876
}
7977

@@ -360,6 +358,22 @@ func (c *gitFetchProject) fetchSource(ctx context.Context, logger client.LoggerP
360358
attempt := 0
361359
return c.retryFetch(ctx, logger, comm, conf, true, opts, func(opts cloneOpts) error {
362360
attempt++
361+
// On the second attempt, check if the merge queue ref was deleted before retrying.
362+
if attempt == 2 && conf.Task.Requester == evergreen.GithubMergeRequester && conf.GithubMergeData.HeadBranch != "" {
363+
ref := "heads/" + conf.GithubMergeData.HeadBranch
364+
appToken, tokenErr := comm.CreateInstallationTokenForClone(ctx, conf.TaskData(), opts.owner, opts.repo)
365+
if tokenErr == nil && appToken != "" {
366+
exists, checkErr := thirdparty.MergeQueueRefExists(ctx, opts.owner, opts.repo, ref, appToken)
367+
if checkErr != nil {
368+
return errors.Wrap(checkErr, "checking if merge queue ref exists")
369+
}
370+
if !exists {
371+
c.refNotFound = true
372+
return errors.New("the GitHub merge SHA is not available most likely because the merge completed or was aborted")
373+
}
374+
}
375+
}
376+
363377
gitCommands, err := c.buildSourceCloneCommand(conf, opts)
364378
if err != nil {
365379
return err
@@ -389,9 +403,10 @@ func (c *gitFetchProject) fetchSource(ctx context.Context, logger client.LoggerP
389403

390404
if err = fetchSourceCmd.Run(ctx); err != nil {
391405
span.SetAttributes(attribute.String(cloneErrorAttribute, err.Error()))
406+
return err
392407
}
393408

394-
return err
409+
return nil
395410
})
396411
}
397412

@@ -410,6 +425,9 @@ func (c *gitFetchProject) retryFetch(ctx context.Context, logger client.LoggerPr
410425
return utility.Retry(
411426
ctx,
412427
func() (bool, error) {
428+
if isSource {
429+
c.refNotFound = false
430+
}
413431
if attemptNum > 2 {
414432
opts.useVerbose = true // use verbose for the last 2 attempts
415433
logger.Task().Error(message.Fields{
@@ -423,7 +441,7 @@ func (c *gitFetchProject) retryFetch(ctx context.Context, logger client.LoggerPr
423441
if isSource && attemptNum == 1 {
424442
logger.Task().Warning("git source clone failed with cached merge SHA; re-requesting merge SHA from GitHub")
425443
}
426-
if strings.Contains(err.Error(), githubMergeQueueInvalidRefError) {
444+
if isSource && c.refNotFound {
427445
if markErr := comm.MarkMergeQueueGitRefNotFound(ctx, conf.TaskData()); markErr != nil {
428446
logger.Task().Warningf("Failed to mark git ref not found: %s", markErr)
429447
}

agent/command/git_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,8 @@ func (s *GitGetProjectSuite) TestRetryFetchStopsOnInvalidGitHubMergeQueueRef() {
242242
attempt := 0
243243
err = c.retryFetch(s.ctx, logger, s.comm, conf, true, opts, func(o cloneOpts) error {
244244
attempt++
245-
return errors.Errorf("fatel: %s", githubMergeQueueInvalidRefError)
245+
c.refNotFound = true
246+
return errors.New("the GitHub merge SHA is not available most likely because the merge completed or was aborted")
246247
})
247248

248249
s.Equal(1, attempt)

rest/data/patch.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func SetMergeQueueGitRefNotFound(ctx context.Context, versionId string) error {
177177
},
178178
}
179179

180-
return patch.UpdateOne(ctx, patch.ById(p.Id), update)
180+
return patch.UpdateOne(ctx, mgobson.M{patch.IdKey: p.Id}, update)
181181
}
182182

183183
// FindPatchesByUser finds patches for the input user as ordered by creation time

thirdparty/github.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,49 @@ func GetTaggedCommitFromGithub(ctx context.Context, owner, repo, tag string) (st
11611161
return sha, nil
11621162
}
11631163

1164+
// MergeQueueRefExists checks if a GitHub ref exists. The ref should be "heads/branch-name"
1165+
// (e.g. "heads/gh-readonly-queue/main/pr-515-abc123").
1166+
// If token is non-empty, it will be used for authentication. Otherwise, an installation
1167+
// token is created from Evergreen config (requires server environment).
1168+
func MergeQueueRefExists(ctx context.Context, owner, repo, ref string, token string) (bool, error) {
1169+
caller := "MergeQueueRefExists"
1170+
ctx, span := tracer.Start(ctx, caller, trace.WithAttributes(
1171+
attribute.String(githubOwnerAttribute, owner),
1172+
attribute.String(githubRepoAttribute, repo),
1173+
attribute.String(githubRefAttribute, ref),
1174+
))
1175+
defer span.End()
1176+
1177+
var authToken string
1178+
if token != "" {
1179+
authToken = token
1180+
} else {
1181+
var err error
1182+
authToken, err = getInstallationToken(ctx, owner, repo, nil)
1183+
if err != nil {
1184+
return false, errors.Wrap(err, "getting installation token")
1185+
}
1186+
}
1187+
1188+
githubClient := getGithubClient(authToken, caller, retryConfig{retry: true})
1189+
defer githubClient.Close()
1190+
1191+
_, resp, err := githubClient.Git.GetRef(ctx, owner, repo, ref)
1192+
if resp != nil {
1193+
defer resp.Body.Close()
1194+
if resp.StatusCode == http.StatusNotFound {
1195+
return false, nil
1196+
}
1197+
if err != nil {
1198+
return false, parseGithubErrorResponse(resp)
1199+
}
1200+
}
1201+
if err != nil {
1202+
return false, errors.Wrapf(err, "checking if ref '%s' exists", ref)
1203+
}
1204+
return true, nil
1205+
}
1206+
11641207
func getObjectTag(ctx context.Context, owner, repo, sha string) (*github.Tag, error) {
11651208
caller := "getObjectTag"
11661209
ctx, span := tracer.Start(ctx, caller, trace.WithAttributes(

thirdparty/github_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,20 @@ func (s *githubSuite) TestGetTaggedCommitFromGithub() {
240240
})
241241
}
242242

243+
func (s *githubSuite) TestMergeQueueRefExists() {
244+
s.Run("RefDoesNotExist", func() {
245+
exists, err := MergeQueueRefExists(s.ctx, "evergreen-ci", "evergreen", "heads/gh-readonly-queue/main/pr-999999999-nonexistent", "")
246+
s.NoError(err)
247+
s.False(exists)
248+
})
249+
250+
s.Run("RefExists", func() {
251+
exists, err := MergeQueueRefExists(s.ctx, "evergreen-ci", "evergreen", "heads/main", "")
252+
s.NoError(err)
253+
s.True(exists)
254+
})
255+
}
256+
243257
func (s *githubSuite) TestGetBranchEvent() {
244258
branch, err := GetBranchEvent(s.ctx, "evergreen-ci", "evergreen", "main")
245259
s.NoError(err)

0 commit comments

Comments
 (0)